@genesislcap/ai-assistant 15.84.3 → 15.85.0-FUI-2666.2

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 (50) hide show
  1. package/dist/ai-assistant.api.json +2175 -138
  2. package/dist/ai-assistant.d.ts +420 -43
  3. package/dist/chat-driver.cjs +780 -210
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +780 -209
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +1514 -424
  8. package/dist/dts/components/agent-session/agent-session.d.ts +50 -2
  9. package/dist/dts/components/agent-session/agent-session.d.ts.map +1 -1
  10. package/dist/dts/components/ai-driver/ai-driver.d.ts +59 -1
  11. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  12. package/dist/dts/components/ai-driver/base-driver-decorator.d.ts +13 -1
  13. package/dist/dts/components/ai-driver/base-driver-decorator.d.ts.map +1 -1
  14. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts +0 -8
  15. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts.map +1 -1
  16. package/dist/dts/components/chat-driver/chat-driver.d.ts +74 -15
  17. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  18. package/dist/dts/components/orchestrating-driver/hand-off.d.ts +20 -0
  19. package/dist/dts/components/orchestrating-driver/hand-off.d.ts.map +1 -0
  20. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +121 -22
  21. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  22. package/dist/dts/config/config.d.ts +66 -0
  23. package/dist/dts/config/config.d.ts.map +1 -1
  24. package/dist/dts/main/main.d.ts +3 -0
  25. package/dist/dts/main/main.d.ts.map +1 -1
  26. package/dist/dts/main/main.types.d.ts +6 -0
  27. package/dist/dts/main/main.types.d.ts.map +1 -1
  28. package/dist/dts/react.d.ts +4 -1
  29. package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
  30. package/dist/dts/state/persistence/session-snapshot.d.ts +33 -1
  31. package/dist/dts/state/persistence/session-snapshot.d.ts.map +1 -1
  32. package/dist/dts/utils/condense-history.d.ts +10 -3
  33. package/dist/dts/utils/condense-history.d.ts.map +1 -1
  34. package/dist/esm/components/agent-session/agent-session.js +128 -7
  35. package/dist/esm/components/ai-driver/ai-driver.js +3 -0
  36. package/dist/esm/components/ai-driver/base-driver-decorator.js +24 -0
  37. package/dist/esm/components/chat-driver/chat-driver.constants.js +0 -8
  38. package/dist/esm/components/chat-driver/chat-driver.js +247 -74
  39. package/dist/esm/components/orchestrating-driver/hand-off.js +59 -0
  40. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +480 -299
  41. package/dist/esm/main/main.js +32 -6
  42. package/dist/esm/main/main.types.js +6 -0
  43. package/dist/esm/state/debug-event-log.js +1 -1
  44. package/dist/esm/state/persistence/session-persister.js +55 -8
  45. package/dist/esm/state/persistence/session-snapshot.js +12 -6
  46. package/dist/esm/utils/condense-history.js +13 -4
  47. package/dist/react.cjs +16 -1
  48. package/dist/react.mjs +16 -1
  49. package/docs/sub_agent.md +1 -1
  50. package/package.json +17 -17
@@ -25,6 +25,9 @@ import type { CondensePolicy } from '@genesislcap/foundation-ai';
25
25
  import { Container } from '@genesislcap/web-core';
26
26
  import { ElementStyles } from '@genesislcap/web-core';
27
27
  import { GenesisElement } from '@genesislcap/web-core';
28
+ import type { HandOffCompletion } from '@genesislcap/foundation-ai';
29
+ import type { HandOffRequest } from '@genesislcap/foundation-ai';
30
+ import type { HandOffResult } from '@genesislcap/foundation-ai';
28
31
  import { InteractionEdit } from '@genesislcap/foundation-ai';
29
32
  import type { InteractionRequestOptions } from '@genesislcap/foundation-ai';
30
33
  import type { InteractionResult } from '@genesislcap/foundation-ai';
@@ -531,8 +534,8 @@ export { AgentPickerMode }
531
534
  * and the `onActivate` / `onDeactivate` lifecycle around a swap. Either driver can own one.
532
535
  *
533
536
  * Knows nothing about routing, tool surfaces or transcripts — whether an agent is terminal, the
534
- * continuation tool, the handoff marker in history all stay with the orchestrator, which is the
535
- * only thing with agents to route between.
537
+ * hand-off stack, the divider in history all stay with the orchestrator, which is the only thing
538
+ * with agents to route between.
536
539
  *
537
540
  * @beta
538
541
  */
