fix(agent-loop): second review pass — late-steering discard, dead-branch, catalog leaks

Address a second fresh-eye review of the review fixes:
- MAJOR: late steering that lands after runTurn returns terminally
  stopped (e.g. during the post-turn flush) was drained by runLoop and
  dropped without a discard, leaving a dangling outstanding id the
  negative-only invariant can't catch. Emit agent/inbox/discard for it,
  symmetric with the in-turn terminal-stop drop.
- remove the dead cancel() idle-settle branch: whenIdle's fast path
  already resolves for a lone quiet item, so no waiter is ever left for
  it to settle. Document why.
- gen-cordis-api classShape now drops private/protected/#private members
  and strips getter/setter bodies, so Session no longer leaks private
  fields and getter bodies into the model catalog.
- document that AgentMessage intentionally omits meta (durable-only).

Adds a regression test for the late-steering discard.
This commit is contained in:
Turtle
2026-07-23 22:33:12 +08:00
parent 98ee4ce429
commit 1f9a3e1bee
14 changed files with 122 additions and 69 deletions
@@ -1733,7 +1733,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'Session',
declaration: 'export class Session {\n private log: SessionEvent[];\n private readonly surfaceManager;\n get surface(): SessionSurface {\n return this.surfaceManager;\n }\n readonly header: SessionHeader;\n get id(): SessionId {\n return this.header.id;\n }\n constructor(id: SessionId, seed?: readonly SessionEvent[], header?: SessionHeader);\n private eventsSnapshot: readonly SessionEvent[] | undefined;\n get events(): readonly SessionEvent[] {\n this.eventsSnapshot ??= Object.freeze([...this.log]);\n return this.eventsSnapshot;\n }\n get seq(): number {\n return this.log.length;\n }\n append<T extends SessionEventType>(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [\n opts: SurfaceIntent\n ] : [\n ]): SessionEvent<T>;\n private headerFold: EpochHeader | undefined;\n private headerFoldSeq;\n requestHeader(): EpochHeader | undefined;\n private derived: Message[];\n private derivedNodes;\n private derivedGeneration;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}',
declaration: 'export class Session {\n get surface(): SessionSurface;\n readonly header: SessionHeader;\n get id(): SessionId;\n constructor(id: SessionId, seed?: readonly SessionEvent[], header?: SessionHeader);\n get events(): readonly SessionEvent[];\n get seq(): number;\n append<T extends SessionEventType>(type: T, data: SessionEventMap[T], ...opts: T extends SurfaceEventType ? [\n opts: SurfaceIntent\n ] : [\n ]): SessionEvent<T>;\n requestHeader(): EpochHeader | undefined;\n deriveMessages(): Message[];\n deriveEventMessage(event: SessionEvent): Message | null;\n}',
},
{
name: 'SessionEvent',