@genesislcap/ai-assistant 15.24.2 → 15.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/ai-assistant.api.json +1065 -33
  2. package/dist/ai-assistant.d.ts +288 -102
  3. package/dist/chat-driver.cjs +630 -378
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +630 -378
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +1198 -388
  8. package/dist/dts/components/agent-session/agent-session.d.ts +134 -0
  9. package/dist/dts/components/agent-session/agent-session.d.ts.map +1 -0
  10. package/dist/dts/components/agent-session/index.d.ts +2 -0
  11. package/dist/dts/components/agent-session/index.d.ts.map +1 -0
  12. package/dist/dts/components/ai-driver/ai-driver.d.ts +31 -4
  13. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  14. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts +52 -0
  15. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts.map +1 -0
  16. package/dist/dts/components/chat-driver/chat-driver.d.ts +100 -82
  17. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  18. package/dist/dts/components/chat-driver/fold-controller.d.ts +82 -0
  19. package/dist/dts/components/chat-driver/fold-controller.d.ts.map +1 -0
  20. package/dist/dts/components/chat-driver/turn-failures.d.ts +20 -1
  21. package/dist/dts/components/chat-driver/turn-failures.d.ts.map +1 -1
  22. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +14 -24
  23. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  24. package/dist/dts/index.d.ts +1 -0
  25. package/dist/dts/index.d.ts.map +1 -1
  26. package/dist/dts/main/main.d.ts +6 -16
  27. package/dist/dts/main/main.d.ts.map +1 -1
  28. package/dist/dts/suggestions/generate-suggestions.d.ts +35 -0
  29. package/dist/dts/suggestions/generate-suggestions.d.ts.map +1 -0
  30. package/dist/dts/utils/agents-key.d.ts +10 -0
  31. package/dist/dts/utils/agents-key.d.ts.map +1 -0
  32. package/dist/dts/utils/resolve-turn-input.d.ts +9 -0
  33. package/dist/dts/utils/resolve-turn-input.d.ts.map +1 -0
  34. package/dist/esm/components/agent-session/agent-session.js +208 -0
  35. package/dist/esm/components/agent-session/index.js +1 -0
  36. package/dist/esm/components/chat-driver/chat-driver.constants.js +72 -0
  37. package/dist/esm/components/chat-driver/chat-driver.js +190 -395
  38. package/dist/esm/components/chat-driver/fold-controller.js +184 -0
  39. package/dist/esm/components/chat-driver/turn-failures.js +14 -0
  40. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +48 -131
  41. package/dist/esm/index.js +1 -0
  42. package/dist/esm/main/main.js +11 -38
  43. package/dist/esm/suggestions/generate-suggestions.js +123 -0
  44. package/dist/esm/utils/agents-key.js +24 -0
  45. package/dist/esm/utils/resolve-turn-input.js +16 -0
  46. package/docs/migration-GENC-1580.md +44 -36
  47. package/package.json +16 -16