@@ -564,6 +567,17 @@ export declare class AgentSession {
564
567
  private restoredStates;
565
568
  /** The tail of the activation queue — see {@link AgentSession.activate}. */
566
569
  private activation;
570
+ /**
571
+ * Callers waiting in a hand-off (GENC-1737), oldest first: still live, so never deactivated by
572
+ * the hand-off, and resumed without `onActivate`. Empty after a reload, when a caller has no live
573
+ * state and is activated again from its saved state instead.
574
+ */
575
+ private suspended;
576
+ /**
577
+ * The `data` of the hand-off that activated each agent (GENC-1737), for its tool handlers: kept
578
+ * while it is active or waits as a caller, and dropped when its activation ends.
579
+ */
580
+ private handOffData;
567
581
  constructor(deps: AgentSessionDeps);
568
582
  /** The active agent, or `undefined` before the first activation and after a release. */
569
583
  getActive(): AgentConfig | undefined;
@@ -589,6 +603,39 @@ export declare class AgentSession {
589
603
  primeRestoredStates(snapshots: Record<string, unknown>): void;
590
604
  /** Whether a restored snapshot is waiting for this agent's next activation. */
591
605
  hasRestoredState(agentName: string): boolean;
606
+ /** The `data` of the hand-off that activated `agentName`, if one did and carried any. */
607
+ getHandOffData(agentName: string): unknown;
608
+ /** Record the `data` a hand-off activated `agentName` with, or restore it; `undefined` drops it. */
609
+ setHandOffData(agentName: string, data: unknown): void;
610
+ /**
611
+ * Drop the flow lock and leave the active agent active (GENC-1737): a plain agent holding it with
612
+ * no hand-off behind it is done once its turn ends.
613
+ */
614
+ dropFlowLock(): void;
615
+ /** The callers waiting in a hand-off whose state is live, oldest first. */
616
+ getSuspended(): readonly AgentConfig[];
617
+ /**
618
+ * Make `target` active for a hand-off (GENC-1737), holding the flow lock whatever kind of agent
619
+ * it is, since a waiting caller depends on it. With `suspendCaller` the active agent waits:
620
+ * it is not deactivated and keeps its state. Without it the active agent is deactivated as on
621
+ * any swap: a one-way hand-off from a target, whose waiting caller then waits for the new
622
+ * target. Queued with {@link AgentSession.activate}.
623
+ */
624
+ handOff(target: AgentConfig, options: {
625
+ suspendCaller: boolean;
626
+ }): Promise<AgentTransition>;
627
+ /**
628
+ * Make a waiting caller active again when its hand-off ends (GENC-1737). One whose state is live
629
+ * resumes without `onActivate`, since it was never deactivated; after a reload it is activated
630
+ * from its saved state like any other agent. `lock` keeps the flow lock on it: it held it before,
631
+ * or another caller is still waiting further down.
632
+ */
633
+ resume(caller: AgentConfig, lock: boolean): Promise<AgentTransition>;
634
+ /**
635
+ * Deactivate the callers left waiting by hand-offs that ended without a hand-back, newest first,
636
+ * when no teardown follows to do it (GENC-1737).
637
+ */
638
+ endSuspended(): Promise<void>;
592
639
  /**
593
640
  * Make `agent` active, firing the lifecycle hooks around the swap: outgoing `onDeactivate`
594
641
  * first, then incoming `onActivate`. Both are awaited so heavy activation work (a machine
@@ -605,6 +652,10 @@ export declare class AgentSession {
605
652
  * the flow owner outside a turn, where a send can land mid-activation.
606
653
  */
607
654
  activate(agent: AgentConfig): Promise<AgentTransition>;
655
+ /**
656
+ * `lock` takes the flow lock whatever kind of agent it is, and `suspendPrevious` leaves the
657
+ * outgoing agent waiting instead of deactivating it, as a hand-off does (GENC-1737).
658
+ */
608
659
  private activateNow;
609
660
  /**
610
661
  * Release the active agent: fire `onDeactivate` and drop the flow lock.
@@ -742,6 +793,12 @@ export declare const AiAssistantEvent: {
742
793
  readonly SessionCleared: "session-cleared";
743
794
  /** Header pressed in `popout-mode="expand"` (drag-to-popout hosts). `detail`: `ChatHeaderMouseDownDetail`. */
744
795
  readonly ChatHeaderMouseDown: "chat-header-mousedown";
796
+ /** An agent's hand-off started (GENC-1737). `detail`: `HandOffFrame`. */
797
+ readonly HandOffStarted: "handoff-started";
798
+ /** A hand-off ended and its caller is active again. `detail`: `HandOffFrame`, with `outcome`. */
799
+ readonly HandOffReturned: "handoff-returned";
800
+ /** A hand-off ended with no hand-back. `detail`: `HandOffFrame`, with `outcome`. */
801
+ readonly HandOffUnwound: "handoff-unwound";
745
802
  };
746
803
 
747
804
  /**
@@ -1033,6 +1090,58 @@ export declare interface AiDriver extends EventTarget {
1033
1090
  restored: number;
1034
1091
  awaitingReactivation: number;
1035
1092
  };
1093
+ /**
1094
+ * The hand-offs in progress, oldest first, to save with the session (GENC-1737). Copies: changing
1095
+ * them changes nothing.
1096
+ *
1097
+ * @beta
1098
+ */
1099
+ getHandOffStack?(): HandOffFrame[];
1100
+ /**
1101
+ * Restore saved hand-offs, before the condense policies, against the agents configured now and
1102
+ * the saved agent states. A frame whose caller has gone, or whose stateful caller has no saved
1103
+ * state (`interrupted` drops them all), is unwound with `handoff-unwound`. When the top frame's
1104
+ * target had finished, or has gone, its caller is due to resume: `resume` names it and the note
1105
+ * its resumed turn needs, and {@link AiDriver.resumeRestoredFlow} hands back. `droppedLocks`
1106
+ * names plain targets of unwound hand-offs, whose saved lock was only that hand-off's.
1107
+ *
1108
+ * @beta
1109
+ */
1110
+ restoreHandOffStack?(saved: unknown, savedStates: Record<string, unknown>, interrupted: boolean): {
1111
+ resume?: {
1112
+ agent: string;
1113
+ note: string;
1114
+ };
1115
+ droppedLocks?: string[];
1116
+ };
1117
+ /**
1118
+ * End every hand-off without a hand-back, newest first: the chat was cleared, or the agents
1119
+ * changed. Each raises `handoff-unwound`; on Clear a waiting caller's `onHandOffReturn` is told.
1120
+ *
1121
+ * @beta
1122
+ */
1123
+ unwindHandOffs?(kind: Extract<HandOffOutcomeKind, 'cleared' | 'state-lost'>): Promise<void>;
1124
+ /**
1125
+ * The saved state of each caller waiting in a hand-off whose state is live, by name, from its
1126
+ * `serialize`. Saved with the active agent's, so a reload can resume the caller.
1127
+ *
1128
+ * @beta
1129
+ */
1130
+ serializeWaitingCallers?(): Record<string, unknown>;
1131
+ /**
1132
+ * The `data` each hand-off target was activated with, by name, for the agents a reload resumes:
1133
+ * the flow owner and the callers waiting in a hand-off. See the tool context's `handOffData`.
1134
+ *
1135
+ * @beta
1136
+ */
1137
+ getHandOffData?(): Record<string, unknown>;
1138
+ /**
1139
+ * Restore what {@link AiDriver.getHandOffData} saved, before the hand-off stack. Nothing comes
1140
+ * back from a frozen (`interrupted`) session, whose flow does not resume.
1141
+ *
1142
+ * @beta
1143
+ */
1144
+ restoreHandOffData?(saved: unknown, interrupted: boolean): void;
1036
1145
  /**
1037
1146
  * The live `AgentConfig` currently active in the driver — the instance whose
1038
1147
  * `onActivate` ran, so its `serialize`/`getDebugSnapshot` closures hold live
@@ -1323,6 +1432,12 @@ export declare interface AiDriverEventMap {
1323
1432
  'agent-released': CustomEvent<AgentConfig>;
1324
1433
  /** The flow-owner lock changed. Detail: the new owner's name, or `null` when cleared. */
1325
1434
  'flow-owner-changed': CustomEvent<string | null>;
1435
+ /** A hand-off started: its target is now active (GENC-1737). */
1436
+ 'handoff-started': CustomEvent<HandOffFrame>;
1437
+ /** A hand-off ended and its caller is active again; `detail.outcome` says how. */
1438
+ 'handoff-returned': CustomEvent<HandOffFrame>;
1439
+ /** A hand-off ended with no hand-back: cleared, or found unresumable. */
1440
+ 'handoff-unwound': CustomEvent<HandOffFrame>;
1326
1441
  /** Routing started (the classifier is about to run). Orchestrator only. */
1327
1442
  'orchestrating-start': CustomEvent<undefined>;
1328
1443
  /** Routing finished and an agent is installed. Orchestrator only. */
@@ -2372,6 +2487,28 @@ declare interface BaseAgentConfig {
2372
2487
  * @beta
2373
2488
  */
2374
2489
  onDeactivate?: (ctx: AgentLifecycleContext) => void | Promise<void>;
2490
+ /**
2491
+ * Fires on the caller when its `handOff` takes effect, after its turn: it now waits, still active
2492
+ * in spirit, for `frame.target` to finish. Awaited before the target runs. GENC-1737.
2493
+ *
2494
+ * @beta
2495
+ */
2496
+ onHandOffStart?: (frame: HandOffFrame) => void | Promise<void>;
2497
+ /**
2498
+ * Fires on the caller when a hand-off it made ends, before its next model call, so its own state
2499
+ * can move on. `outcome.kind` says how it ended. Not fired for `state-lost`, whose caller cannot
2500
+ * resume. GENC-1737.
2501
+ *
2502
+ * @beta
2503
+ */
2504
+ onHandOffReturn?: (outcome: HandOffOutcome, frame: HandOffFrame) => void | Promise<void>;
2505
+ /**
2506
+ * The agents this one may hand off to. Left out, any other agent. A `handOff` to one not listed
2507
+ * is refused with `not-allowed`. GENC-1737.
2508
+ *
2509
+ * @beta
2510
+ */
2511
+ handOffTargets?: string[];
2375
2512
  /**
2376
2513
  * Returns an agent-supplied debug payload for the export log. Called once per
2377
2514
  * LLM call by the driver (alongside the resolved prompt and tool list) and
@@ -2534,6 +2671,18 @@ export declare abstract class BaseDriverDecorator<TInner extends AiDriver = AiDr
2534
2671
  restored: number;
2535
2672
  awaitingReactivation: number;
2536
2673
  };
2674
+ getHandOffStack(): HandOffFrame[];
2675
+ restoreHandOffStack(saved: unknown, savedStates: Record<string, unknown>, interrupted: boolean): {
2676
+ resume?: {
2677
+ agent: string;
2678
+ note: string;
2679
+ };
2680
+ droppedLocks?: string[];
2681
+ };
2682
+ unwindHandOffs(kind: Extract<HandOffOutcomeKind, 'cleared' | 'state-lost'>): Promise<void>;
2683
+ serializeWaitingCallers(): Record<string, unknown>;
2684
+ getHandOffData(): Record<string, unknown>;
2685
+ restoreHandOffData(saved: unknown, interrupted: boolean): void;
2537
2686
  getActiveAgent(): AgentConfig | undefined;
2538
2687
  setPinnedAgent(name: string | null): void;
2539
2688
  setFlowOwner(name: string | null): void;
@@ -2801,9 +2950,8 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
2801
2950
  /** Sub-agents declared on the active agent config, keyed by name. */
2802
2951
  private subAgentsMap;
2803
2952
  /**
2804
- * Set by `completeSubAgent` inside a sub-agent tool handler. Checked at the
2805
- * same point in the loop as `REQUEST_CONTINUATION_TOOL` — after tool results
2806
- * are appended — so the exit path mirrors the system-call pattern.
2953
+ * Set by `completeSubAgent` inside a sub-agent tool handler. Checked after tool
2954
+ * results are appended, so the turn ends without another model call.
2807
2955
  * `undefined` means the loop has not been stopped early.
2808
2956
  */
2809
2957
  private subAgentCompletion;
@@ -2835,6 +2983,23 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
2835
2983
  * doesn't leak forward.
2836
2984
  */
2837
2985
  private agentReleaseRequested;
2986
+ /** A `handOff` this call accepted, for the orchestrator to act on after it (GENC-1737). */
2987
+ private handOffRequested?;
2988
+ /** What `completeHandOff` handed back this call, beside the release it also requests. */
2989
+ private handOffCompletion?;
2990
+ /** See {@link ChatDriver.setHandOffCheck}. */
2991
+ private checkHandOff?;
2992
+ /**
2993
+ * The `agentEnd` activation of each caller waiting in a hand-off, by agent name, with the phase
2994
+ * epoch it was suspended at (GENC-1737). A waiting caller is suspended, not ended, so neither its
2995
+ * `agentEnd` payloads nor its `phaseEnd` ones (to another agent's `endPhase()`) collapse while the
2996
+ * target runs; when it is installed again its entries move to the new activation and, while
2997
+ * their phase had not ended, to the current epoch. A caller restored from a saved session gets
2998
+ * an activation placeholder below zero, which no live activation uses.
2999
+ */
3000
+ private readonly suspendedActivations;
3001
+ /** The last placeholder handed out for a restored caller; see `suspendedActivations`. */
3002
+ private restoredSuspensionSeq;
2838
3003
  /**
2839
3004
  * Per-turn snapshots, forwarded sub-agent snapshots, and harvested external diagnostics —
2840
3005
  * everything the exported debug log draws from this driver.
@@ -2980,8 +3145,8 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
2980
3145
  * The stateful-agent contract for this driver: active agent, user pin, flow lock, restored
2981
3146
  * state, and the `onActivate` / `onDeactivate` hooks (§7.1).
2982
3147
  *
2983
- * **Public because `OrchestratingDriver` reaches through to it.** The orchestrator decorates a
2984
- * config before installing it (the cross-agent continuation tool), so it cannot route through
3148
+ * **Public because `OrchestratingDriver` reaches through to it.** The orchestrator builds a
3149
+ * config before installing it (the fallback's prompt), so it cannot route through
2985
3150
  * {@link ChatDriver.activateAgent} and must drive the session itself — and one shared session is
2986
3151
  * the point: two would let `getActiveAgent()` disagree between the outer and inner driver.
2987
3152
  * `@internal` rather than private so that reach is legal without six delegating one-liners.
@@ -3223,17 +3388,13 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
3223
3388
  * does not show it twice. Marks it shown otherwise. Only the driver the user sees keeps count.
3224
3389
  */
3225
3390
  private aiServiceSentenceShown;
3226
- /**
3227
- * Whether a sub-agent hit a wall this turn that stops this driver too. A handoff goes on past an
3228
- * AI-service wall: the next agent may use another vendor, and if it uses this one its own call
3229
- * meets the wall and says so.
3230
- */
3391
+ /** Whether a sub-agent hit a wall this turn that stops this driver too. */
3231
3392
  private subAgentWallThisTurn;
3232
3393
  /**
3233
3394
  * Ends the turn when a sub-agent hit a wall this turn that stops this driver too: the provider
3234
3395
  * refusing the account, the AI service (GENC-1611), or the budget. Checked before each model call,
3235
- * and again right after a tool batch, before a handoff or the iteration cap can report the turn as
3236
- * something else.
3396
+ * and again right after a tool batch, before the iteration cap can report the turn as something
3397
+ * else.
3237
3398
  */
3238
3399
  private endTurnOnSubAgentWall;
3239
3400
  /** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
@@ -3406,6 +3567,53 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
3406
3567
  * Consumed by the orchestrator to trigger the auto-pin release path.
3407
3568
  */
3408
3569
  getAgentReleaseRequested(): boolean;
3570
+ /**
3571
+ * Offer `handOff` and `completeHandOff` to tool handlers, checking each `handOff` with `check`
3572
+ * (GENC-1737). Set by `OrchestratingDriver`, which knows the agents and the hand-off stack and acts
3573
+ * on the request after the call; a bare `ChatDriver` has no other agent to hand to.
3574
+ *
3575
+ * @internal
3576
+ */
3577
+ setHandOffCheck(check: (request: HandOffRequest, caller: string | undefined) => HandOffResult): void;
3578
+ /** The `handOff` the most recent call accepted, if any (GENC-1737). */
3579
+ getHandOffRequest(): HandOffRequest | undefined;
3580
+ /** What `completeHandOff` handed back in the most recent call, if it was called. */
3581
+ getHandOffCompletion(): HandOffCompletion | undefined;
3582
+ /**
3583
+ * A handler's `handOff`: refused once this call has handed off or released, or when `data` is
3584
+ * not JSON, and otherwise checked by the orchestrator. `data` is kept as a JSON copy, since it is
3585
+ * saved with the session and the handler may change its own object afterwards.
3586
+ */
3587
+ private requestHandOff;
3588
+ /**
3589
+ * The note of the closing call a hand-off asks for (GENC-1737), or `undefined` when none: once a
3590
+ * handler's `handOff` is accepted, or `completeHandOff` is called, the rest of that batch runs,
3591
+ * then one model call with no tools, so the agent can say so but not carry on, which a model told
3592
+ * so in a tool result does anyway. Then the turn ends.
3593
+ */
3594
+ private closingCallNote;
3595
+ /**
3596
+ * Suspend the active agent's activation for a hand-off it made (GENC-1737): installing the target
3597
+ * advances the `agentEnd` clock as any swap does, but this caller's payloads stay live.
3598
+ */
3599
+ suspendActivation(agentName: string): void;
3600
+ /**
3601
+ * Suspend restored callers before their condense policies are restored, so entries still live
3602
+ * at save time stay live instead of waiting for "the next activation", which is the target's.
3603
+ */
3604
+ suspendRestoredActivations(agentNames: readonly string[]): void;
3605
+ /** End every suspension: the hand-offs were unwound, so their callers' activations are over. */
3606
+ endSuspendedActivations(): void;
3607
+ /**
3608
+ * A caller installed again takes its suspended entries into the activation it resumes in, and
3609
+ * the ones whose phase had not ended into the current phase epoch.
3610
+ */
3611
+ private resumeSuspendedActivation;
3612
+ /**
3613
+ * The suspended activations, each with the phase epoch it was suspended at, for the `agentEnd`
3614
+ * and `phaseEnd` checks and for persistence.
3615
+ */
3616
+ private suspendedClocks;
3409
3617
  /**
3410
3618
  * Act on a `releaseAgent` call from a tool handler: fire `onDeactivate` and drop the flow lock,
3411
3619
  * so the next turn starts clean and the persister does not serialize a flow that is over.
@@ -3856,7 +4064,7 @@ export declare interface ChatDriverConfig {
3856
4064
  * Leave acting on `releaseAgent` to the owner of this driver.
3857
4065
  *
3858
4066
  * Set by `OrchestratingDriver` for its inner driver, which performs the release itself after the
3859
- * turn, because a release ends its handoff loop and it reads the flag to know. Both drivers share
4067
+ * call, because a release may hand back to a waiting caller and it reads the flag to know. Both drivers share
3860
4068
  * one `AgentSession`, and releasing here first would clear the flag before the orchestrator saw
3861
4069
  * it. The host-facing `agent-released` / `agent-changed` / `flow-owner-changed` events fire from
3862
4070
  * the session's transition either way (GENC-1485).
@@ -4696,6 +4904,9 @@ export declare function formatBlockedReason(budget?: Partial<Extract<ChatDriverR
4696
4904
  *
4697
4905
  * @fires chat-header-mousedown - Fired when the user presses the chat header in `popout-mode="expand"` (for drag-to-popout hosts). Bubbles and composed. detail: `ChatHeaderMouseDownDetail`
4698
4906
  * @fires session-cleared - Fired after the session is wiped (the lifecycle menu's Clear). The payload is captured before teardown so the host can clean up any external state the active agent was working on. Bubbles and composed. detail: `SessionClearedDetail`
4907
+ * @fires handoff-started - Fired when an agent's `handOff` takes effect and its target becomes active. Bubbles and composed. detail: `HandOffFrame`
4908
+ * @fires handoff-returned - Fired when a hand-off ends and its caller is active again; `detail.outcome` says how it ended. Bubbles and composed. detail: `HandOffFrame`
4909
+ * @fires handoff-unwound - Fired when a hand-off ends with no hand-back: the chat was cleared, the agents changed, or a restore found it could not resume. Bubbles and composed. detail: `HandOffFrame`
4699
4910
  *
4700
4911
  * @beta
4701
4912
  */
@@ -7018,6 +7229,53 @@ export declare interface HandlerCapabilityScope {
7018
7229
  readonly flowOwner: string | null;
7019
7230
  }
7020
7231
 
7232
+ /**
7233
+ * One hand-off on the stack, saved with the session: who waits, for whom, and why. `outcome` is set
7234
+ * when the target finishes, until the caller has resumed, so a reload in between still hands back.
7235
+ * Also the `detail` of the `handoff-started`, `handoff-returned` and `handoff-unwound` events.
7236
+ *
7237
+ * @beta
7238
+ */
7239
+ export declare interface HandOffFrame {
7240
+ id: string;
7241
+ /** The agent waiting for the hand-back. */
7242
+ caller: string;
7243
+ target: string;
7244
+ returnTo: boolean;
7245
+ /** The brief. */
7246
+ message: string;
7247
+ /** JSON only. */
7248
+ data?: unknown;
7249
+ /** ISO time the hand-off started. */
7250
+ startedAt: string;
7251
+ outcome?: HandOffOutcome;
7252
+ }
7253
+
7254
+ /**
7255
+ * What a hand-off's caller gets back. `message` is the target's `completeHandOff` summary, or for
7256
+ * `released` its final reply since the brief; `data` is what `completeHandOff` passed.
7257
+ *
7258
+ * @beta
7259
+ */
7260
+ export declare interface HandOffOutcome {
7261
+ kind: HandOffOutcomeKind;
7262
+ message?: string;
7263
+ data?: unknown;
7264
+ }
7265
+
7266
+ /**
7267
+ * How a hand-off ended (GENC-1737):
7268
+ * - `completed`: the target called `completeHandOff`;
7269
+ * - `released`: the target called `releaseAgent`, or, a plain one, ended its turn;
7270
+ * - `cleared`: the user cleared the chat;
7271
+ * - `agent-removed`: the caller or the target is no longer in the agent list, found on restore;
7272
+ * - `state-lost`: the caller's saved state is gone, so it cannot resume: a session saved while its
7273
+ * flow could not be resumed, or a change to the agents, which disposes them.
7274
+ *
7275
+ * @beta
7276
+ */
7277
+ export declare type HandOffOutcomeKind = 'completed' | 'released' | 'cleared' | 'agent-removed' | 'state-lost';
7278
+
7021
7279
  /**
7022
7280
  * A single in-flight per-call chat-input override pushed by a `requestSubAgent`
7023
7281
  * or `requestInteraction` call. Tracked as an array (not a counter) so the
@@ -7772,22 +8030,37 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7772
8030
  */
7773
8031
  private get agentSession();
7774
8032
  /**
7775
- * Set by `cancel()` for the duration of a `sendMessage` run. Breaks the
7776
- * handoff loop so we don't classify or start another agent turn after the
7777
- * user stops. Reset at the start of each `sendMessage`.
8033
+ * Set by `cancel()` for the duration of a run. Stops it before the next agent
8034
+ * runs — a hand-off's target or a hand-back's caller — so nothing more is asked
8035
+ * of a provider after the user stops. Reset at the start of each run.
7778
8036
  */
7779
8037
  private cancelled;
7780
8038
  /**
7781
8039
  * Whether the CURRENT turn's user message has reached the inner driver's
7782
8040
  * history. False through the pre-first-turn classify (where the only echo of
7783
8041
  * the message is an optimistic `history-updated` dispatch), true from the
7784
- * moment `chatDriver.sendMessage` is entered — including through every later
7785
- * handoff classify. Read by the budget-wall catch in `sendMessage` to decide
8042
+ * moment `chatDriver.sendMessage` is entered. Read by the budget-wall catch in
8043
+ * `sendMessage` to decide
7786
8044
  * whether `reportBudgetExhausted` must append the message itself: appending
7787
8045
  * it when already appended duplicated it; not appending it when unappended
7788
8046
  * made it vanish. Reset at the top of each `runOrchestratedTurn`.
7789
8047
  */
7790
8048
  private userMessageAppended;
8049
+ /** How many callers may wait in hand-offs at once (GENC-1737). */
8050
+ private readonly maxHandOffDepth;
8051
+ /**
8052
+ * The hand-offs in progress, oldest first (GENC-1737). The top frame's target is the active agent
8053
+ * and holds the flow lock; each caller waits, suspended, for the target above it. Saved with the
8054
+ * session through {@link OrchestratingDriver.getHandOffStack}.
8055
+ */
8056
+ private handOffStack;
8057
+ /** Hand-offs and hand-backs this user turn, against `maxHandoffs`. */
8058
+ private handOffsThisTurn;
8059
+ /**
8060
+ * A hand-back a restore found due — the top frame's target had finished, or has gone — which
8061
+ * {@link OrchestratingDriver.resumeRestoredFlow} carries out.
8062
+ */
8063
+ private pendingHandBack?;
7791
8064
  /**
7792
8065
  * The active agent, or `undefined` before the first activation and after a release.
7793
8066
  *
@@ -7798,6 +8071,8 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7798
8071
  constructor(providerRegistry: AIProviderRegistry, agents: AgentConfig[], options?: {
7799
8072
  sessionKey?: string;
7800
8073
  maxHandoffs?: number;
8074
+ /** How many callers may wait in hand-offs at once. Default 1. */
8075
+ maxHandOffDepth?: number;
7801
8076
  classifierHistoryLength?: number;
7802
8077
  classifierRetries?: number;
7803
8078
  maxToolIterations?: number;
@@ -7818,6 +8093,33 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7818
8093
  getSuggestions(history: ChatMessage[], prompt: string, count: number, allAgentInfo?: AllAgentSummary[]): Promise<string[]>;
7819
8094
  sendMessage(input: string, attachments?: ChatAttachment[], options?: SendMessageOptions): Promise<ChatDriverResult>;
7820
8095
  private runOrchestratedTurn;
8096
+ /**
8097
+ * Act on what the agent call that just ended asked for, and run the next agent in the same user
8098
+ * turn while there is one (GENC-1737): a `handOff` runs its target with the brief, and a target
8099
+ * that releases hands back to its caller. Returns the last call's result.
8100
+ *
8101
+ * A failed call ends the turn there, as a Stop does: what it asked for still happens, and the
8102
+ * next agent runs on the next message. Running it now would meet the same wall (a budget, a
8103
+ * refused account, a full context window), or build on a reply that never came.
8104
+ */
8105
+ private carryOn;
8106
+ /**
8107
+ * After one agent call: start the hand-off it asked for, or release it and, when it was a
8108
+ * hand-off's target, hand back. Returns the tail note for the agent to run next, or `undefined`
8109
+ * when the turn ends here.
8110
+ *
8111
+ * A plain target hands back when its call ends, released or not, as a plain agent is done once
8112
+ * it has answered. A stateful one holds the hand-off until it releases, as it holds its flow.
8113
+ */
8114
+ private afterAgentCall;
8115
+ /** How the target of `frame` finished: with `completeHandOff`, or a release and its last reply. */
8116
+ private outcomeOf;
8117
+ /**
8118
+ * A plain agent is done once it has answered (GENC-1737): the data a one-way hand-off gave it ends
8119
+ * with its turn, and so does a flow lock no hand-off holds, such as one a restore left on a
8120
+ * target whose hand-off it could not resume.
8121
+ */
8122
+ private endPlainTurn;
7821
8123
  /** {@inheritDoc AiDriver.canRestoreFlowOwner} */
7822
8124
  canRestoreFlowOwner(agentName: string): boolean;
7823
8125
  /** {@inheritDoc AiDriver.canResumeRestoredFlow} */
@@ -7835,25 +8137,31 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7835
8137
  * undecorated.
7836
8138
  */
7837
8139
  resumeRestoredFlow(resumeNote?: string): Promise<ChatDriverResult | undefined>;
8140
+ /**
8141
+ * The hand-back a restore found due (GENC-1737). The first call, inside the host's restore gate,
8142
+ * makes the caller active again and tells it the outcome; the call with the note runs its turn.
8143
+ */
8144
+ private resumeHandBack;
7838
8145
  /**
7839
8146
  * A continuation outside a routed turn — a decorator carrying the agent on, say — can finish the
7840
8147
  * flow as surely as a turn can, so a release it requests is acted on here too. Before
7841
8148
  * (GENC-1660), only `sendMessage` checked, and a flow finished by a continuation stayed locked.
7842
8149
  */
7843
8150
  continueFromHistory(transientPrimer?: ChatMessage[], options?: ContinueFromHistoryOptions): Promise<ChatDriverResult>;
7844
- /** Release the flow if the turn that just ended asked to, and an agent is still active to release. */
7845
- private releaseIfRequested;
7846
8151
  /**
7847
8152
  * {@inheritDoc AiDriver.getContextReserve}
7848
8153
  *
7849
8154
  * Two cases, because routing has two modes and they need opposite answers.
7850
8155
  */
7851
8156
  getContextReserve(): ContextReserveResolution | undefined;
8157
+ /** Make `agent` active through the session, firing its hooks, and install it. */
8158
+ private installAgent;
7852
8159
  /**
7853
- * `pin` stands in for the user-pin when given, as for
7854
- * {@link OrchestratingDriver.resolvePinnedAgent}.
8160
+ * Install an agent the session has already made active on the inner driver: the transcript's
8161
+ * divider when another agent spoke before it, and its own view of the history. What is left here
8162
+ * is this driver's own job, the transcript; the session owns the lifecycle.
7855
8163
  */
7856
- private installAgent;
8164
+ private applyAgent;
7857
8165
  /**
7858
8166
  * Release the current stateful agent: fire `onDeactivate` and clear the flow-owner lock. The
7859
8167
  * inner driver raises the events from the session's transition, so the host (and Redux) reflect
@@ -7866,10 +8174,66 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7866
8174
  * routing through the classifier.
7867
8175
  */
7868
8176
  private releaseActiveAgent;
8177
+ /** The agent named `name` as this driver installs it: the fallback with its prompt built. */
8178
+ private agentNamed;
8179
+ /**
8180
+ * End the hand-offs when a turn runs an agent other than the top target (GENC-1737): the lock
8181
+ * that kept turns on the target was taken away, by a host clearing `flowOwnerAgentName` say,
8182
+ * and its caller would otherwise wait for good.
8183
+ */
8184
+ private endOrphanedHandOffs;
8185
+ /**
8186
+ * Check a handler's `handOff` when it is made (GENC-1737), so a refusal reaches its model. Checked
8187
+ * against the agents and the stack as they are now; the hand-off itself starts after the call.
8188
+ */
8189
+ private checkHandOff;
8190
+ /**
8191
+ * Start the hand-off the agent call just made, and return the target's tail note (GENC-1737).
8192
+ *
8193
+ * - `returnTo` (the default): push a frame. The caller waits without being deactivated, its
8194
+ * `agentEnd` payloads suspended, and the target takes the flow lock.
8195
+ * - One-way from a hand-off's target: that frame's caller now waits for the new target instead.
8196
+ * - One-way with nobody waiting: a plain switch, the caller deactivated as on any swap.
8197
+ *
8198
+ * Either way the brief is recorded as a message the caller sent on the user's behalf.
8199
+ */
8200
+ private startHandOff;
8201
+ /**
8202
+ * Hand back to `frame`'s caller with `outcome`, and return its tail note (GENC-1737). The outcome
8203
+ * is kept on the frame until the caller is active again and its `onHandOffReturn` has run, so a
8204
+ * reload in between still hands back; the frame then goes, before the caller's model call.
8205
+ */
8206
+ private handBack;
8207
+ /** The top frame, when `agent` is its target. */
8208
+ private topFrameFor;
8209
+ /** The target's last reply since its brief, for a target that released without a summary. */
8210
+ private lastReply;
8211
+ /** The brief, in the transcript as a message the caller sent on the user's behalf. */
8212
+ private recordDelegated;
8213
+ /** End `frame` with `kind` and no hand-back, for the host. */
8214
+ private unwound;
8215
+ /** {@inheritDoc AiDriver.getHandOffStack} */
8216
+ getHandOffStack(): HandOffFrame[];
8217
+ /** {@inheritDoc AiDriver.restoreHandOffStack} */
8218
+ restoreHandOffStack(saved: unknown, savedStates: Record<string, unknown>, interrupted: boolean): {
8219
+ resume?: {
8220
+ agent: string;
8221
+ note: string;
8222
+ };
8223
+ droppedLocks?: string[];
8224
+ };
8225
+ /** {@inheritDoc AiDriver.unwindHandOffs} */
8226
+ unwindHandOffs(kind: 'cleared' | 'state-lost'): Promise<void>;
8227
+ /** {@inheritDoc AiDriver.getHandOffData} */
8228
+ getHandOffData(): Record<string, unknown>;
8229
+ /** {@inheritDoc AiDriver.restoreHandOffData} */
8230
+ restoreHandOffData(saved: unknown, interrupted: boolean): void;
8231
+ /** {@inheritDoc AiDriver.serializeWaitingCallers} */
8232
+ serializeWaitingCallers(): Record<string, unknown>;
7869
8233
  /**
7870
8234
  * Stop the current turn (user "stop" button). Cancels the inner chat driver's
7871
- * in-flight request and breaks the handoff loop so no further agent turn or
7872
- * classify runs. The driver stays usable for the next message.
8235
+ * in-flight request, and no further agent runs this turn, not even a hand-off's
8236
+ * target or a hand-back. The driver stays usable for the next message.
7873
8237
  */
7874
8238
  cancel(): void;
7875
8239
  /**
@@ -7911,7 +8275,7 @@ export declare class OrchestratingDriver extends BaseDriverDecorator<ChatDriver>
7911
8275
  */
7912
8276
  private resolvePinnedAgent;
7913
8277
  /**
7914
- * Append prose the ORCHESTRATOR wrote — the handoff cap, the no-specialist apology. Flagged
8278
+ * Append prose the ORCHESTRATOR wrote — the no-specialist apology. Flagged
7915
8279
  * `excludeFromProvider` for the same reason the driver's own notices are: it is not something a
7916
8280
  * model said, and feeding it back invites the model to echo it. See
7917
8281
  * `ChatMessage.excludeFromProvider`.
@@ -8076,6 +8440,33 @@ export declare interface PersistedSession {
8076
8440
  componentName: string;
8077
8441
  value: unknown;
8078
8442
  };
8443
+ /**
8444
+ * The hand-offs in progress, oldest first (GENC-1737): who waits for whom, and why. A frame with
8445
+ * an `outcome` was finished by its target but its caller had not resumed yet, so a restore hands
8446
+ * back. Each agent's own state is in `agentSnapshots`, the waiting callers' included. Optional
8447
+ * and additive, like `condensePolicies`; omitted when there are none. The frame's shape, written
8448
+ * out here as for `checkpoints`.
8449
+ */
8450
+ handoffStack?: Array<{
8451
+ id: string;
8452
+ caller: string;
8453
+ target: string;
8454
+ returnTo: boolean;
8455
+ message: string;
8456
+ data?: unknown;
8457
+ startedAt: string;
8458
+ outcome?: {
8459
+ kind: string;
8460
+ message?: string;
8461
+ data?: unknown;
8462
+ };
8463
+ }>;
8464
+ /**
8465
+ * The `data` each hand-off target was activated with, by agent name (GENC-1737), for the agents
8466
+ * a restore resumes: the flow owner and the waiting callers. Not written for a frozen session.
8467
+ * Optional and additive, like `handoffStack`; omitted when there is none.
8468
+ */
8469
+ handOffData?: Record<string, unknown>;
8079
8470
  /**
8080
8471
  * Interrupted-flow marker (GENC-1351 §5.9). Present when the session was saved
8081
8472
  * while an agent declared its current state non-resumable (`resumable === false`).
@@ -8163,13 +8554,6 @@ export declare function registerInteraction<TController = unknown>(name: string,
8163
8554
  */
8164
8555
  export declare function registerTieredAIProviders(container: Container, options: Omit<TieredAIProviderSwitcherOptions, 'container'>): TieredAIProviderSwitcher;
8165
8556
 
8166
- /**
8167
- * Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver.
8168
- *
8169
- * @beta
8170
- */
8171
- export declare const REQUEST_CONTINUATION_TOOL = "request_continuation";
8172
-
8173
8557
  /**
8174
8558
  * Ask the person for permission to show notifications, and resolve to the result. Call it from a
8175
8559
  * click or key handler: browsers ignore or refuse a request that does not come from the person.
@@ -8336,13 +8720,6 @@ export declare type RoutingDecision =
8336
8720
  'classified'
8337
8721
  /** The classifier ran and answered -1: nothing listed covers the request. */
8338
8722
  | 'no-match'
8339
- /**
8340
- * Every classifier-eligible specialist had already handled part of this turn and handed on, so
8341
- * the ballot was empty and no call was made. Distinct from `'no-match'`, which means a
8342
- * classifier RAN and declined — conflating the two re-creates exactly the "did anything actually
8343
- * get asked?" ambiguity this event exists to remove.
8344
- */
8345
- | 'ballot-exhausted'
8346
8723
  /** One viable specialist and no fallback — no point asking. */
8347
8724
  | 'single-candidate'
8348
8725
  /** Nothing classifier-eligible was configured. */
@@ -9684,7 +10061,7 @@ export declare type UnresolvedToolInput = (ctx: UnresolvedToolContext) => string
9684
10061
  * A fallback prompt written by a consumer reliably covers two cases — small talk, and a request
9685
10062
  * that belongs to a specialist — because those are the ones you think of when you write it. The
9686
10063
  * case it exists FOR is the third: a substantive request that no specialist covers, and no way to
9687
- * act on it, because a fallback holds no tools and is denied the continuation tool as a leaf.
10064
+ * act on it, because a fallback holds no tools.
9688
10065
  *
9689
10066
  * Create's fallback prompt is the worked example (GENC-1573). It covered chit-chat and
9690
10067
  * "out of scope, here are the specialists", said nothing about a request nothing can serve, and