@@ -405,6 +405,141 @@ export declare class AgentPicker extends GenesisElement {
405
405
 
406
406
  export { AgentPickerMode }
407
407
 
408
+ /**
409
+ * The stateful-agent contract: which agent is active, whether a flow or a user pin holds routing,
410
+ * and the `onActivate` / `onDeactivate` lifecycle around a swap. Either driver can own one.
411
+ *
412
+ * Knows nothing about routing, tool surfaces or transcripts — whether an agent is terminal, the
413
+ * continuation tool, the handoff marker in history all stay with the orchestrator, which is the
414
+ * only thing with agents to route between.
415
+ *
416
+ * @beta
417
+ */
418
+ export declare class AgentSession {
419
+ private readonly deps;
420
+ /**
421
+ * Aborted on disposal. Threaded into `AgentLifecycleContext.signal` so long-running
422
+ * `onActivate` work can bail if the session disconnects.
423
+ */
424
+ private readonly lifecycleAbortController;
425
+ private active?;
426
+ /**
427
+ * Sticky user pick from the picker (or the host's `setAgent` API). Only changes on explicit
428
+ * user action. Survives flow completion: when a stateful agent releases, the user-pin stays put
429
+ * and routing falls back to it on the next turn.
430
+ */
431
+ private pinned;
432
+ /**
433
+ * Transient flow lock — set when a stateful agent activates, cleared on release. While
434
+ * non-null, routing precedence goes to this agent and the picker is locked. Independent of
435
+ * the user pin so completing a flow doesn't wipe the user's sticky choice.
436
+ */
437
+ private flowOwner;
438
+ /**
439
+ * Per-agent restored state for a rehydrated session, keyed by agent name. Primed by the host on
440
+ * restore; each entry is passed to that agent's `onActivate` as `ctx.restored` on its next
441
+ * activation, then deleted so a later re-activation starts fresh.
442
+ */
443
+ private restoredStates;
444
+ constructor(deps: AgentSessionDeps);
445
+ /** The active agent, or `undefined` before the first activation and after a release. */
446
+ getActive(): AgentConfig | undefined;
447
+ /** The sticky user pin, or `null` for automatic routing. */
448
+ getPinned(): string | null;
449
+ /** The transient flow lock, or `null` when no flow holds routing. */
450
+ getFlowOwner(): string | null;
451
+ /**
452
+ * The name routing is locked to, flow lock first. A flow in progress outranks the user's
453
+ * sticky pick for the duration of the flow; neither being set means automatic routing.
454
+ */
455
+ getLockedName(): string | null;
456
+ /** Set the sticky user pin. `null` returns to automatic routing. */
457
+ setPinned(name: string | null): void;
458
+ /**
459
+ * Set the flow-owner lock directly — used to restore state on driver reconstruction
460
+ * (agents-array changes, pop-in/pop-out), not to steer routing. Writes without firing hooks or
461
+ * reporting a transition, because the flow it names is already running.
462
+ */
463
+ setFlowOwner(name: string | null): void;
464
+ /** Prime per-agent restored state for a rehydrated session. Replaces any pending entries. */
465
+ primeRestoredStates(snapshots: Record<string, unknown>): void;
466
+ /** Whether a restored snapshot is waiting for this agent's next activation. */
467
+ hasRestoredState(agentName: string): boolean;
468
+ /**
469
+ * Make `agent` active, firing the lifecycle hooks around the swap: outgoing `onDeactivate`
470
+ * first, then incoming `onActivate`. Both are awaited so heavy activation work (a machine
471
+ * restore, say) completes before the agent's first turn runs, and a throw from either is logged
472
+ * rather than propagated — an agent whose hook fails still activates.
473
+ *
474
+ * Re-activating the same agent by name is not a switch: no hooks fire, no lock moves, and no
475
+ * handoff is recorded. That guard matters because a per-turn re-resolve calls through here.
476
+ */
477
+ activate(agent: AgentConfig): Promise<AgentTransition>;
478
+ /**
479
+ * Release the active agent: fire `onDeactivate` and drop the flow lock.
480
+ *
481
+ * The user pin is intentionally **not** cleared — someone who explicitly picked this agent
482
+ * should stay on it after the flow ends, so the next message starts a fresh flow on the same
483
+ * agent rather than routing through the classifier.
484
+ *
485
+ * Returns `undefined` when there was no active agent, so a caller can skip its events.
486
+ */
487
+ release(): Promise<AgentTransition | undefined>;
488
+ /**
489
+ * Fire `onDeactivate` on the active agent and abort pending lifecycle work, for session
490
+ * teardown, so machines release resources cleanly. The session is not reusable afterwards: the
491
+ * abort signal stays aborted, so a later `activate` would hand its hook a dead signal.
492
+ *
493
+ * `onDeactivate` runs **before** the abort, deliberately: teardown that needs to flush state
494
+ * would otherwise be handed an already-aborted signal and have to skip its own work. The cost is
495
+ * that an `onActivate` still in flight keeps running until `onDeactivate` finishes — both hold
496
+ * the same signal. Acceptable because the two are sequenced by `activate`, so overlap only
497
+ * happens if a hook resolves before the work it started; a hook that must stop immediately
498
+ * should own its own cancellation.
499
+ */
500
+ dispose(): Promise<void>;
501
+ /**
502
+ * The one place `onDeactivate` is invoked. `reason` only reaches the warning text, which is what
503
+ * tells a log reader which path fired.
504
+ */
505
+ private fireDeactivate;
506
+ }
507
+
508
+ /**
509
+ * Callbacks an {@link AgentSession} needs from its owner.
510
+ *
511
+ * @beta
512
+ */
513
+ export declare interface AgentSessionDeps {
514
+ /**
515
+ * The debug-log session key, read per call rather than captured: a driver's key is assigned
516
+ * after construction on some paths, so a value captured here would pin the wrong one.
517
+ */
518
+ sessionKey: () => string;
519
+ }
520
+
521
+ /**
522
+ * What changed when an agent was activated or released.
523
+ *
524
+ * Returned rather than dispatched: the host listens on the `OrchestratingDriver`, not on the
525
+ * `ChatDriver` that owns the session, so a self-dispatching session would need telling which
526
+ * target to fire on.
527
+ *
528
+ * @beta
529
+ */
530
+ export declare interface AgentTransition {
531
+ /** The agent that was active before, if any. */
532
+ previousAgent?: AgentConfig;
533
+ /** True when the activation was a real swap rather than a re-apply of the same agent. */
534
+ switched: boolean;
535
+ /**
536
+ * Set when the flow-owner lock changed, to the value it changed *to* (`null` on release).
537
+ * `undefined` means unchanged — distinct from `null`, so a caller can tell "cleared" from
538
+ * "not touched" without re-reading.
539
+ */
540
+ flowOwnerChangedTo?: string | null;
541
+ }
542
+
408
543
  /**
409
544
  * Union of all available animation names, derived from {@link ANIMATION_DEFS}.
410
545
  *
@@ -622,8 +757,12 @@ export declare interface AiDriver extends EventTarget {
622
757
  /**
623
758
  * Prime per-agent restored state for a session being rehydrated. Each entry
624
759
  * (keyed by agent name) is handed to that agent's `onActivate`/`init` as
625
- * `ctx.restored` on its next activation, then consumed. Optional — only
626
- * multi-agent drivers that own agent activation implement it.
760
+ * `ctx.restored` on its next activation, then consumed.
761
+ *
762
+ * Implemented by every driver since GENC-1580 — each owns an `AgentSession` — so "only
763
+ * multi-agent drivers implement it" is no longer true. Still declared optional: this interface
764
+ * is exported and `@beta`, so requiring it would break any third party implementing it. The `?`
765
+ * comes off with the `installAgent` rename, which is the ticket's one breaking release.
627
766
  */
628
767
  primeRestoredAgentState?(snapshots: Record<string, unknown>): void;
629
768
  /**
@@ -641,10 +780,33 @@ export declare interface AiDriver extends EventTarget {
641
780
  * The live `AgentConfig` currently active in the driver — the instance whose
642
781
  * `onActivate` ran, so its `serialize`/`getDebugSnapshot` closures hold live
643
782
  * state. Prefer this over looking the agent up in the host's `agents` array,
644
- * which may mint fresh, never-activated configs. Optional — only drivers that
645
- * retain the active config implement it.
783
+ * which may mint fresh, never-activated configs.
784
+ *
785
+ * `undefined` means no agent is active (before the first activation, after a release), which is
786
+ * a different statement from the driver not being able to answer. Both drivers implement it
787
+ * since GENC-1580; see {@link AiDriver.primeRestoredAgentState} for why the `?` stays for now.
646
788
  */
647
789
  getActiveAgent?(): AgentConfig | undefined;
790
+ /**
791
+ * Pin routing to an agent by name, or `null` for automatic routing. A pin is sticky: it
792
+ * survives a stateful flow completing, so the next turn starts fresh on the same agent.
793
+ *
794
+ * On a driver with nothing to route between this is recorded rather than acted on — the pick
795
+ * belongs to the session, which outlives the question of which driver holds it.
796
+ *
797
+ * Added to the interface in GENC-1580 — it was previously reached by narrowing to
798
+ * `OrchestratingDriver` with `instanceof`. Optional, so adding it breaks no implementor.
799
+ */
800
+ setPinnedAgent?(name: string | null): void;
801
+ /**
802
+ * Set the transient flow lock — used to restore state on driver reconstruction (agents-array
803
+ * changes, pop-in/pop-out), **not** to steer routing; that is
804
+ * {@link AiDriver.setPinnedAgent}'s job. Writes without firing lifecycle hooks, because the
805
+ * flow it names is already running.
806
+ *
807
+ * Added to the interface in GENC-1580, as for {@link AiDriver.setPinnedAgent}.
808
+ */
809
+ setFlowOwner?(name: string | null): void;
648
810
  /**
649
811
  * Headroom the next turn could need, and which agent set that requirement
650
812
  * (GENC-1582). `undefined` when no agent the turn could land on declares a
@@ -1411,10 +1573,8 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1411
1573
  * (`callPhaseEpoch < currentPhaseEpoch`), with an `agentEnd` backstop.
1412
1574
  */
1413
1575
  private currentPhaseEpoch;
1414
- /** Stack of fold frames — grows when a fold opens, shrinks when it closes. */
1415
- private foldStack;
1416
- /** Consecutive fold open/close ops without a real tool call. Reset on real tool execution. */
1417
- private consecutiveFoldOps;
1576
+ /** Tool folds: the open-fold stack, the no-progress counter, and the tool-surface swap. */
1577
+ private readonly folds;
1418
1578
  /** Consecutive unknown-tool calls without a real tool call. Reset on real tool execution. */
1419
1579
  private consecutiveUnknownToolCalls;
1420
1580
  /**
@@ -1616,6 +1776,21 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1616
1776
  private readonly condenseBatchCalls;
1617
1777
  /** Session identity used to file meta events onto the shared debug-log timeline. */
1618
1778
  private readonly sessionKey;
1779
+ /** See {@link ChatDriverConfig.deferAgentRelease}. */
1780
+ private readonly deferAgentRelease;
1781
+ /**
1782
+ * The stateful-agent contract for this driver: active agent, user pin, flow lock, restored
1783
+ * state, and the `onActivate` / `onDeactivate` hooks (§7.1).
1784
+ *
1785
+ * **Public because `OrchestratingDriver` reaches through to it.** The orchestrator decorates a
1786
+ * config before installing it (the cross-agent continuation tool), so it cannot route through
1787
+ * {@link ChatDriver.activateAgent} and must drive the session itself — and one shared session is
1788
+ * the point: two would let `getActiveAgent()` disagree between the outer and inner driver.
1789
+ * `@internal` rather than private so that reach is legal without six delegating one-liners.
1790
+ *
1791
+ * @internal
1792
+ */
1793
+ readonly agentSession: AgentSession;
1619
1794
  /** Injected activity bus; defaults to a no-op off-browser (Node/tests/headless). */
1620
1795
  private readonly activityBus;
1621
1796
  /**
@@ -1689,7 +1864,7 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1689
1864
  * interaction is instead settled by `resolveInteraction` (user response or
1690
1865
  * timeout), which also stamps history and reverts the chat-input override.
1691
1866
  */
1692
- dispose(): void;
1867
+ dispose(): Promise<void>;
1693
1868
  /**
1694
1869
  * Stop the current turn (user "stop" button). Aborts the in-flight provider
1695
1870
  * request immediately; if a tool is mid-execution it runs to completion and
@@ -1710,15 +1885,6 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1710
1885
  * cached history would otherwise gain a stray marker on remount).
1711
1886
  */
1712
1887
  private completeAbortedTurn;
1713
- /**
1714
- * Build the `done` loop result, carrying the typed failure reason when the turn
1715
- * bailed (PTC-0). The discriminant stays `'done'` either way — the same value a
1716
- * clean turn returns — so consumers matching on `reason === 'done'` are unchanged;
1717
- * `failureReason` is simply present on a failure and absent on success. Omitted
1718
- * (rather than set to `undefined`) so a happy-path result stays byte-identical to
1719
- * the historical `{ reason: 'done' }`.
1720
- */
1721
- private turnDone;
1722
1888
  /**
1723
1889
  * End the turn on a terminal failure: fail upward when this driver is a sub-agent, speak
1724
1890
  * to the user when it is not, and hand the reason to the caller either way.
@@ -1793,8 +1959,55 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1793
1959
  */
1794
1960
  private loopEndDetail;
1795
1961
  /**
1796
- * Swap in a new agent's configuration. Called by OrchestratingDriver before
1797
- * each specialist turn so the shared driver runs with the right tools and prompt.
1962
+ * Make `config` the active agent, firing its lifecycle hooks — `onDeactivate` on the outgoing
1963
+ * agent, then `onActivate` on this one — before installing it.
1964
+ *
1965
+ * This is the entry point a host should use. {@link ChatDriver.applyAgent} installs a config
1966
+ * *without* firing hooks, which is right for the two callers that need it (the orchestrator,
1967
+ * which fires them through the shared session, and `invokeSubAgent`, where a child's config is
1968
+ * not an activation) and wrong for everyone else.
1969
+ *
1970
+ * @beta
1971
+ */
1972
+ activateAgent(config: AgentConfig): Promise<void>;
1973
+ /**
1974
+ * {@inheritDoc AiDriver.getActiveAgent}
1975
+ *
1976
+ * @beta
1977
+ */
1978
+ getActiveAgent(): AgentConfig | undefined;
1979
+ /** {@inheritDoc AiDriver.primeRestoredAgentState} */
1980
+ primeRestoredAgentState(snapshots: Record<string, unknown>): void;
1981
+ /**
1982
+ * Record the sticky agent pin. See {@link AiDriver.setPinnedAgent} for the contract.
1983
+ *
1984
+ * Recorded, not acted on: this driver has one agent and nothing to route between, so a pin
1985
+ * changes no behaviour here. It is still honoured as state, because the session outlives the
1986
+ * question of which driver is holding it — a host that pins before an orchestrator is built
1987
+ * would otherwise lose the pick.
1988
+ *
1989
+ * @beta
1990
+ */
1991
+ setPinnedAgent(name: string | null): void;
1992
+ /**
1993
+ * Record the transient flow lock. See {@link AiDriver.setFlowOwner} for the contract.
1994
+ *
1995
+ * Recorded, not acted on, for the same reason as {@link ChatDriver.setPinnedAgent}.
1996
+ *
1997
+ * @beta
1998
+ */
1999
+ setFlowOwner(name: string | null): void;
2000
+ /**
2001
+ * Install `config` as the active agent **without firing lifecycle hooks**.
2002
+ *
2003
+ * Prefer {@link ChatDriver.activateAgent} unless you are deliberately skipping the hooks. Two
2004
+ * callers are: `OrchestratingDriver`, which fires them through the shared
2005
+ * {@link ChatDriver.agentSession} and then installs a decorated config, and `invokeSubAgent`,
2006
+ * where the child driver's config is a tool surface rather than an activation.
2007
+ *
2008
+ * Renamed to `installAgent` in a follow-up (GENC-1580) — see `docs/migration-GENC-1580.md`.
2009
+ * Until then the name says less than it should, so do not add a third caller to this path
2010
+ * without checking which of the two above it resembles.
1798
2011
  */
1799
2012
  applyAgent(config: AgentConfig): void;
1800
2013
  /**
@@ -1832,12 +2045,6 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1832
2045
  * throw all yield empty fields — attribution is diagnostic, never load-bearing.
1833
2046
  */
1834
2047
  private resolveStatusForProvider;
1835
- /**
1836
- * Resolve a per-turn config input that is either a static value or a function
1837
- * of the turn context — the value-or-resolver shape shared by `provider`,
1838
- * `temperature`, and `toolChoice`. Returns undefined when the input is unset.
1839
- */
1840
- private resolveTurnInput;
1841
2048
  /**
1842
2049
  * Returns the early-stop result set by `completeSubAgent`, if any.
1843
2050
  * Called by a parent `ChatDriver` after running this instance as a sub-agent.
@@ -1875,6 +2082,15 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1875
2082
  * Consumed by the orchestrator to trigger the auto-pin release path.
1876
2083
  */
1877
2084
  getAgentReleaseRequested(): boolean;
2085
+ /**
2086
+ * Act on a `releaseAgent` call from a tool handler: fire `onDeactivate` and drop the flow lock,
2087
+ * so the next turn starts clean and the persister does not serialize a flow that is over.
2088
+ *
2089
+ * No-op when the owner handles release ({@link ChatDriverConfig.deferAgentRelease}). The flag is
2090
+ * cleared here rather than only at the next `sendMessage`, so the signal cannot be acted on twice
2091
+ * and release a later agent that never asked.
2092
+ */
2093
+ private releaseIfRequested;
1878
2094
  /**
1879
2095
  * Return the per-turn snapshots captured so far. Used by the host's debug
1880
2096
  * log exporter to show what the LLM saw on each turn — system prompt, tool
@@ -1975,6 +2191,11 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1975
2191
  * unaffected — which is what makes the elision recoverable.
1976
2192
  */
1977
2193
  getHistoryForPersistence(): ChatMessage[];
2194
+ /**
2195
+ * {@inheritDoc AiDriver.getSuggestions}
2196
+ *
2197
+ * @beta
2198
+ */
1978
2199
  getSuggestions(history: ChatMessage[], prompt: string, count: number, allAgentInfo?: AllAgentSummary[]): Promise<string[]>;
1979
2200
  /**
1980
2201
  * Whether `compact()` would do anything right now — a summarizable run of older
@@ -2124,36 +2345,6 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
2124
2345
  * Used by OrchestratingDriver after an agent handoff.
2125
2346
  */
2126
2347
  continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
2127
- /** Extract ToolFold metadata from a handler, or undefined if it isn't a fold facade. */
2128
- private getFold;
2129
- /**
2130
- * Search all currently registered fold facades (and their nested folds recursively)
2131
- * to find which fold contains a given tool name. Returns the immediate parent fold name.
2132
- */
2133
- private findFoldContaining;
2134
- /**
2135
- * If an open fold is hiding a previously-available tool, return the name of
2136
- * the fold to close to start getting it back. Only exclusive folds hide tools
2137
- * (they replace the tool set on open rather than extending it), so a base tool
2138
- * that was visible before the fold opened now sits in a fold-stack frame's
2139
- * `previousHandlers` but not in the live handler map. Only the top fold's
2140
- * `close_` tool is active, so that's always the actionable next step — even
2141
- * when the tool lives further down the stack, closing repeatedly walks back to
2142
- * it. Returns null when no open fold accounts for the tool.
2143
- */
2144
- private foldHidingTool;
2145
- /**
2146
- * Install the fold's inner tool set, replacing (exclusive) or extending (non-exclusive)
2147
- * the current tool set. Also injects the close tool. Does NOT touch the fold stack.
2148
- */
2149
- private applyFoldToolSet;
2150
- /** Open a fold: push a stack frame, swap the tool set, return the response message. */
2151
- private openFold;
2152
- private pushFoldFrame;
2153
- /** Close the top fold: pop the stack frame, restore the previous tool set. */
2154
- private closeFold;
2155
- /** Build the fold-awareness suffix appended to the system prompt each LLM call. */
2156
- private buildFoldSystemPromptSuffix;
2157
2348
  private runToolLoop;
2158
2349
  /**
2159
2350
  * Append prose the DRIVER wrote rather than the model — a failure notice, a limit explanation,
@@ -2237,6 +2428,19 @@ export declare interface ChatDriverConfig {
2237
2428
  * reversible change.
2238
2429
  */
2239
2430
  budgetExhaustedMessage?: string;
2431
+ /**
2432
+ * Leave acting on `releaseAgent` to the owner of this driver.
2433
+ *
2434
+ * Set by `OrchestratingDriver` for its inner driver, which performs the release itself after the
2435
+ * turn so it can fire the host-facing `agent-released` / `agent-changed` / `flow-owner-changed`
2436
+ * events. Both drivers share one `AgentSession`, so releasing here first would empty it and make
2437
+ * the orchestrator's own release a silent no-op — leaving the host with a locked picker and a
2438
+ * stale active agent.
2439
+ *
2440
+ * Left unset, a driver releases its own agent, which is what a bare `ChatDriver` needs: nothing
2441
+ * else is watching the flag.
2442
+ */
2443
+ deferAgentRelease?: boolean;
2240
2444
  /**
2241
2445
  * Overrides the transcript sentence shown when the upstream PROVIDER refuses the account
2242
2446
  * (GENC-1506) — see `TURN_FAILURE_EXITS.providerRefused.copy`.
@@ -3353,8 +3557,8 @@ export declare class FoundationAiAssistant extends GenesisElement {
3353
3557
  /**
3354
3558
  * Aggregated USD cost across every chat turn in this session.
3355
3559
  *
3356
- * Read-only (unlike previously): it is a projection of `sessionUsage`, and a
3357
- * host assigning it would have set a figure the next transcript change overwrites.
3560
+ * Read-only: a projection of `sessionUsage`, so an assigned figure would be overwritten by
3561
+ * the next transcript change.
3358
3562
  */
3359
3563
  get sessionCostUsd(): number;
3360
3564
  /**
@@ -3545,14 +3749,9 @@ export declare class FoundationAiAssistant extends GenesisElement {
3545
3749
  showingSplash: boolean;
3546
3750
  private driver?;
3547
3751
  /**
3548
- * This element's driver as an orchestrator, or `undefined` when the session is
3549
- * running a bare `ChatDriver`.
3550
- *
3551
- * The five call sites that used to test the type themselves now read this
3552
- * (§11.2 constraint 3 of `docs/code-health.md`): the agent pin, the flow-owner
3553
- * lock, the wire-up in `wireDriver`, and the two `driverKind` diagnostics. The
3554
- * type test itself lives in `asOrchestrator`, shared with the session
3555
- * persister.
3752
+ * This element's driver as an orchestrator, or `undefined` when the session is running a bare
3753
+ * `ChatDriver`. The only place in this element that narrows the type; the test itself lives in
3754
+ * `asOrchestrator`, shared with the session persister.
3556
3755
  */
3557
3756
  private get orchestrator();
3558
3757
  private driverCleanup?;
@@ -3681,8 +3880,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
3681
3880
  */
3682
3881
  get activeInteractionRow(): ChatMessage[];
3683
3882
  agentsChanged(): void;
3684
- /** Returns a stable fingerprint for an agents array based on agent names and tool handler keys. */
3685
- private getAgentsKey;
3686
3883
  /**
3687
3884
  * Pure factory — creates a ChatDriver or OrchestratingDriver based on the
3688
3885
  * current agent configuration. Does not wire event listeners or register in
@@ -3804,10 +4001,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
3804
4001
  * history no longer shrinks the totals.
3805
4002
  */
3806
4003
  private syncUsageTotals;
3807
- /**
3808
- * Runs side effects that were previously in `messagesChanged()`.
3809
- * Called from the `messages` setter after dispatching to the store.
3810
- */
4004
+ /** Transcript side effects, called from the `messages` setter after it dispatches to the store. */
3811
4005
  private handleMessagesUpdate;
3812
4006
  showLoadingIndicatorChanged(): void;
3813
4007
  private scrollToBottom;
@@ -4628,10 +4822,16 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
4628
4822
  private readonly classifierRetries;
4629
4823
  private readonly sessionKey;
4630
4824
  /**
4631
- * Aborted on driver disposal. Threaded into `AgentLifecycleContext.signal`
4632
- * so long-running `onActivate` work can bail if the session disconnects.
4825
+ * The stateful-agent contract (§7.1): active agent, user pin, flow lock, restored state, and
4826
+ * the `onActivate` / `onDeactivate` hooks.
4827
+ *
4828
+ * **The inner driver's session, not one of our own.** One session is the invariant: two would
4829
+ * let the outer and inner driver disagree about which agent is active, and the inner one would
4830
+ * never be activated at all, because this driver installs a *decorated* config through
4831
+ * `applyAgent` rather than going via `ChatDriver.activateAgent`. This driver keeps the routing
4832
+ * decisions; the session owns the lifecycle.
4633
4833
  */
4634
- private readonly lifecycleAbortController;
4834
+ private get agentSession();
4635
4835
  /**
4636
4836
  * Set by `cancel()` for the duration of a `sendMessage` run. Breaks the
4637
4837
  * handoff loop so we don't classify or start another agent turn after the
@@ -4650,28 +4850,12 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
4650
4850
  */
4651
4851
  private userMessageAppended;
4652
4852
  /**
4653
- * Sticky user pick from the picker (or the host's `setAgent` API). Only
4654
- * changes on explicit user action. Survives flow completion: when a stateful
4655
- * agent releases, the user-pin stays put and routing falls back to it on
4656
- * the next turn.
4657
- */
4658
- private pinnedAgentName;
4659
- /**
4660
- * Transient flow lock — set when a stateful agent's `onActivate` fires,
4661
- * cleared by `releaseActiveAgent`. While non-null, routing precedence goes
4662
- * to this agent and the picker is locked. Independent of
4663
- * `pinnedAgentName` so completing a flow doesn't wipe the user's sticky
4664
- * choice.
4665
- */
4666
- private flowOwnerAgentName;
4667
- /**
4668
- * Per-agent restored state for a rehydrated session, keyed by agent name.
4669
- * Primed by the host on restore; each entry is passed to that agent's
4670
- * `onActivate` as `ctx.restored` on its next activation, then deleted so a
4671
- * later re-activation starts fresh.
4853
+ * The active agent, or `undefined` before the first activation and after a release.
4854
+ *
4855
+ * Kept as an accessor over `agentSession` rather than removed: it is a public member of an
4856
+ * exported class, so nothing in this package reading it is not the same as nothing reading it.
4672
4857
  */
4673
- private restoredAgentStates;
4674
- activeAgent?: AgentConfig;
4858
+ get activeAgent(): AgentConfig | undefined;
4675
4859
  constructor(providerRegistry: AIProviderRegistry, agents: AgentConfig[], options?: {
4676
4860
  sessionKey?: string;
4677
4861
  maxHandoffs?: number;
@@ -4912,7 +5096,11 @@ declare type ProviderRefusedDetail = NonNullable<Extract<ChatDriverResult, {
4912
5096
  reason: 'done';
4913
5097
  }>['providerRefused']>;
4914
5098
 
4915
- /** Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver. */
5099
+ /**
5100
+ * Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver.
5101
+ *
5102
+ * @beta
5103
+ */
4916
5104
  export declare const REQUEST_CONTINUATION_TOOL = "request_continuation";
4917
5105
 
4918
5106
  /** Resolved context-headroom thresholds for the active model. */
@@ -5799,11 +5987,9 @@ export declare interface TurnSnapshot {
5799
5987
  /** Name of the agent active when this LLM call ran. */
5800
5988
  agentName?: string;
5801
5989
  /**
5802
- * The agent's resolved system prompt sent to the LLM, verbatim. NOTE: this is
5803
- * now the *bare* prompt — the fold-surface suffix and the malformed/empty retry
5804
- * nudge no longer mutate it (they kept it byte-unstable and busted prompt
5805
- * caching), so they moved to {@link TurnSnapshot.tailContext}. To reconstruct
5806
- * exactly what the model saw, read both fields.
5990
+ * The agent's resolved system prompt sent to the LLM, verbatim — the **bare** prompt. The
5991
+ * fold-surface suffix and the retry nudges live in {@link TurnSnapshot.tailContext} instead, to
5992
+ * keep this byte-stable for prompt caching; read both fields to reconstruct what the model saw.
5807
5993
  */
5808
5994
  systemPrompt?: string;
5809
5995
  /**