@genesislcap/ai-assistant 15.58.2 → 15.59.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 (58) hide show
  1. package/dist/ai-assistant.api.json +10096 -6986
  2. package/dist/ai-assistant.d.ts +545 -101
  3. package/dist/chat-driver.cjs +624 -207
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +624 -207
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +1836 -378
  8. package/dist/dts/components/agent-session/agent-session.d.ts +19 -3
  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 +120 -10
  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 +103 -0
  13. package/dist/dts/components/ai-driver/base-driver-decorator.d.ts.map +1 -0
  14. package/dist/dts/components/ai-driver/index.d.ts +3 -1
  15. package/dist/dts/components/ai-driver/index.d.ts.map +1 -1
  16. package/dist/dts/components/chat-driver/chat-driver.d.ts +84 -8
  17. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  18. package/dist/dts/components/chat-driver/interaction-controller.d.ts +37 -3
  19. package/dist/dts/components/chat-driver/interaction-controller.d.ts.map +1 -1
  20. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +30 -93
  21. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  22. package/dist/dts/index.d.ts +4 -1
  23. package/dist/dts/index.d.ts.map +1 -1
  24. package/dist/dts/main/main.d.ts +4 -4
  25. package/dist/dts/main/main.d.ts.map +1 -1
  26. package/dist/dts/state/debug-event-log.d.ts +1 -1
  27. package/dist/dts/state/debug-event-log.d.ts.map +1 -1
  28. package/dist/dts/state/interaction-context.d.ts +7 -1
  29. package/dist/dts/state/interaction-context.d.ts.map +1 -1
  30. package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
  31. package/dist/dts/suggestions/generate-suggestions.d.ts.map +1 -1
  32. package/dist/dts/types/handler-capability.d.ts +28 -0
  33. package/dist/dts/types/handler-capability.d.ts.map +1 -0
  34. package/dist/dts/types/interaction-context.d.ts +16 -0
  35. package/dist/dts/types/interaction-context.d.ts.map +1 -1
  36. package/dist/dts/types/interaction-interceptor.d.ts +109 -0
  37. package/dist/dts/types/interaction-interceptor.d.ts.map +1 -0
  38. package/dist/dts/utils/delegated-message.d.ts +52 -0
  39. package/dist/dts/utils/delegated-message.d.ts.map +1 -0
  40. package/dist/dts/utils/history-transform.d.ts.map +1 -1
  41. package/dist/esm/components/agent-session/agent-session.js +17 -0
  42. package/dist/esm/components/ai-driver/ai-driver.js +21 -1
  43. package/dist/esm/components/ai-driver/base-driver-decorator.js +211 -0
  44. package/dist/esm/components/ai-driver/index.js +2 -1
  45. package/dist/esm/components/chat-driver/chat-driver.js +195 -14
  46. package/dist/esm/components/chat-driver/interaction-controller.js +168 -14
  47. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +72 -196
  48. package/dist/esm/index.js +1 -0
  49. package/dist/esm/main/main.js +79 -77
  50. package/dist/esm/state/debug-event-log.js +7 -2
  51. package/dist/esm/state/interaction-context.js +11 -0
  52. package/dist/esm/state/persistence/session-persister.js +5 -6
  53. package/dist/esm/suggestions/generate-suggestions.js +8 -1
  54. package/dist/esm/types/handler-capability.js +1 -0
  55. package/dist/esm/types/interaction-interceptor.js +1 -0
  56. package/dist/esm/utils/delegated-message.js +25 -0
  57. package/dist/esm/utils/history-transform.js +12 -1
  58. package/package.json +17 -17
@@ -894,6 +894,54 @@
894
894
  "package": "./utils/tool-fold"
895
895
  }
896
896
  },
897
+ {
898
+ "kind": "js",
899
+ "name": "DEFAULT_DELEGATE_FRAMING",
900
+ "declaration": {
901
+ "name": "DEFAULT_DELEGATE_FRAMING",
902
+ "module": "./utils/delegated-message"
903
+ }
904
+ },
905
+ {
906
+ "kind": "js",
907
+ "name": "isDelegated",
908
+ "declaration": {
909
+ "name": "isDelegated",
910
+ "module": "./utils/delegated-message"
911
+ }
912
+ },
913
+ {
914
+ "kind": "js",
915
+ "name": "type",
916
+ "declaration": {
917
+ "name": "type",
918
+ "module": "./utils/delegated-message"
919
+ }
920
+ },
921
+ {
922
+ "kind": "js",
923
+ "name": "DelegatedMessage",
924
+ "declaration": {
925
+ "name": "DelegatedMessage",
926
+ "module": "./utils/delegated-message"
927
+ }
928
+ },
929
+ {
930
+ "kind": "js",
931
+ "name": "type",
932
+ "declaration": {
933
+ "name": "type",
934
+ "module": "./utils/delegated-message"
935
+ }
936
+ },
937
+ {
938
+ "kind": "js",
939
+ "name": "SendMessageOptions",
940
+ "declaration": {
941
+ "name": "SendMessageOptions",
942
+ "module": "./utils/delegated-message"
943
+ }
944
+ },
897
945
  {
898
946
  "kind": "js",
899
947
  "name": "assistantSettingsSectionTitleStyles",
@@ -974,6 +1022,62 @@
974
1022
  "module": "./types/interaction-context"
975
1023
  }
976
1024
  },
1025
+ {
1026
+ "kind": "js",
1027
+ "name": "InteractionHolder",
1028
+ "declaration": {
1029
+ "name": "InteractionHolder",
1030
+ "module": "./types/interaction-context"
1031
+ }
1032
+ },
1033
+ {
1034
+ "kind": "js",
1035
+ "name": "DelegatedDecision",
1036
+ "declaration": {
1037
+ "name": "DelegatedDecision",
1038
+ "module": "./types/interaction-interceptor"
1039
+ }
1040
+ },
1041
+ {
1042
+ "kind": "js",
1043
+ "name": "InteractionHold",
1044
+ "declaration": {
1045
+ "name": "InteractionHold",
1046
+ "module": "./types/interaction-interceptor"
1047
+ }
1048
+ },
1049
+ {
1050
+ "kind": "js",
1051
+ "name": "InteractionInterceptor",
1052
+ "declaration": {
1053
+ "name": "InteractionInterceptor",
1054
+ "module": "./types/interaction-interceptor"
1055
+ }
1056
+ },
1057
+ {
1058
+ "kind": "js",
1059
+ "name": "InteractionOpened",
1060
+ "declaration": {
1061
+ "name": "InteractionOpened",
1062
+ "module": "./types/interaction-interceptor"
1063
+ }
1064
+ },
1065
+ {
1066
+ "kind": "js",
1067
+ "name": "HandlerCapabilityFactory",
1068
+ "declaration": {
1069
+ "name": "HandlerCapabilityFactory",
1070
+ "module": "./types/handler-capability"
1071
+ }
1072
+ },
1073
+ {
1074
+ "kind": "js",
1075
+ "name": "HandlerCapabilityScope",
1076
+ "declaration": {
1077
+ "name": "HandlerCapabilityScope",
1078
+ "module": "./types/handler-capability"
1079
+ }
1080
+ },
977
1081
  {
978
1082
  "kind": "js",
979
1083
  "name": "AiChatMarkdown",
@@ -4594,12 +4698,12 @@
4594
4698
  },
4595
4699
  {
4596
4700
  "kind": "field",
4597
- "name": "orchestrator",
4701
+ "name": "driverKind",
4598
4702
  "type": {
4599
- "text": "OrchestratingDriver | undefined"
4703
+ "text": "'orchestrating' | 'chat'"
4600
4704
  },
4601
4705
  "privacy": "private",
4602
- "description": "This element's driver as an orchestrator, or `undefined` when the session is running a bare\n`ChatDriver`. The only place in this element that narrows the type; the test itself lives in\n`asOrchestrator`, shared with the session persister.",
4706
+ "description": "Which kind of driver this element runs, for the `driver.created` / `assistant.connected` log\nlines only. Looks through any decorators (GENC-1660): behaviour goes through `AiDriver`\nmembers, which every layer forwards, so this is the one place the concrete class matters.",
4603
4707
  "readonly": true
4604
4708
  },
4605
4709
  {
@@ -7136,7 +7240,7 @@
7136
7240
  "type": {
7137
7241
  "text": "Record<MetaEventType, MetaEventImportance>"
7138
7242
  },
7139
- "default": "{\n 'turn.error': 'high',\n 'tool.failed': 'high',\n 'subagent.failed': 'high',\n 'file.read-failed': 'high',\n 'suggestions.failed': 'high',\n 'context.threshold-crossed': 'high',\n 'context.compacted': 'high',\n 'persistence.restore-orphan': 'high',\n // A save that did not land is silent data loss in progress: the transcript on screen\n // is no longer backed by the store, and every later turn widens the gap. GENC-1511.\n 'persistence.save-failed': 'high',\n\n 'assistant.connected': 'normal',\n 'assistant.disconnected': 'normal',\n 'assistant.popout': 'normal',\n 'assistant.popin': 'normal',\n 'driver.created': 'normal',\n 'state.changed': 'normal',\n 'turn.start': 'normal',\n 'turn.end': 'normal',\n 'turn.retry': 'normal',\n 'tool.unresolved': 'normal',\n 'subagent.started': 'normal',\n 'subagent.completed': 'normal',\n // One per routing decision, with what the classifier answered and what the call cost. Same\n // level as the handoff it produces: you read them together to follow how a turn was routed.\n 'routing.decided': 'normal',\n 'agent.handoff': 'normal',\n 'agent.pinned': 'normal',\n 'agent.unpinned': 'normal',\n 'provider.selected': 'normal',\n 'interaction.requested': 'normal',\n 'interaction.resolved': 'normal',\n // Bookkeeping about an already-finished turn; interesting when reconciling a session total.\n 'interaction.external-cost': 'normal',\n // Spend that was billed but could NOT be recorded (the message was trimmed/condensed away, or\n // the value was not real spend). 'high': it is the one way the session total goes quietly\n // wrong, so it must stand out when reconciling.\n 'interaction.external-cost-dropped': 'high',\n // What came of an answer, recorded by the tool that asked (see recordInteractionOutcome).\n 'interaction.outcome': 'normal',\n // An outcome that could not be recorded: the widget goes on showing only the answer.\n 'interaction.outcome-dropped': 'high',\n 'external-diagnostics.folded': 'normal',\n 'session.cleared': 'normal',\n 'session.switched': 'low',\n 'session.switch-deferred': 'low',\n 'persistence.restored': 'normal',\n // Whether a restored flow will be re-activated and resumed (GENC-1662), and how much the trim cut.\n 'persistence.resume-planned': 'normal',\n // A planned resume that did not run, and why (GENC-1662) — the divider was put back to plain.\n 'persistence.resume-withdrawn': 'normal',\n 'persistence.suppressed': 'normal',\n 'persistence.not-resumable': 'normal',\n\n 'driver.wired': 'low',\n 'driver.unwired': 'low',\n 'context.updated': 'low',\n 'context.condensed': 'low',\n 'panel.toggled': 'low',\n 'persistence.toggled': 'low',\n 'persistence.saved': 'low',\n 'attachment.added': 'low',\n}",
7243
+ "default": "{\n 'turn.error': 'high',\n 'tool.failed': 'high',\n 'subagent.failed': 'high',\n 'file.read-failed': 'high',\n 'suggestions.failed': 'high',\n 'context.threshold-crossed': 'high',\n 'context.compacted': 'high',\n 'persistence.restore-orphan': 'high',\n // A save that did not land is silent data loss in progress: the transcript on screen\n // is no longer backed by the store, and every later turn widens the gap. GENC-1511.\n 'persistence.save-failed': 'high',\n\n 'assistant.connected': 'normal',\n 'assistant.disconnected': 'normal',\n 'assistant.popout': 'normal',\n 'assistant.popin': 'normal',\n 'driver.created': 'normal',\n 'state.changed': 'normal',\n 'turn.start': 'normal',\n 'turn.end': 'normal',\n 'turn.retry': 'normal',\n 'tool.unresolved': 'normal',\n 'subagent.started': 'normal',\n 'subagent.completed': 'normal',\n // One per routing decision, with what the classifier answered and what the call cost. Same\n // level as the handoff it produces: you read them together to follow how a turn was routed.\n 'routing.decided': 'normal',\n 'agent.handoff': 'normal',\n 'agent.pinned': 'normal',\n 'agent.unpinned': 'normal',\n 'provider.selected': 'normal',\n 'interaction.requested': 'normal',\n 'interaction.resolved': 'normal',\n // Bookkeeping about an already-finished turn; interesting when reconciling a session total.\n 'interaction.external-cost': 'normal',\n // Spend that was billed but could NOT be recorded (the message was trimmed/condensed away, or\n // the value was not real spend). 'high': it is the one way the session total goes quietly\n // wrong, so it must stand out when reconciling.\n 'interaction.external-cost-dropped': 'high',\n // What came of an answer, recorded by the tool that asked (see recordInteractionOutcome).\n 'interaction.outcome': 'normal',\n // An outcome that could not be recorded: the widget goes on showing only the answer.\n 'interaction.outcome-dropped': 'high',\n // A claimant handing a pending interaction to the person, or taking it back.\n 'interaction.held': 'normal',\n // A claim on a `userOnly` interaction, refused. Expected when an interceptor claims broadly.\n 'interaction.claim-refused': 'low',\n 'external-diagnostics.folded': 'normal',\n 'session.cleared': 'normal',\n 'session.switched': 'low',\n 'session.switch-deferred': 'low',\n 'persistence.restored': 'normal',\n // Whether a restored flow will be re-activated and resumed (GENC-1662), and how much the trim cut.\n 'persistence.resume-planned': 'normal',\n // A planned resume that did not run, and why (GENC-1662) — the divider was put back to plain.\n 'persistence.resume-withdrawn': 'normal',\n 'persistence.suppressed': 'normal',\n 'persistence.not-resumable': 'normal',\n\n 'driver.wired': 'low',\n 'driver.unwired': 'low',\n 'context.updated': 'low',\n 'context.condensed': 'low',\n 'panel.toggled': 'low',\n 'persistence.toggled': 'low',\n 'persistence.saved': 'low',\n 'attachment.added': 'low',\n}",
7140
7244
  "description": "Importance is intrinsic to the event *type*, not the instance, so it's\ndefined once here and stamped automatically by recordMetaEvent — call\nsites never pass it. The `Record` is exhaustive: adding a `MetaEventType`\nwithout a level here is a compile error.\n\n- `high` — failures and hard limits you almost always want to see.\n- `normal` — meaningful session flow you read to follow what happened.\n- `low` — frequent UI / bookkeeping noise, usually safe to skip."
7141
7245
  },
7142
7246
  {
@@ -7298,7 +7402,7 @@
7298
7402
  "type": {
7299
7403
  "text": "readonly string[]"
7300
7404
  },
7301
- "default": "[\n 'This is an exported debug log for the Genesis AI assistant. Read it top-to-bottom.',\n \"`sessionUsage` is what this session spent: `costUsd` (USD, provider-reported per request and summed, cache discounts already applied) plus the four TOKEN BUCKETS — `uncachedInputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `outputTokens`. Those four are disjoint and safe to add up for a total-tokens figure; the per-message fields further down are NOT (see kind:'message'). Each bucket bills at a different rate — cache reads a fraction of uncached input, cache writes a premium, output highest — so a big token count against a small cost means the prompt was mostly cache hits, not a missing charge. It covers sub-agent turns and spend a compaction banked, so for the CONVERSATION it is the authoritative total: prefer it over re-summing the timeline, which can be short by whatever the ring buffers evicted. The same figures appear under `meta.context`. It is as current as the snapshot it came from: the assistant's own download stamps a fresh one, but a log stitched by another tool can carry an older one — compare `meta.timestamp` with the last timeline entry, and if it is well behind, read these totals as historical and fall back to summing the timeline. One scope caveat: it is derived from the transcript, so it excludes billed calls that produced no message (a blank or refused attempt that was retried — see kind:'turn'.`usage`). Summing the turns can therefore come out HIGHER than this, and the difference is exactly that discarded spend, not a double-count.\",\n \"What 'session' means for those figures: the CURRENT conversation under this session key (a host-supplied per-project id, or the element id + header title when the host supplies none) — which is not necessarily everything in `timeline`. It is re-derived from the live transcript, so it spans page loads only when the chat is being remembered, and a Clear resets it to zero. `timeline` is append-only regardless: it keeps every page load and the pre-Clear conversation, with the session.cleared event as the boundary. So on a long-lived log, read these totals as the latest conversation's and the earlier sections as history — not as a total of the whole file.\",\n '`timeline` is the entire session as one array, already sorted chronologically by `timestamp` (ISO 8601). Every entry has a `kind`.',\n 'Timestamps are millisecond-resolution; entries that share the same millisecond are ordered by a fixed kind rank (event, then turn, then message), which is a heuristic and may not reflect exact causal order within that millisecond — e.g. a user message and the turn it triggered, or a final assistant message and its turn.end event, can appear in either order depending on whether they landed in the same millisecond. Read the logical structure of a turn rather than over-interpreting the micro-ordering of co-timestamped entries of different kinds.',\n \"kind:'message' — the conversation. `role` is user/assistant/tool/system-event/synthetic-user; `agentName` says which agent produced it; `toolCalls`/`toolResult`/`interaction` carry tool and widget activity; `inputTokens`/`outputTokens`/`cost` are per-message LLM usage, where `inputTokens` is the WHOLE prompt for that request and `cacheReadTokens`/`cacheWriteTokens` BREAK IT DOWN rather than add to it — uncached input is `inputTokens` minus those two, and adding the cache fields to `inputTokens` counts the prompt twice. The cache fields are absent on providers that report no cache split (Gemini reports reads only, since implicit caching bills no write) and on messages persisted before they existed, so read them as 0 when missing. Each bucket bills at a different rate — cache reads a fraction of uncached input, cache writes a premium, output highest — so a large token count at a small cost means the prompt was mostly cache hits. `externalCostUsd` is any non-LLM cost a widget reported for its own external service calls (folded into the session cost total alongside `cost`). On model-produced assistant messages, `model` is the concrete model id that generated it (e.g. 'gemini-2.5-flash-lite') and `providerName` is the registry slot it resolved under (e.g. a tier name like 'high'/'low', or the default); together they attribute the message — and any tool calls it carries — to an exact model even across a mid-session vendor/tier switch, where one slot name can map to different models before and after the switch. Both are undefined on any entry that is NOT an LLM response: non-assistant roles (user/tool/system-event) and 'synthetic-user' echoes; assistant interaction/widget entries (empty content carrying an `interaction` — a rendered widget, not a model turn); driver-authored assistant fallbacks (the timeout, repeated-malformed-call, and empty-response apology messages); and messages restored from a session persisted before these fields existed. One partial case: on a genuine model turn whose provider exposes no `getStatus` (or reports no model), `providerName` is still set but `model` alone is undefined. A 'synthetic-user' message is a display-only echo of an interaction outcome (e.g. the answer a widget reported): it renders on the user's side of the chat and `agentName` is the agent that created it, but it is never sent to the LLM — so it has no matching 'turn' and the model learns the outcome only from the corresponding tool result.\",\n \"Sub-agent messages appear inline. When a tool delegates to a sub-agent (via `requestSubAgent`), the sub-agent's whole conversation — its own assistant/tool messages, each with their own `content`/`thinking`/`toolCalls`/`toolResult` and per-message `model`/`providerName`/`inputTokens`/`outputTokens`/`cacheReadTokens`/`cacheWriteTokens`/`cost` — is hoisted into the timeline as ordinary kind:'message' entries, interleaved by timestamp right after the tool call that spawned them (so you read the delegation top-to-bottom). A hoisted entry is marked: `subAgentDepth` is its delegation depth (1 for a sub-agent, 2 for a sub-agent's sub-agent, …), `subAgentOf` is the id of the parent tool call that spawned it (correlates it back even when two sub-agents run in one parent turn), `subAgentName` is the sub-agent's own name, and `agentName` is rewritten to a `\\\"<parent> › <sub-agent>\\\"` breadcrumb (composing when nested, e.g. `\\\"UI Builder › Planner › Grounding\\\"`). The sub-agent's per-LLM-call snapshots also surface as kind:'turn' entries with an N-M `turnIndex`, and subagent.started/completed (or subagent.failed) events bracket the run. Per-message `cost` on hoisted entries is already part of the session total (it is summed from the un-flattened history), so summing the top-level timeline does NOT double-count.\",\n \"kind:'turn' — one LLM call. `turnIndex` is a string: a top-level turn is the bare counter ('0', '1', …); a sub-agent's turns are numbered under the parent turn that activated them ('3-1', '3-2', …, and a nested sub-agent contributes '3-2-1', …), and `agentName` names the agent that ran the turn. `systemPrompt` and `toolNames` are what the model saw. A systemPrompt of '<repeated — identical to turn N>' was byte-identical to turn N and de-duplicated; the full prompt is shown whenever it changes (often because a stateful agent advanced), so prompt evolution is visible.\",\n \"kind:'turn'.`model`/`providerName`/`provider` — which model ran that call: the concrete model id, the registry slot it resolved under (a tier name like 'high'/'low', or the default), and the vendor. Recorded per CALL, so an agent whose `provider` selector varies by state — a flow that plans on a high tier and executes on a low one — has every step attributed, including calls that produced no message. `model` is the SERVING model where the provider reports one, so a turn answered by a server-side fallback names the model that answered rather than the one requested; it always matches the `model` on the message that call produced. Absent when the provider exposes no `getStatus` and the transport stamped nothing.\",\n \"kind:'turn'.`usage` — what that ONE call cost, in the same four-bucket + `costUsd` shape as `sessionUsage`. Absent while a call is in flight and on providers that report no usage. This is the SAME money as the message the call produced, not extra money: never add turn usage to message usage, and read a turn plus its message as one charge. Its distinct value is the calls that produced NO message — a blank or refused response is billed and then discarded before the retry, so the turn entry is the only record of that spend, and a turn with `usage` but no message after it is exactly that. `costUsd: 0` alongside a nonzero token count means the provider reported no price, not that the call was free.\",\n \"kind:'turn'.`agentSnapshot` — the active agent's own view of its internal state, captured at that turn. An agent opts into this by exposing a `getDebugSnapshot()` that returns JSON-serializable per-state info; stateful/flow agents wire it automatically, so you can watch a flow advance turn-by-turn (e.g. current step, cursor, collected fields, pending changes). Absent for agents that don't expose one.\",\n \"kind:'event' — a meta/lifecycle event. `type` names it (see below); `detail` carries structured data. `detail.placement` is the emitting UI instance: 'bubble' (collapsed), 'panel' (popped-out), or 'standalone'.\",\n \"Each 'event' also has an `importance`: 'high' (failures/limits — turn.error, tool.failed, subagent.failed, file.read-failed, suggestions.failed, context.threshold-crossed), 'normal' (session flow — connects, turns, retries, handoffs, agent/provider changes, interactions, sub-agent start/complete), or 'low' (skippable UI/bookkeeping noise — panel.toggled, attachment.added, driver.wired/unwired, context.updated, context.condensed). To skim, ignore importance:'low'; to triage a failure, filter to importance:'high' then read the nearby messages and turns. A 'high' turn.error is often preceded by one or more 'normal' turn.retry events for the same reason — read them together to see how many attempts were made before bailing. 'message' and 'turn' entries carry no importance — they are the substance, always read them.\",\n 'Event types: assistant.connected/disconnected (mount + placement + whether the session was created or restored), assistant.popout/popin (window placement), driver.created/wired/unwired (which driver is live and why it stops/starts responding across a popout), state.changed (idle↔loading), turn.start/turn.end (turn boundary; turn.end carries durationMs), turn.retry (a recoverable in-turn retry — detail.reason plus attempt/maxAttempts; for malformed calls also finishMessage; for empty responses also the provider finishReason + thoughtsTokens + parts breakdown), turn.error (a turn failed or hit a guardrail — detail.reason is one of exception/malformed-function-call/empty-response/unknown-tool-limit/max-iterations/response-truncated/refusal/budget-exhausted/provider-refused/context-exhausted/ai-service-error, plus reason-specific diagnostics: attempts (for empty-response also finishReason + thoughtsTokens + a parts breakdown, distinguishing a thinking-only STOP from a truly empty turn), finishMessage, for response-truncated the model + maxTokens + outputTokens + tools, unknownTools (split into staleTools — real earlier this activation but retired by the current state or hidden behind an open exclusive fold — and hallucinatedTools — never advertised) + availableTools, iterations + limit, for budget-exhausted the budgetUsd + spentUsd figures reported by the proxy plus the resolved vendor, for provider-refused the kind (spend|auth) plus the upstream status and error type — the vendor refusing the ACCOUNT rather than us refusing to spend, so the remedy is a top-up or a key rotation rather than a raised cap, and the kind is the only place that distinction survives because the user-facing copy is deliberately cause-free, for context-exhausted the estimated contextTokens against the guard that stopped the turn plus the iterations reached — the driver ending a runaway tool loop at the edge of the context window rather than issuing a request the provider would reject, so the transcript survives intact and can still be compacted, for ai-service-error the code (one of AI_SERVICE_ERROR_CODES, e.g. NO_API_KEY) and the status the AI service answered with — its own text goes to the browser console only, or name + message for exceptions), tool.failed (a tool threw), tool.unresolved (the model called a tool that could not be dispatched — detail.kind is folded/fold-hidden/stale/unknown, plus tool + agent and, for the counted kinds, the consecutive streak; the recurring lead-up to an unknown-tool-limit turn.error), subagent.started/completed/failed (the lifecycle of a `requestSubAgent` delegation — detail.agent names the sub-agent; these bracket the sub-agent turns that appear as kind:turn entries with an N-M `turnIndex`; subagent.failed also carries detail.reason, one of max_iterations/malformed_tool_call/empty_response/unknown_tool_limit/timeout/response_truncated/refusal/budget_exhausted/provider_refused/context_exhausted/ai_service_error; budget_exhausted and provider_refused are terminal for the PARENT turn too, and so is ai_service_error when the refusal is about the service rather than the request and came from the vendor the parent calls — the parent stops rather than calling the model again into the same wall; after context_exhausted the parent goes on, but its own next call is likely to stop on the same guard), agent.handoff (routing; from=null is the initial activation), agent.pinned/unpinned (forced routing), routing.decided (ONE PER ROUTING DECISION — see its own paragraph below), provider.selected (the resolved provider for the upcoming turns — detail.provider is the registry SLOT/tier name, detail.model the concrete model behind it and detail.vendor its vendor; emitted only when the slot CHANGES, so read the per-turn `model` for the model of any given call rather than assuming the nearest event still applies), interaction.requested/resolved (blocking user widgets — explain quiet gaps; note that when a sub-agent opens a widget, detail.agent — and the agentName on the interaction message — is the HOST agent that owns the widget, NOT the sub-agent that asked, because widgets render and resolve on the host driver), context.updated/threshold-crossed (token + cost), context.condensed (a stale tool payload was collapsed out of the model-bound history by a `condenseWhen` declaration on the tool — detail.tool + toolCallId, target args|response, trigger (superseded:<key> or age:<n>), stubLen, and an estimated tokensSaved; stored history and this log keep the FULL payload, so the model-visible slice at any point is the full history minus the condensations recorded up to then), panel.toggled, attachment.added, file.read-failed, suggestions.failed.',\n 'Sub-agent meta events: a sub-agent\\'s own turn.retry/turn.error/tool.failed/tool.unresolved events are merged into this same timeline, tagged with `detail.subAgent` — a `\"<parent> › <sub-agent>\"` breadcrumb that composes when nested (e.g. `\"UI Builder › Planner › Grounding\"`) — and interleaved by their original timestamps within the subagent.started→completed/failed bracket. These are the per-attempt/per-failure signals that do NOT appear among the sub-agent\\'s (hoisted) messages: a malformed/empty attempt that gets retried produces no message, and the stale-vs-hallucinated split and streak counts live only on the event. A sub-agent\\'s high-volume, message-derivable events (turn.start/turn.end, provider.selected, context.updated) are intentionally NOT merged — read its hoisted messages for model/tokens/cost and turn-by-turn activity, and the bracketing subagent.* events for the run\\'s span.',\n \"`meta` holds context captured at export time: agentSummary (full agent configs), context (active model, contextTokens/contextLimit/contextUsagePercent for the last call, and the session totals — sessionCostUsd, sessionTokensConsumed, and the four-bucket sessionUsage lifted to the top of this log), activeDebugSnapshot (the active agent's `getDebugSnapshot()` taken fresh at export — reflects state NOW, which may have advanced beyond the last turn's agentSnapshot), debug (optional host-supplied debug state), host, and the export timestamp.\",\n 'Note the two different scopes in `meta.context`: `contextTokens` is the prompt size of the LAST call (against `contextLimit`, the model context window), while `sessionUsage`/`sessionTokensConsumed` are cumulative BILLED throughput. Every turn resends the conversation, so the cumulative figure counts each turn’s prompt again in the next turn’s and is expected to dwarf the context size — that is not double-counting.',\n \"kind:'event' type 'routing.decided' — how ONE turn was routed, emitted by the orchestrating driver before the agent runs (and again after every handoff). `decision` is why that agent was chosen: 'classified' (the classifier ran and picked one), 'no-match' (it ran and answered -1 — nothing listed covers the request), 'ballot-exhausted' (every eligible specialist had already handed off this turn, so the ballot was empty and NO call was made — distinct from 'no-match', where one ran and declined), 'pinned' (a user pin or an active stateful flow owned the turn, so no call was made), 'single-candidate'/'no-specialists' (configuration left nothing to decide), 'provider-unavailable' (the registry default exposes no chat()), 'classifier-failed' (every attempt threw — `error` carries the last one). `to` names the agent and `fallback` says whether that agent is the fallback. `candidates` is what was actually ON THE BALLOT, in ballot order, so `index` — the classifier's own answer — indexes it directly; `excluded` names the specialists left off because they already handed off this turn, so the full eligible set is `candidates` ∪ `excluded`. `candidates` is absent on the four decisions that put nothing to a vote — 'pinned', 'single-candidate', 'no-specialists', 'ballot-exhausted'. It IS present on 'provider-unavailable', which is not an exception worth removing: the ballot was built there, it simply never got sent. `attempts` covers the retry ladder. `durationMs` is on EVERY routing.decided, and is the field to read when a decision looks wrong: the four decisions that can reach the fallback are told apart by the clock as much as by their name, because a fallback routing that resolved in tens of milliseconds made no network call at all whatever its `decision` says.\",\n \"kind:'event' type 'routing.decided'.`usage` — what that classifier call cost, in the same four-bucket + costUsd shape as `sessionUsage`, plus `model`. Absent on the decisions that make no call (see 'pinned' and the configuration ones). THIS SPEND IS NOT IN `sessionUsage`: a classifier call never enters the transcript and `sessionUsage` is derived from the transcript, so classifier cost is missing from the session total by construction, not by accident. Sum these events to close that gap — it is the one component of a session's real spend that the totals cannot see.\",\n 'To debug a failure: find the last turn.error or tool.failed, then read upward for the user message, the turn(s), and the agent/provider/state events that led into it. For a turn that went to the wrong agent, read its routing.decided first — `decision` says whether anything was actually asked.',\n]",
7405
+ "default": "[\n 'This is an exported debug log for the Genesis AI assistant. Read it top-to-bottom.',\n \"`sessionUsage` is what this session spent: `costUsd` (USD, provider-reported per request and summed, cache discounts already applied) plus the four TOKEN BUCKETS — `uncachedInputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `outputTokens`. Those four are disjoint and safe to add up for a total-tokens figure; the per-message fields further down are NOT (see kind:'message'). Each bucket bills at a different rate — cache reads a fraction of uncached input, cache writes a premium, output highest — so a big token count against a small cost means the prompt was mostly cache hits, not a missing charge. It covers sub-agent turns and spend a compaction banked, so for the CONVERSATION it is the authoritative total: prefer it over re-summing the timeline, which can be short by whatever the ring buffers evicted. The same figures appear under `meta.context`. It is as current as the snapshot it came from: the assistant's own download stamps a fresh one, but a log stitched by another tool can carry an older one — compare `meta.timestamp` with the last timeline entry, and if it is well behind, read these totals as historical and fall back to summing the timeline. One scope caveat: it is derived from the transcript, so it excludes billed calls that produced no message (a blank or refused attempt that was retried — see kind:'turn'.`usage`). Summing the turns can therefore come out HIGHER than this, and the difference is exactly that discarded spend, not a double-count.\",\n \"What 'session' means for those figures: the CURRENT conversation under this session key (a host-supplied per-project id, or the element id + header title when the host supplies none) — which is not necessarily everything in `timeline`. It is re-derived from the live transcript, so it spans page loads only when the chat is being remembered, and a Clear resets it to zero. `timeline` is append-only regardless: it keeps every page load and the pre-Clear conversation, with the session.cleared event as the boundary. So on a long-lived log, read these totals as the latest conversation's and the earlier sections as history — not as a total of the whole file.\",\n '`timeline` is the entire session as one array, already sorted chronologically by `timestamp` (ISO 8601). Every entry has a `kind`.',\n 'Timestamps are millisecond-resolution; entries that share the same millisecond are ordered by a fixed kind rank (event, then turn, then message), which is a heuristic and may not reflect exact causal order within that millisecond — e.g. a user message and the turn it triggered, or a final assistant message and its turn.end event, can appear in either order depending on whether they landed in the same millisecond. Read the logical structure of a turn rather than over-interpreting the micro-ordering of co-timestamped entries of different kinds.',\n \"kind:'message' — the conversation. `role` is user/assistant/tool/system-event/synthetic-user; `agentName` says which agent produced it; `toolCalls`/`toolResult`/`interaction` carry tool and widget activity; `inputTokens`/`outputTokens`/`cost` are per-message LLM usage, where `inputTokens` is the WHOLE prompt for that request and `cacheReadTokens`/`cacheWriteTokens` BREAK IT DOWN rather than add to it — uncached input is `inputTokens` minus those two, and adding the cache fields to `inputTokens` counts the prompt twice. The cache fields are absent on providers that report no cache split (Gemini reports reads only, since implicit caching bills no write) and on messages persisted before they existed, so read them as 0 when missing. Each bucket bills at a different rate — cache reads a fraction of uncached input, cache writes a premium, output highest — so a large token count at a small cost means the prompt was mostly cache hits. `externalCostUsd` is any non-LLM cost a widget reported for its own external service calls (folded into the session cost total alongside `cost`). On model-produced assistant messages, `model` is the concrete model id that generated it (e.g. 'gemini-2.5-flash-lite') and `providerName` is the registry slot it resolved under (e.g. a tier name like 'high'/'low', or the default); together they attribute the message — and any tool calls it carries — to an exact model even across a mid-session vendor/tier switch, where one slot name can map to different models before and after the switch. Both are undefined on any entry that is NOT an LLM response: non-assistant roles (user/tool/system-event) and 'synthetic-user' echoes; assistant interaction/widget entries (empty content carrying an `interaction` — a rendered widget, not a model turn); driver-authored assistant fallbacks (the timeout, repeated-malformed-call, and empty-response apology messages); and messages restored from a session persisted before these fields existed. One partial case: on a genuine model turn whose provider exposes no `getStatus` (or reports no model), `providerName` is still set but `model` alone is undefined. A 'synthetic-user' message is a display-only echo of an interaction outcome (e.g. the answer a widget reported): it renders on the user's side of the chat and `agentName` is the agent that created it, but it is never sent to the LLM — so it has no matching 'turn' and the model learns the outcome only from the corresponding tool result. An echo of an answer an interceptor (such as an autopilot) gave is NOT synthetic-user: see the delegated-messages paragraph.\",\n \"Delegated messages (GENC-1660): a message with a `delegate` field was written or sent ON THE USER'S BEHALF by a driver decorator, and `delegate` names it (e.g. autopilot). The platform stamps it; nothing else can. It renders as the assistant whatever its role. A delegated `user` message was NOT typed by the user: it is an answer given on their behalf, and the model read it with `delegateFraming` in brackets in front of the content (the stored `content` is the bare text). A delegated `assistant` message is display-only (`excludeFromProvider`): the decorator's narration, or the echo of a widget answer it gave, which takes the place of a synthetic-user echo. To tell what the person decided from what was decided for them, read `interaction.answeredBy` and these fields, never the role alone.\",\n \"Sub-agent messages appear inline. When a tool delegates to a sub-agent (via `requestSubAgent`), the sub-agent's whole conversation — its own assistant/tool messages, each with their own `content`/`thinking`/`toolCalls`/`toolResult` and per-message `model`/`providerName`/`inputTokens`/`outputTokens`/`cacheReadTokens`/`cacheWriteTokens`/`cost` — is hoisted into the timeline as ordinary kind:'message' entries, interleaved by timestamp right after the tool call that spawned them (so you read the delegation top-to-bottom). A hoisted entry is marked: `subAgentDepth` is its delegation depth (1 for a sub-agent, 2 for a sub-agent's sub-agent, …), `subAgentOf` is the id of the parent tool call that spawned it (correlates it back even when two sub-agents run in one parent turn), `subAgentName` is the sub-agent's own name, and `agentName` is rewritten to a `\\\"<parent> › <sub-agent>\\\"` breadcrumb (composing when nested, e.g. `\\\"UI Builder › Planner › Grounding\\\"`). The sub-agent's per-LLM-call snapshots also surface as kind:'turn' entries with an N-M `turnIndex`, and subagent.started/completed (or subagent.failed) events bracket the run. Per-message `cost` on hoisted entries is already part of the session total (it is summed from the un-flattened history), so summing the top-level timeline does NOT double-count.\",\n \"kind:'turn' — one LLM call. `turnIndex` is a string: a top-level turn is the bare counter ('0', '1', …); a sub-agent's turns are numbered under the parent turn that activated them ('3-1', '3-2', …, and a nested sub-agent contributes '3-2-1', …), and `agentName` names the agent that ran the turn. `systemPrompt` and `toolNames` are what the model saw. A systemPrompt of '<repeated — identical to turn N>' was byte-identical to turn N and de-duplicated; the full prompt is shown whenever it changes (often because a stateful agent advanced), so prompt evolution is visible.\",\n \"kind:'turn'.`model`/`providerName`/`provider` — which model ran that call: the concrete model id, the registry slot it resolved under (a tier name like 'high'/'low', or the default), and the vendor. Recorded per CALL, so an agent whose `provider` selector varies by state — a flow that plans on a high tier and executes on a low one — has every step attributed, including calls that produced no message. `model` is the SERVING model where the provider reports one, so a turn answered by a server-side fallback names the model that answered rather than the one requested; it always matches the `model` on the message that call produced. Absent when the provider exposes no `getStatus` and the transport stamped nothing.\",\n \"kind:'turn'.`usage` — what that ONE call cost, in the same four-bucket + `costUsd` shape as `sessionUsage`. Absent while a call is in flight and on providers that report no usage. This is the SAME money as the message the call produced, not extra money: never add turn usage to message usage, and read a turn plus its message as one charge. Its distinct value is the calls that produced NO message — a blank or refused response is billed and then discarded before the retry, so the turn entry is the only record of that spend, and a turn with `usage` but no message after it is exactly that. `costUsd: 0` alongside a nonzero token count means the provider reported no price, not that the call was free.\",\n \"kind:'turn'.`agentSnapshot` — the active agent's own view of its internal state, captured at that turn. An agent opts into this by exposing a `getDebugSnapshot()` that returns JSON-serializable per-state info; stateful/flow agents wire it automatically, so you can watch a flow advance turn-by-turn (e.g. current step, cursor, collected fields, pending changes). Absent for agents that don't expose one.\",\n \"kind:'event' — a meta/lifecycle event. `type` names it (see below); `detail` carries structured data. `detail.placement` is the emitting UI instance: 'bubble' (collapsed), 'panel' (popped-out), or 'standalone'.\",\n \"Each 'event' also has an `importance`: 'high' (failures/limits — turn.error, tool.failed, subagent.failed, file.read-failed, suggestions.failed, context.threshold-crossed), 'normal' (session flow — connects, turns, retries, handoffs, agent/provider changes, interactions, sub-agent start/complete), or 'low' (skippable UI/bookkeeping noise — panel.toggled, attachment.added, driver.wired/unwired, context.updated, context.condensed). To skim, ignore importance:'low'; to triage a failure, filter to importance:'high' then read the nearby messages and turns. A 'high' turn.error is often preceded by one or more 'normal' turn.retry events for the same reason — read them together to see how many attempts were made before bailing. 'message' and 'turn' entries carry no importance — they are the substance, always read them.\",\n 'Event types: assistant.connected/disconnected (mount + placement + whether the session was created or restored), assistant.popout/popin (window placement), driver.created/wired/unwired (which driver is live and why it stops/starts responding across a popout), state.changed (idle↔loading), turn.start/turn.end (turn boundary; turn.end carries durationMs), turn.retry (a recoverable in-turn retry — detail.reason plus attempt/maxAttempts; for malformed calls also finishMessage; for empty responses also the provider finishReason + thoughtsTokens + parts breakdown), turn.error (a turn failed or hit a guardrail — detail.reason is one of exception/malformed-function-call/empty-response/unknown-tool-limit/max-iterations/response-truncated/refusal/budget-exhausted/provider-refused/context-exhausted/ai-service-error, plus reason-specific diagnostics: attempts (for empty-response also finishReason + thoughtsTokens + a parts breakdown, distinguishing a thinking-only STOP from a truly empty turn), finishMessage, for response-truncated the model + maxTokens + outputTokens + tools, unknownTools (split into staleTools — real earlier this activation but retired by the current state or hidden behind an open exclusive fold — and hallucinatedTools — never advertised) + availableTools, iterations + limit, for budget-exhausted the budgetUsd + spentUsd figures reported by the proxy plus the resolved vendor, for provider-refused the kind (spend|auth) plus the upstream status and error type — the vendor refusing the ACCOUNT rather than us refusing to spend, so the remedy is a top-up or a key rotation rather than a raised cap, and the kind is the only place that distinction survives because the user-facing copy is deliberately cause-free, for context-exhausted the estimated contextTokens against the guard that stopped the turn plus the iterations reached — the driver ending a runaway tool loop at the edge of the context window rather than issuing a request the provider would reject, so the transcript survives intact and can still be compacted, for ai-service-error the code (one of AI_SERVICE_ERROR_CODES, e.g. NO_API_KEY) and the status the AI service answered with — its own text goes to the browser console only, or name + message for exceptions), tool.failed (a tool threw), tool.unresolved (the model called a tool that could not be dispatched — detail.kind is folded/fold-hidden/stale/unknown, plus tool + agent and, for the counted kinds, the consecutive streak; the recurring lead-up to an unknown-tool-limit turn.error), subagent.started/completed/failed (the lifecycle of a `requestSubAgent` delegation — detail.agent names the sub-agent; these bracket the sub-agent turns that appear as kind:turn entries with an N-M `turnIndex`; subagent.failed also carries detail.reason, one of max_iterations/malformed_tool_call/empty_response/unknown_tool_limit/timeout/response_truncated/refusal/budget_exhausted/provider_refused/context_exhausted/ai_service_error; budget_exhausted and provider_refused are terminal for the PARENT turn too, and so is ai_service_error when the refusal is about the service rather than the request and came from the vendor the parent calls — the parent stops rather than calling the model again into the same wall; after context_exhausted the parent goes on, but its own next call is likely to stop on the same guard), agent.handoff (routing; from=null is the initial activation), agent.pinned/unpinned (forced routing), routing.decided (ONE PER ROUTING DECISION — see its own paragraph below), provider.selected (the resolved provider for the upcoming turns — detail.provider is the registry SLOT/tier name, detail.model the concrete model behind it and detail.vendor its vendor; emitted only when the slot CHANGES, so read the per-turn `model` for the model of any given call rather than assuming the nearest event still applies), interaction.requested/resolved (blocking user widgets — explain quiet gaps; note that when a sub-agent opens a widget, detail.agent — and the agentName on the interaction message — is the HOST agent that owns the widget, NOT the sub-agent that asked, because widgets render and resolve on the host driver; detail.ownerPath names the agents that actually asked, outermost first, the last being the one whose tool requested it, and detail.heldBy says who held it when it opened: user, or the id of the interceptor that claimed it, such as autopilot; on interaction.resolved, detail.answeredBy is who answered — the claimant id when it settled through its own hold, user for any other answer (a click counts as the person even on a widget a claimant held) — absent when it closed unanswered (a timeout or a stop); the same value is stamped on the interaction message as answeredBy, and when it is not user the tool result that interaction returned carries a decidedOnUsersBehalf record, so the model was told the choice was not made by the person), interaction.held (a claimant moved a pending interaction to the person or back to itself — detail.heldBy is the new holder, detail.claimant the interceptor; a widget held by anything but user was not answered by the person), interaction.claim-refused (a claim on a userOnly interaction, which stays with the person — detail.claimant + reason), context.updated/threshold-crossed (token + cost), context.condensed (a stale tool payload was collapsed out of the model-bound history by a `condenseWhen` declaration on the tool — detail.tool + toolCallId, target args|response, trigger (superseded:<key> or age:<n>), stubLen, and an estimated tokensSaved; stored history and this log keep the FULL payload, so the model-visible slice at any point is the full history minus the condensations recorded up to then), panel.toggled, attachment.added, file.read-failed, suggestions.failed.',\n 'Sub-agent meta events: a sub-agent\\'s own turn.retry/turn.error/tool.failed/tool.unresolved events are merged into this same timeline, tagged with `detail.subAgent` — a `\"<parent> › <sub-agent>\"` breadcrumb that composes when nested (e.g. `\"UI Builder › Planner › Grounding\"`) — and interleaved by their original timestamps within the subagent.started→completed/failed bracket. These are the per-attempt/per-failure signals that do NOT appear among the sub-agent\\'s (hoisted) messages: a malformed/empty attempt that gets retried produces no message, and the stale-vs-hallucinated split and streak counts live only on the event. A sub-agent\\'s high-volume, message-derivable events (turn.start/turn.end, provider.selected, context.updated) are intentionally NOT merged — read its hoisted messages for model/tokens/cost and turn-by-turn activity, and the bracketing subagent.* events for the run\\'s span.',\n \"`meta` holds context captured at export time: agentSummary (full agent configs), context (active model, contextTokens/contextLimit/contextUsagePercent for the last call, and the session totals — sessionCostUsd, sessionTokensConsumed, and the four-bucket sessionUsage lifted to the top of this log), activeDebugSnapshot (the active agent's `getDebugSnapshot()` taken fresh at export — reflects state NOW, which may have advanced beyond the last turn's agentSnapshot), debug (optional host-supplied debug state), host, and the export timestamp.\",\n 'Note the two different scopes in `meta.context`: `contextTokens` is the prompt size of the LAST call (against `contextLimit`, the model context window), while `sessionUsage`/`sessionTokensConsumed` are cumulative BILLED throughput. Every turn resends the conversation, so the cumulative figure counts each turn’s prompt again in the next turn’s and is expected to dwarf the context size — that is not double-counting.',\n \"kind:'event' type 'routing.decided' — how ONE turn was routed, emitted by the orchestrating driver before the agent runs (and again after every handoff). `decision` is why that agent was chosen: 'classified' (the classifier ran and picked one), 'no-match' (it ran and answered -1 — nothing listed covers the request), 'ballot-exhausted' (every eligible specialist had already handed off this turn, so the ballot was empty and NO call was made — distinct from 'no-match', where one ran and declined), 'pinned' (a user pin or an active stateful flow owned the turn, so no call was made), 'single-candidate'/'no-specialists' (configuration left nothing to decide), 'provider-unavailable' (the registry default exposes no chat()), 'classifier-failed' (every attempt threw — `error` carries the last one). `to` names the agent and `fallback` says whether that agent is the fallback. `candidates` is what was actually ON THE BALLOT, in ballot order, so `index` — the classifier's own answer — indexes it directly; `excluded` names the specialists left off because they already handed off this turn, so the full eligible set is `candidates` ∪ `excluded`. `candidates` is absent on the four decisions that put nothing to a vote — 'pinned', 'single-candidate', 'no-specialists', 'ballot-exhausted'. It IS present on 'provider-unavailable', which is not an exception worth removing: the ballot was built there, it simply never got sent. `attempts` covers the retry ladder. `durationMs` is on EVERY routing.decided, and is the field to read when a decision looks wrong: the four decisions that can reach the fallback are told apart by the clock as much as by their name, because a fallback routing that resolved in tens of milliseconds made no network call at all whatever its `decision` says.\",\n \"kind:'event' type 'routing.decided'.`usage` — what that classifier call cost, in the same four-bucket + costUsd shape as `sessionUsage`, plus `model`. Absent on the decisions that make no call (see 'pinned' and the configuration ones). THIS SPEND IS NOT IN `sessionUsage`: a classifier call never enters the transcript and `sessionUsage` is derived from the transcript, so classifier cost is missing from the session total by construction, not by accident. Sum these events to close that gap — it is the one component of a session's real spend that the totals cannot see.\",\n 'To debug a failure: find the last turn.error or tool.failed, then read upward for the user message, the turn(s), and the agent/provider/state events that led into it. For a turn that went to the wrong agent, read its routing.decided first — `decision` says whether anything was actually asked.',\n]",
7302
7406
  "description": "Human/agent-facing guide emitted as the first key of the exported debug log,\nso whoever opens the JSON (often an AI agent) knows how to read it without\nreverse-engineering the shape. Kept here next to the event catalogue it\ndescribes — but proximity alone demonstrably did NOT keep the two in sync\n(`refusal` was missing from both reason lists for a whole release), so\n`debug-event-log.test.ts` now asserts that every `TurnFailureReason` and every\n`SubAgentFailureReason` is named here, and fails the build when one is added\nwithout a matching sentence. Enumerations below read as exhaustive and WILL be\ntrusted by an agent reading the JSON — keep them so."
7303
7407
  }
7304
7408
  ],
@@ -8038,12 +8142,24 @@
8038
8142
  "declarations": [],
8039
8143
  "exports": []
8040
8144
  },
8145
+ {
8146
+ "kind": "javascript-module",
8147
+ "path": "src/types/handler-capability.ts",
8148
+ "declarations": [],
8149
+ "exports": []
8150
+ },
8041
8151
  {
8042
8152
  "kind": "javascript-module",
8043
8153
  "path": "src/types/interaction-context.ts",
8044
8154
  "declarations": [],
8045
8155
  "exports": []
8046
8156
  },
8157
+ {
8158
+ "kind": "javascript-module",
8159
+ "path": "src/types/interaction-interceptor.ts",
8160
+ "declarations": [],
8161
+ "exports": []
8162
+ },
8047
8163
  {
8048
8164
  "kind": "javascript-module",
8049
8165
  "path": "src/utils/agents-key.ts",
@@ -8965,6 +9081,57 @@
8965
9081
  }
8966
9082
  ]
8967
9083
  },
9084
+ {
9085
+ "kind": "javascript-module",
9086
+ "path": "src/utils/delegated-message.ts",
9087
+ "declarations": [
9088
+ {
9089
+ "kind": "function",
9090
+ "name": "isDelegated",
9091
+ "return": {
9092
+ "type": {
9093
+ "text": "message is DelegatedMessage"
9094
+ }
9095
+ },
9096
+ "parameters": [
9097
+ {
9098
+ "name": "message",
9099
+ "type": {
9100
+ "text": "ChatMessage"
9101
+ }
9102
+ }
9103
+ ],
9104
+ "description": "Whether `message` was written or sent on the user's behalf. A delegated message renders as the\nassistant, whatever its role."
9105
+ },
9106
+ {
9107
+ "kind": "variable",
9108
+ "name": "DEFAULT_DELEGATE_FRAMING",
9109
+ "type": {
9110
+ "text": "string"
9111
+ },
9112
+ "default": "\"Sent on the user's behalf.\"",
9113
+ "description": "How a delegated `user` message is framed for the model when its sender gives no phrase. Names no\ndecorator, so the model has none to repeat."
9114
+ }
9115
+ ],
9116
+ "exports": [
9117
+ {
9118
+ "kind": "js",
9119
+ "name": "isDelegated",
9120
+ "declaration": {
9121
+ "name": "isDelegated",
9122
+ "module": "src/utils/delegated-message.ts"
9123
+ }
9124
+ },
9125
+ {
9126
+ "kind": "js",
9127
+ "name": "DEFAULT_DELEGATE_FRAMING",
9128
+ "declaration": {
9129
+ "name": "DEFAULT_DELEGATE_FRAMING",
9130
+ "module": "src/utils/delegated-message.ts"
9131
+ }
9132
+ }
9133
+ ]
9134
+ },
8968
9135
  {
8969
9136
  "kind": "javascript-module",
8970
9137
  "path": "src/utils/derive-cost-session-title.ts",
@@ -10908,6 +11075,25 @@
10908
11075
  },
10909
11076
  "description": "Release the active agent: fire `onDeactivate` and drop the flow lock.\n\nThe user pin is intentionally **not** cleared — someone who explicitly picked this agent\nshould stay on it after the flow ends, so the next message starts a fresh flow on the same\nagent rather than routing through the classifier.\n\nReturns `undefined` when there was no active agent, so a caller can skip its events."
10910
11077
  },
11078
+ {
11079
+ "kind": "method",
11080
+ "name": "reportTransition",
11081
+ "privacy": "private",
11082
+ "return": {
11083
+ "type": {
11084
+ "text": "void"
11085
+ }
11086
+ },
11087
+ "parameters": [
11088
+ {
11089
+ "name": "transition",
11090
+ "type": {
11091
+ "text": "AgentTransition"
11092
+ }
11093
+ }
11094
+ ],
11095
+ "description": "Hand a transition to the owner. A throwing listener is logged rather than propagated, for the\nsame reason a throwing hook is: the transition has already happened, and failing the caller\nwould leave it believing it had not."
11096
+ },
10911
11097
  {
10912
11098
  "kind": "method",
10913
11099
  "name": "dispose",
@@ -10975,135 +11161,809 @@
10975
11161
  {
10976
11162
  "kind": "javascript-module",
10977
11163
  "path": "src/components/ai-driver/ai-driver.ts",
10978
- "declarations": [],
10979
- "exports": []
10980
- },
10981
- {
10982
- "kind": "javascript-module",
10983
- "path": "src/components/ai-driver/index.ts",
10984
- "declarations": [],
10985
- "exports": [
10986
- {
10987
- "kind": "js",
10988
- "name": "AiDriver",
10989
- "declaration": {
10990
- "name": "AiDriver",
10991
- "module": "./ai-driver"
10992
- }
10993
- }
10994
- ]
10995
- },
10996
- {
10997
- "kind": "javascript-module",
10998
- "path": "src/components/ai-driver/stub-driver.ts",
10999
- "declarations": [],
11000
- "exports": []
11001
- },
11002
- {
11003
- "kind": "javascript-module",
11004
- "path": "src/components/chat-bubble/chat-bubble.styles.ts",
11005
11164
  "declarations": [
11006
11165
  {
11007
11166
  "kind": "variable",
11008
- "name": "styles",
11009
- "default": "css`\n :host {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 9999;\n overflow: visible;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .bubble {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n cursor: grab;\n pointer-events: all;\n overflow: visible;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n transition:\n transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.25s ease;\n background: linear-gradient(135deg, #0e1628 0%, #192340 100%);\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n color: white;\n }\n\n .bubble img {\n width: 62px;\n height: 62px;\n border-radius: 50%;\n object-fit: cover;\n pointer-events: none;\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n }\n\n .bubble svg {\n pointer-events: none;\n }\n\n .bubble:hover,\n .bubble.hovered {\n transform: scale(1.18);\n }\n\n .bubble:hover img,\n .bubble.hovered img {\n box-shadow:\n 0 6px 32px rgb(0 100 255 / 55%),\n 0 0 0 3px rgb(0 120 255 / 35%);\n }\n\n .bubble:active {\n cursor: grabbing;\n }\n\n .tooltip {\n position: absolute;\n bottom: calc(100% + 10px);\n background: linear-gradient(135deg, #0078ff 0%, #0050d0 100%);\n color: #fff;\n font-size: 0.78em;\n font-weight: 600;\n letter-spacing: 0.03em;\n white-space: nowrap;\n padding: 5px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 14px rgb(0 120 255 / 45%);\n pointer-events: none;\n opacity: 0%;\n translate: 0 4px;\n transition:\n opacity 0.18s ease,\n translate 0.18s ease;\n }\n\n .tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: var(--arrow-left, 50%);\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #0050d0;\n }\n\n .bubble:hover .tooltip,\n .bubble.hovered .tooltip {\n opacity: 100%;\n translate: 0 0;\n }\n\n .pulse-ring {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n border: 2px solid rgb(0 120 255 / 50%);\n animation: pulse 2.5s ease-out infinite;\n pointer-events: none;\n }\n\n @keyframes pulse {\n 0% {\n transform: scale(1);\n opacity: 70%;\n }\n\n 70% {\n transform: scale(1.5);\n opacity: 0%;\n }\n\n 100% {\n transform: scale(1.5);\n opacity: 0%;\n }\n }\n\n .dialog {\n position: absolute;\n box-sizing: border-box;\n width: 400px;\n height: 520px;\n min-width: 300px;\n min-height: 300px;\n max-width: 50vw;\n max-height: calc(100vh - 40px);\n background: var(--neutral-layer-1);\n border-radius: 14px;\n box-shadow:\n 0 12px 50px rgb(0 0 0 / 55%),\n 0 0 0 1px rgb(255 255 255 / 6%);\n pointer-events: all;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n resize: both;\n border: 1px solid var(--neutral-stroke-rest);\n animation: dialog-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes dialog-in {\n from {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n\n to {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n }\n\n @keyframes dialog-out {\n from {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n\n to {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n }\n\n .dialog.closing {\n animation: dialog-out 0.2s cubic-bezier(0.4, 0, 0.6, 1) forwards;\n }\n\n .dialog-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n min-height: 0;\n }\n\n .dialog-content ::slotted(*) {\n display: flex;\n flex: 1;\n width: 100%;\n min-height: 0;\n }\n`"
11010
- }
11011
- ],
11012
- "exports": [
11013
- {
11014
- "kind": "js",
11015
- "name": "styles",
11016
- "declaration": {
11017
- "name": "styles",
11018
- "module": "src/components/chat-bubble/chat-bubble.styles.ts"
11019
- }
11020
- }
11021
- ]
11022
- },
11023
- {
11024
- "kind": "javascript-module",
11025
- "path": "src/components/chat-bubble/chat-bubble.template.ts",
11026
- "declarations": [
11027
- {
11028
- "kind": "function",
11029
- "name": "AiChatBubbleTemplate",
11030
- "return": {
11031
- "type": {
11032
- "text": "ViewTemplate<AiChatBubble>"
11033
- }
11034
- }
11167
+ "name": "AI_DRIVER_EVENT_TYPES",
11168
+ "description": "The event types in AiDriverEventMap, as a runtime list. Built from a record keyed by the\nmap, so adding an event to the map without adding it here is a compile error."
11035
11169
  }
11036
11170
  ],
11037
11171
  "exports": [
11038
11172
  {
11039
11173
  "kind": "js",
11040
- "name": "AiChatBubbleTemplate",
11174
+ "name": "AI_DRIVER_EVENT_TYPES",
11041
11175
  "declaration": {
11042
- "name": "AiChatBubbleTemplate",
11043
- "module": "src/components/chat-bubble/chat-bubble.template.ts"
11176
+ "name": "AI_DRIVER_EVENT_TYPES",
11177
+ "module": "src/components/ai-driver/ai-driver.ts"
11044
11178
  }
11045
11179
  }
11046
11180
  ]
11047
11181
  },
11048
11182
  {
11049
11183
  "kind": "javascript-module",
11050
- "path": "src/components/chat-bubble/chat-bubble.ts",
11184
+ "path": "src/components/ai-driver/base-driver-decorator.ts",
11051
11185
  "declarations": [
11052
11186
  {
11053
11187
  "kind": "class",
11054
- "description": "Floating chat bubble component that provides a draggable orb and dialog panel\nfor embedding an AI assistant without a full-page layout.",
11055
- "name": "AiChatBubble",
11188
+ "description": "The base of every driver decorator (GENC-1660): an AiDriver that wraps another and\npasses every member, and every event in AiDriverEventMap, through to it.\n\nA decorator extends this and overrides only what it changes, so decorators stack in any order\nand a member or event added to `AiDriver` reaches the host through every layer by default.\n`OrchestratingDriver` is one; it used to forward by hand, and twice a member it did not forward\nsilently did nothing for every multi-agent host (`recordExternalCost`, then\n`getHistoryForPersistence`).\n\nAbstract with no abstract members, so it cannot be constructed on its own: a decorator that\nchanges nothing is not one.\n\nOptional `AiDriver` members are implemented here unconditionally, as a forward that falls back\nto the \"not supported\" answer when the inner driver lacks them. A host that feature-detects with\n`?.` therefore always finds them, and gets the same answer it would from the inner driver.",
11189
+ "name": "BaseDriverDecorator",
11056
11190
  "members": [
11057
11191
  {
11058
11192
  "kind": "field",
11059
- "name": "title",
11060
- "type": {
11061
- "text": "string"
11062
- },
11063
- "default": "'AI Assistant'"
11064
- },
11065
- {
11066
- "kind": "field",
11067
- "name": "imageSrc",
11068
- "type": {
11069
- "text": "string | undefined"
11070
- }
11071
- },
11072
- {
11073
- "kind": "field",
11074
- "name": "designSystemPrefix",
11193
+ "name": "detachers",
11075
11194
  "type": {
11076
- "text": "string"
11195
+ "text": "Array<() => void>"
11077
11196
  },
11078
- "default": "'rapid'"
11197
+ "privacy": "private",
11198
+ "readonly": true,
11199
+ "default": "[]",
11200
+ "description": "Undoes this layer's hold on its inner driver; see BaseDriverDecorator.detach."
11079
11201
  },
11080
11202
  {
11081
- "kind": "field",
11082
- "name": "bubbleLeft",
11083
- "type": {
11084
- "text": "number"
11203
+ "kind": "method",
11204
+ "name": "detach",
11205
+ "return": {
11206
+ "type": {
11207
+ "text": "void"
11208
+ }
11085
11209
  },
11086
- "default": "0",
11087
- "description": "Current left position of the bubble orb (px from viewport left). @internal"
11210
+ "description": "Let go of the inner driver WITHOUT disposing it: stop forwarding its events, and undo whatever\na subclass registered through BaseDriverDecorator.onDetach (its interceptors, its\ncapabilities, its own listeners). For dropping or replacing a decorator while the driver it\nwrapped lives on — otherwise the old layer keeps re-dispatching every event and is never\ncollected. Idempotent. `dispose()` detaches too."
11088
11211
  },
11089
11212
  {
11090
- "kind": "field",
11091
- "name": "bubbleTop",
11092
- "type": {
11093
- "text": "number"
11213
+ "kind": "method",
11214
+ "name": "onDetach",
11215
+ "privacy": "protected",
11216
+ "return": {
11217
+ "type": {
11218
+ "text": "void"
11219
+ }
11094
11220
  },
11095
- "default": "0",
11096
- "description": "Current top position of the bubble orb (px from viewport top). @internal"
11221
+ "parameters": [
11222
+ {
11223
+ "name": "undo",
11224
+ "type": {
11225
+ "text": "() => void"
11226
+ }
11227
+ }
11228
+ ],
11229
+ "description": "Register something BaseDriverDecorator.detach must undo."
11097
11230
  },
11098
11231
  {
11099
- "kind": "field",
11100
- "name": "dialogLeft",
11101
- "type": {
11102
- "text": "number"
11232
+ "kind": "method",
11233
+ "name": "sendMessage",
11234
+ "return": {
11235
+ "type": {
11236
+ "text": "Promise<ChatDriverResult>"
11237
+ }
11103
11238
  },
11104
- "default": "0",
11105
- "description": "Current left position of the dialog panel (px from viewport left). @internal"
11106
- },
11239
+ "parameters": [
11240
+ {
11241
+ "name": "input",
11242
+ "type": {
11243
+ "text": "string"
11244
+ }
11245
+ },
11246
+ {
11247
+ "name": "attachments",
11248
+ "optional": true,
11249
+ "type": {
11250
+ "text": "ChatAttachment[]"
11251
+ }
11252
+ },
11253
+ {
11254
+ "name": "options",
11255
+ "optional": true,
11256
+ "type": {
11257
+ "text": "SendMessageOptions"
11258
+ }
11259
+ }
11260
+ ]
11261
+ },
11262
+ {
11263
+ "kind": "method",
11264
+ "name": "continueFromHistory",
11265
+ "return": {
11266
+ "type": {
11267
+ "text": "Promise<ChatDriverResult>"
11268
+ }
11269
+ },
11270
+ "parameters": [
11271
+ {
11272
+ "name": "transientPrimer",
11273
+ "optional": true,
11274
+ "type": {
11275
+ "text": "ChatMessage[]"
11276
+ }
11277
+ },
11278
+ {
11279
+ "name": "options",
11280
+ "optional": true,
11281
+ "type": {
11282
+ "text": "ContinueFromHistoryOptions"
11283
+ }
11284
+ }
11285
+ ]
11286
+ },
11287
+ {
11288
+ "kind": "method",
11289
+ "name": "resolveInteraction",
11290
+ "return": {
11291
+ "type": {
11292
+ "text": "void"
11293
+ }
11294
+ },
11295
+ "parameters": [
11296
+ {
11297
+ "name": "interactionId",
11298
+ "type": {
11299
+ "text": "string"
11300
+ }
11301
+ },
11302
+ {
11303
+ "name": "result",
11304
+ "type": {
11305
+ "text": "unknown"
11306
+ }
11307
+ }
11308
+ ]
11309
+ },
11310
+ {
11311
+ "kind": "method",
11312
+ "name": "recordExternalCost",
11313
+ "return": {
11314
+ "type": {
11315
+ "text": "boolean"
11316
+ }
11317
+ },
11318
+ "parameters": [
11319
+ {
11320
+ "name": "interactionId",
11321
+ "type": {
11322
+ "text": "string"
11323
+ }
11324
+ },
11325
+ {
11326
+ "name": "costUsd",
11327
+ "type": {
11328
+ "text": "number"
11329
+ }
11330
+ }
11331
+ ]
11332
+ },
11333
+ {
11334
+ "kind": "method",
11335
+ "name": "getInteractionContext",
11336
+ "return": {
11337
+ "type": {
11338
+ "text": "InteractionContext | undefined"
11339
+ }
11340
+ },
11341
+ "parameters": [
11342
+ {
11343
+ "name": "interactionId",
11344
+ "type": {
11345
+ "text": "string"
11346
+ }
11347
+ }
11348
+ ]
11349
+ },
11350
+ {
11351
+ "kind": "method",
11352
+ "name": "loadHistory",
11353
+ "return": {
11354
+ "type": {
11355
+ "text": "void"
11356
+ }
11357
+ },
11358
+ "parameters": [
11359
+ {
11360
+ "name": "messages",
11361
+ "type": {
11362
+ "text": "ChatMessage[]"
11363
+ }
11364
+ }
11365
+ ]
11366
+ },
11367
+ {
11368
+ "kind": "method",
11369
+ "name": "primeRestoredAgentState",
11370
+ "return": {
11371
+ "type": {
11372
+ "text": "void"
11373
+ }
11374
+ },
11375
+ "parameters": [
11376
+ {
11377
+ "name": "snapshots",
11378
+ "type": {
11379
+ "text": "Record<string, unknown>"
11380
+ }
11381
+ }
11382
+ ]
11383
+ },
11384
+ {
11385
+ "kind": "method",
11386
+ "name": "canRestoreFlowOwner",
11387
+ "return": {
11388
+ "type": {
11389
+ "text": "boolean"
11390
+ }
11391
+ },
11392
+ "parameters": [
11393
+ {
11394
+ "name": "agentName",
11395
+ "type": {
11396
+ "text": "string"
11397
+ }
11398
+ }
11399
+ ]
11400
+ },
11401
+ {
11402
+ "kind": "method",
11403
+ "name": "canResumeRestoredFlow",
11404
+ "return": {
11405
+ "type": {
11406
+ "text": "boolean"
11407
+ }
11408
+ },
11409
+ "parameters": [
11410
+ {
11411
+ "name": "agentName",
11412
+ "type": {
11413
+ "text": "string"
11414
+ }
11415
+ }
11416
+ ]
11417
+ },
11418
+ {
11419
+ "kind": "method",
11420
+ "name": "resumeRestoredFlow",
11421
+ "return": {
11422
+ "type": {
11423
+ "text": "Promise<ChatDriverResult | undefined>"
11424
+ }
11425
+ },
11426
+ "parameters": [
11427
+ {
11428
+ "name": "resumeNote",
11429
+ "optional": true,
11430
+ "type": {
11431
+ "text": "string"
11432
+ }
11433
+ },
11434
+ {
11435
+ "name": "agent",
11436
+ "optional": true,
11437
+ "type": {
11438
+ "text": "AgentConfig"
11439
+ }
11440
+ }
11441
+ ]
11442
+ },
11443
+ {
11444
+ "kind": "method",
11445
+ "name": "getRawHistory",
11446
+ "return": {
11447
+ "type": {
11448
+ "text": "readonly ChatMessage[]"
11449
+ }
11450
+ }
11451
+ },
11452
+ {
11453
+ "kind": "method",
11454
+ "name": "getHistoryForPersistence",
11455
+ "return": {
11456
+ "type": {
11457
+ "text": "ChatMessage[]"
11458
+ }
11459
+ }
11460
+ },
11461
+ {
11462
+ "kind": "method",
11463
+ "name": "getCondensePoliciesForPersistence",
11464
+ "return": {
11465
+ "type": {
11466
+ "text": "PersistedCondensePolicy[]"
11467
+ }
11468
+ },
11469
+ "parameters": [
11470
+ {
11471
+ "name": "messages",
11472
+ "type": {
11473
+ "text": "readonly ChatMessage[]"
11474
+ }
11475
+ }
11476
+ ]
11477
+ },
11478
+ {
11479
+ "kind": "method",
11480
+ "name": "restoreCondensePolicies",
11481
+ "return": {
11482
+ "type": {
11483
+ "text": "{\n restored: number;\n awaitingReactivation: number;\n }"
11484
+ }
11485
+ },
11486
+ "parameters": [
11487
+ {
11488
+ "name": "entries",
11489
+ "type": {
11490
+ "text": "readonly unknown[] | undefined"
11491
+ }
11492
+ }
11493
+ ]
11494
+ },
11495
+ {
11496
+ "kind": "method",
11497
+ "name": "getActiveAgent",
11498
+ "return": {
11499
+ "type": {
11500
+ "text": "AgentConfig | undefined"
11501
+ }
11502
+ }
11503
+ },
11504
+ {
11505
+ "kind": "method",
11506
+ "name": "setPinnedAgent",
11507
+ "return": {
11508
+ "type": {
11509
+ "text": "void"
11510
+ }
11511
+ },
11512
+ "parameters": [
11513
+ {
11514
+ "name": "name",
11515
+ "type": {
11516
+ "text": "string | null"
11517
+ }
11518
+ }
11519
+ ]
11520
+ },
11521
+ {
11522
+ "kind": "method",
11523
+ "name": "setFlowOwner",
11524
+ "return": {
11525
+ "type": {
11526
+ "text": "void"
11527
+ }
11528
+ },
11529
+ "parameters": [
11530
+ {
11531
+ "name": "name",
11532
+ "type": {
11533
+ "text": "string | null"
11534
+ }
11535
+ }
11536
+ ]
11537
+ },
11538
+ {
11539
+ "kind": "method",
11540
+ "name": "getFlowOwner",
11541
+ "return": {
11542
+ "type": {
11543
+ "text": "string | null"
11544
+ }
11545
+ }
11546
+ },
11547
+ {
11548
+ "kind": "method",
11549
+ "name": "addInteractionInterceptor",
11550
+ "return": {
11551
+ "type": {
11552
+ "text": "() => void"
11553
+ }
11554
+ },
11555
+ "parameters": [
11556
+ {
11557
+ "name": "interceptor",
11558
+ "type": {
11559
+ "text": "InteractionInterceptor"
11560
+ }
11561
+ }
11562
+ ],
11563
+ "description": "Forwarded, so an interceptor installed on any layer lands on the driver that opens the\ninteractions. A decorator that answers interactions installs its own on `this.inner`."
11564
+ },
11565
+ {
11566
+ "kind": "method",
11567
+ "name": "presentInteraction",
11568
+ "return": {
11569
+ "type": {
11570
+ "text": "Promise<T>"
11571
+ }
11572
+ },
11573
+ "parameters": [
11574
+ {
11575
+ "name": "componentName",
11576
+ "type": {
11577
+ "text": "string"
11578
+ }
11579
+ },
11580
+ {
11581
+ "name": "data",
11582
+ "type": {
11583
+ "text": "unknown"
11584
+ }
11585
+ },
11586
+ {
11587
+ "name": "options",
11588
+ "optional": true,
11589
+ "type": {
11590
+ "text": "InteractionRequestOptions"
11591
+ }
11592
+ }
11593
+ ]
11594
+ },
11595
+ {
11596
+ "kind": "method",
11597
+ "name": "provideHandlerCapability",
11598
+ "return": {
11599
+ "type": {
11600
+ "text": "() => void"
11601
+ }
11602
+ },
11603
+ "parameters": [
11604
+ {
11605
+ "name": "name",
11606
+ "type": {
11607
+ "text": "string"
11608
+ }
11609
+ },
11610
+ {
11611
+ "name": "factory",
11612
+ "type": {
11613
+ "text": "HandlerCapabilityFactory"
11614
+ }
11615
+ }
11616
+ ]
11617
+ },
11618
+ {
11619
+ "kind": "method",
11620
+ "name": "getContextReserve",
11621
+ "return": {
11622
+ "type": {
11623
+ "text": "ContextReserveResolution | undefined"
11624
+ }
11625
+ }
11626
+ },
11627
+ {
11628
+ "kind": "method",
11629
+ "name": "getPreambleTokens",
11630
+ "return": {
11631
+ "type": {
11632
+ "text": "number | undefined"
11633
+ }
11634
+ }
11635
+ },
11636
+ {
11637
+ "kind": "method",
11638
+ "name": "setContextGuard",
11639
+ "return": {
11640
+ "type": {
11641
+ "text": "void"
11642
+ }
11643
+ },
11644
+ "parameters": [
11645
+ {
11646
+ "name": "policy",
11647
+ "optional": true,
11648
+ "type": {
11649
+ "text": "ContextGuardPolicy"
11650
+ }
11651
+ }
11652
+ ]
11653
+ },
11654
+ {
11655
+ "kind": "method",
11656
+ "name": "compact",
11657
+ "return": {
11658
+ "type": {
11659
+ "text": "Promise<ChatMessage | null>"
11660
+ }
11661
+ },
11662
+ "parameters": [
11663
+ {
11664
+ "name": "options",
11665
+ "optional": true,
11666
+ "type": {
11667
+ "text": "CompactionPlanOptions"
11668
+ }
11669
+ }
11670
+ ]
11671
+ },
11672
+ {
11673
+ "kind": "method",
11674
+ "name": "getCompactionPlan",
11675
+ "return": {
11676
+ "type": {
11677
+ "text": "CompactionProjection | null"
11678
+ }
11679
+ },
11680
+ "parameters": [
11681
+ {
11682
+ "name": "options",
11683
+ "optional": true,
11684
+ "type": {
11685
+ "text": "CompactionPlanOptions"
11686
+ }
11687
+ }
11688
+ ]
11689
+ },
11690
+ {
11691
+ "kind": "method",
11692
+ "name": "canCompact",
11693
+ "return": {
11694
+ "type": {
11695
+ "text": "boolean"
11696
+ }
11697
+ },
11698
+ "parameters": [
11699
+ {
11700
+ "name": "options",
11701
+ "optional": true,
11702
+ "type": {
11703
+ "text": "CompactionPlanOptions"
11704
+ }
11705
+ }
11706
+ ]
11707
+ },
11708
+ {
11709
+ "kind": "method",
11710
+ "name": "isBusy",
11711
+ "return": {
11712
+ "type": {
11713
+ "text": "boolean"
11714
+ }
11715
+ }
11716
+ },
11717
+ {
11718
+ "kind": "method",
11719
+ "name": "getSuggestions",
11720
+ "return": {
11721
+ "type": {
11722
+ "text": "Promise<string[]>"
11723
+ }
11724
+ },
11725
+ "parameters": [
11726
+ {
11727
+ "name": "history",
11728
+ "type": {
11729
+ "text": "ChatMessage[]"
11730
+ }
11731
+ },
11732
+ {
11733
+ "name": "prompt",
11734
+ "type": {
11735
+ "text": "string"
11736
+ }
11737
+ },
11738
+ {
11739
+ "name": "count",
11740
+ "type": {
11741
+ "text": "number"
11742
+ }
11743
+ },
11744
+ {
11745
+ "name": "allAgentInfo",
11746
+ "optional": true,
11747
+ "type": {
11748
+ "text": "AllAgentSummary[]"
11749
+ }
11750
+ }
11751
+ ]
11752
+ },
11753
+ {
11754
+ "kind": "method",
11755
+ "name": "getTurnSnapshots",
11756
+ "return": {
11757
+ "type": {
11758
+ "text": "ReadonlyArray<TurnSnapshot>"
11759
+ }
11760
+ }
11761
+ },
11762
+ {
11763
+ "kind": "method",
11764
+ "name": "getExternalDiagnostics",
11765
+ "return": {
11766
+ "type": {
11767
+ "text": "ReadonlyArray<DiagnosticEntry>"
11768
+ }
11769
+ },
11770
+ "description": "The inner driver's entries. Diagnostics compose down the stack: a decorator with entries of\nits own overrides this and appends them to `super.getExternalDiagnostics()`."
11771
+ },
11772
+ {
11773
+ "kind": "method",
11774
+ "name": "getActiveProviderName",
11775
+ "return": {
11776
+ "type": {
11777
+ "text": "string"
11778
+ }
11779
+ }
11780
+ },
11781
+ {
11782
+ "kind": "method",
11783
+ "name": "dispose",
11784
+ "return": {
11785
+ "type": {
11786
+ "text": "void | Promise<void>"
11787
+ }
11788
+ }
11789
+ },
11790
+ {
11791
+ "kind": "method",
11792
+ "name": "cancel",
11793
+ "return": {
11794
+ "type": {
11795
+ "text": "void"
11796
+ }
11797
+ }
11798
+ }
11799
+ ],
11800
+ "superclass": {
11801
+ "name": "EventTarget",
11802
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
11803
+ }
11804
+ }
11805
+ ],
11806
+ "exports": [
11807
+ {
11808
+ "kind": "js",
11809
+ "name": "BaseDriverDecorator",
11810
+ "declaration": {
11811
+ "name": "BaseDriverDecorator",
11812
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
11813
+ }
11814
+ }
11815
+ ]
11816
+ },
11817
+ {
11818
+ "kind": "javascript-module",
11819
+ "path": "src/components/ai-driver/index.ts",
11820
+ "declarations": [],
11821
+ "exports": [
11822
+ {
11823
+ "kind": "js",
11824
+ "name": "AiDriver",
11825
+ "declaration": {
11826
+ "name": "AiDriver",
11827
+ "module": "./ai-driver"
11828
+ }
11829
+ },
11830
+ {
11831
+ "kind": "js",
11832
+ "name": "AiDriverEventMap",
11833
+ "declaration": {
11834
+ "name": "AiDriverEventMap",
11835
+ "module": "./ai-driver"
11836
+ }
11837
+ },
11838
+ {
11839
+ "kind": "js",
11840
+ "name": "AI_DRIVER_EVENT_TYPES",
11841
+ "declaration": {
11842
+ "name": "AI_DRIVER_EVENT_TYPES",
11843
+ "module": "./ai-driver"
11844
+ }
11845
+ },
11846
+ {
11847
+ "kind": "js",
11848
+ "name": "BaseDriverDecorator",
11849
+ "declaration": {
11850
+ "name": "BaseDriverDecorator",
11851
+ "module": "./base-driver-decorator"
11852
+ }
11853
+ }
11854
+ ]
11855
+ },
11856
+ {
11857
+ "kind": "javascript-module",
11858
+ "path": "src/components/ai-driver/stub-driver.ts",
11859
+ "declarations": [],
11860
+ "exports": []
11861
+ },
11862
+ {
11863
+ "kind": "javascript-module",
11864
+ "path": "src/components/chat-bubble/chat-bubble.styles.ts",
11865
+ "declarations": [
11866
+ {
11867
+ "kind": "variable",
11868
+ "name": "styles",
11869
+ "default": "css`\n :host {\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 9999;\n overflow: visible;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n .bubble {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n cursor: grab;\n pointer-events: all;\n overflow: visible;\n display: flex;\n align-items: center;\n justify-content: center;\n user-select: none;\n transition:\n transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),\n box-shadow 0.25s ease;\n background: linear-gradient(135deg, #0e1628 0%, #192340 100%);\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n color: white;\n }\n\n .bubble img {\n width: 62px;\n height: 62px;\n border-radius: 50%;\n object-fit: cover;\n pointer-events: none;\n box-shadow: 0 4px 20px rgb(0 0 0 / 45%);\n border: 2px solid rgb(255 255 255 / 15%);\n }\n\n .bubble svg {\n pointer-events: none;\n }\n\n .bubble:hover,\n .bubble.hovered {\n transform: scale(1.18);\n }\n\n .bubble:hover img,\n .bubble.hovered img {\n box-shadow:\n 0 6px 32px rgb(0 100 255 / 55%),\n 0 0 0 3px rgb(0 120 255 / 35%);\n }\n\n .bubble:active {\n cursor: grabbing;\n }\n\n .tooltip {\n position: absolute;\n bottom: calc(100% + 10px);\n background: linear-gradient(135deg, #0078ff 0%, #0050d0 100%);\n color: #fff;\n font-size: 0.78em;\n font-weight: 600;\n letter-spacing: 0.03em;\n white-space: nowrap;\n padding: 5px 10px;\n border-radius: 8px;\n box-shadow: 0 4px 14px rgb(0 120 255 / 45%);\n pointer-events: none;\n opacity: 0%;\n translate: 0 4px;\n transition:\n opacity 0.18s ease,\n translate 0.18s ease;\n }\n\n .tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: var(--arrow-left, 50%);\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: #0050d0;\n }\n\n .bubble:hover .tooltip,\n .bubble.hovered .tooltip {\n opacity: 100%;\n translate: 0 0;\n }\n\n .pulse-ring {\n position: absolute;\n width: 62px;\n height: 62px;\n border-radius: 50%;\n border: 2px solid rgb(0 120 255 / 50%);\n animation: pulse 2.5s ease-out infinite;\n pointer-events: none;\n }\n\n @keyframes pulse {\n 0% {\n transform: scale(1);\n opacity: 70%;\n }\n\n 70% {\n transform: scale(1.5);\n opacity: 0%;\n }\n\n 100% {\n transform: scale(1.5);\n opacity: 0%;\n }\n }\n\n .dialog {\n position: absolute;\n box-sizing: border-box;\n width: 400px;\n height: 520px;\n min-width: 300px;\n min-height: 300px;\n max-width: 50vw;\n max-height: calc(100vh - 40px);\n background: var(--neutral-layer-1);\n border-radius: 14px;\n box-shadow:\n 0 12px 50px rgb(0 0 0 / 55%),\n 0 0 0 1px rgb(255 255 255 / 6%);\n pointer-events: all;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n resize: both;\n border: 1px solid var(--neutral-stroke-rest);\n animation: dialog-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes dialog-in {\n from {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n\n to {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n }\n\n @keyframes dialog-out {\n from {\n opacity: 100%;\n transform: scale(1) translateY(0);\n }\n\n to {\n opacity: 0%;\n transform: scale(0.85) translateY(12px);\n }\n }\n\n .dialog.closing {\n animation: dialog-out 0.2s cubic-bezier(0.4, 0, 0.6, 1) forwards;\n }\n\n .dialog-content {\n flex: 1;\n overflow: hidden;\n display: flex;\n min-height: 0;\n }\n\n .dialog-content ::slotted(*) {\n display: flex;\n flex: 1;\n width: 100%;\n min-height: 0;\n }\n`"
11870
+ }
11871
+ ],
11872
+ "exports": [
11873
+ {
11874
+ "kind": "js",
11875
+ "name": "styles",
11876
+ "declaration": {
11877
+ "name": "styles",
11878
+ "module": "src/components/chat-bubble/chat-bubble.styles.ts"
11879
+ }
11880
+ }
11881
+ ]
11882
+ },
11883
+ {
11884
+ "kind": "javascript-module",
11885
+ "path": "src/components/chat-bubble/chat-bubble.template.ts",
11886
+ "declarations": [
11887
+ {
11888
+ "kind": "function",
11889
+ "name": "AiChatBubbleTemplate",
11890
+ "return": {
11891
+ "type": {
11892
+ "text": "ViewTemplate<AiChatBubble>"
11893
+ }
11894
+ }
11895
+ }
11896
+ ],
11897
+ "exports": [
11898
+ {
11899
+ "kind": "js",
11900
+ "name": "AiChatBubbleTemplate",
11901
+ "declaration": {
11902
+ "name": "AiChatBubbleTemplate",
11903
+ "module": "src/components/chat-bubble/chat-bubble.template.ts"
11904
+ }
11905
+ }
11906
+ ]
11907
+ },
11908
+ {
11909
+ "kind": "javascript-module",
11910
+ "path": "src/components/chat-bubble/chat-bubble.ts",
11911
+ "declarations": [
11912
+ {
11913
+ "kind": "class",
11914
+ "description": "Floating chat bubble component that provides a draggable orb and dialog panel\nfor embedding an AI assistant without a full-page layout.",
11915
+ "name": "AiChatBubble",
11916
+ "members": [
11917
+ {
11918
+ "kind": "field",
11919
+ "name": "title",
11920
+ "type": {
11921
+ "text": "string"
11922
+ },
11923
+ "default": "'AI Assistant'"
11924
+ },
11925
+ {
11926
+ "kind": "field",
11927
+ "name": "imageSrc",
11928
+ "type": {
11929
+ "text": "string | undefined"
11930
+ }
11931
+ },
11932
+ {
11933
+ "kind": "field",
11934
+ "name": "designSystemPrefix",
11935
+ "type": {
11936
+ "text": "string"
11937
+ },
11938
+ "default": "'rapid'"
11939
+ },
11940
+ {
11941
+ "kind": "field",
11942
+ "name": "bubbleLeft",
11943
+ "type": {
11944
+ "text": "number"
11945
+ },
11946
+ "default": "0",
11947
+ "description": "Current left position of the bubble orb (px from viewport left). @internal"
11948
+ },
11949
+ {
11950
+ "kind": "field",
11951
+ "name": "bubbleTop",
11952
+ "type": {
11953
+ "text": "number"
11954
+ },
11955
+ "default": "0",
11956
+ "description": "Current top position of the bubble orb (px from viewport top). @internal"
11957
+ },
11958
+ {
11959
+ "kind": "field",
11960
+ "name": "dialogLeft",
11961
+ "type": {
11962
+ "text": "number"
11963
+ },
11964
+ "default": "0",
11965
+ "description": "Current left position of the dialog panel (px from viewport left). @internal"
11966
+ },
11107
11967
  {
11108
11968
  "kind": "field",
11109
11969
  "name": "dialogTop",
@@ -12085,6 +12945,23 @@
12085
12945
  "privacy": "private",
12086
12946
  "description": "A sub-agent runs inside its parent's message: the parent's `turnSignal`, handed down."
12087
12947
  },
12948
+ {
12949
+ "kind": "field",
12950
+ "name": "handlerCapabilities",
12951
+ "privacy": "private",
12952
+ "readonly": true,
12953
+ "default": "new Map<string, HandlerCapabilityFactory>()",
12954
+ "description": "Capabilities provided on this driver by decorators (GENC-1660). Only the root's are used."
12955
+ },
12956
+ {
12957
+ "kind": "field",
12958
+ "name": "capabilityParent",
12959
+ "type": {
12960
+ "text": "ChatDriver | undefined"
12961
+ },
12962
+ "privacy": "private",
12963
+ "description": "For a sub-agent's driver, the driver that invoked it: its capabilities, flow owner and owner\npath are the ones this driver's handlers see, so a capability reaches sub-agents unchanged."
12964
+ },
12088
12965
  {
12089
12966
  "kind": "field",
12090
12967
  "name": "activeProviderInput",
@@ -12730,6 +13607,51 @@
12730
13607
  ],
12731
13608
  "description": "Make `config` the active agent, firing its lifecycle hooks — `onDeactivate` on the outgoing\nagent, then `onActivate` on this one — before installing it.\n\nThis is the entry point a host should use. ChatDriver.installAgent installs a config\n*without* firing hooks, which is right for the two callers that need it (the orchestrator,\nwhich fires them through the shared session, and `invokeSubAgent`, where a child's config is\nnot an activation) and wrong for everyone else."
12732
13609
  },
13610
+ {
13611
+ "kind": "method",
13612
+ "name": "resumeRestoredFlow",
13613
+ "return": {
13614
+ "type": {
13615
+ "text": "Promise<ChatDriverResult | undefined>"
13616
+ }
13617
+ },
13618
+ "parameters": [
13619
+ {
13620
+ "name": "resumeNote",
13621
+ "optional": true,
13622
+ "type": {
13623
+ "text": "string"
13624
+ }
13625
+ },
13626
+ {
13627
+ "name": "agent",
13628
+ "optional": true,
13629
+ "type": {
13630
+ "text": "AgentConfig"
13631
+ }
13632
+ }
13633
+ ],
13634
+ "description": "{@inheritDoc AiDriver.resumeRestoredFlow}\n\nThis driver holds no list of agents, so it resumes only the `agent` it is handed (GENC-1660):\nwhoever runs a bare `ChatDriver` knows which agent that is, and after a reload only the name\nsurvived. Without one it does nothing, as a driver without this member did before; given one\nthat is not the restored flow owner, it refuses.\n\nFor headless callers: the shipped host restores and resumes through `OrchestratingDriver`,\nand a bare `ChatDriver` does not answer `canRestoreFlowOwner`, so the host never plans a resume\nfor one.\n\nThe orchestrator does not delegate here, because it installs a decorated config of its own;\nthis is the bare-driver path."
13635
+ },
13636
+ {
13637
+ "kind": "method",
13638
+ "name": "dispatchAgentTransition",
13639
+ "privacy": "private",
13640
+ "return": {
13641
+ "type": {
13642
+ "text": "void"
13643
+ }
13644
+ },
13645
+ "parameters": [
13646
+ {
13647
+ "name": "t",
13648
+ "type": {
13649
+ "text": "AgentTransition"
13650
+ }
13651
+ }
13652
+ ],
13653
+ "description": "Raise the host-facing lifecycle events for one session transition (GENC-1485), in the order\nthe orchestrator raised them before it stopped doing so itself: the flow lock first, then the\nrelease, then the active agent."
13654
+ },
12733
13655
  {
12734
13656
  "kind": "method",
12735
13657
  "name": "getActiveAgent",
@@ -12794,6 +13716,16 @@
12794
13716
  ],
12795
13717
  "description": "Record the transient flow lock. See AiDriver.setFlowOwner for the contract.\n\nRecorded, not acted on, for the same reason as ChatDriver.setPinnedAgent."
12796
13718
  },
13719
+ {
13720
+ "kind": "method",
13721
+ "name": "getFlowOwner",
13722
+ "return": {
13723
+ "type": {
13724
+ "text": "string | null"
13725
+ }
13726
+ },
13727
+ "description": "{@inheritDoc AiDriver.getFlowOwner}"
13728
+ },
12797
13729
  {
12798
13730
  "kind": "method",
12799
13731
  "name": "installAgent",
@@ -13396,9 +14328,118 @@
13396
14328
  "type": {
13397
14329
  "text": "(interactionId: string) => void"
13398
14330
  }
14331
+ },
14332
+ {
14333
+ "name": "innerPath",
14334
+ "optional": true,
14335
+ "type": {
14336
+ "text": "readonly string[]"
14337
+ }
14338
+ },
14339
+ {
14340
+ "name": "onSettled",
14341
+ "optional": true,
14342
+ "type": {
14343
+ "text": "(settled: InteractionSettled) => void"
14344
+ }
14345
+ }
14346
+ ],
14347
+ "description": "Request a custom UI interaction. Emits a new message with the interaction.\nTool handlers can call this to pause execution until the user completes the UI interaction.\n\nIf a host requester is wired (sub-agent case), the call delegates upward\nso the interaction lives on the parent — the main UI is only listening to\nthe root driver. Only one interaction may be in flight at any time on a\ngiven root: concurrent calls (e.g. two parallel sub-agents both spawning a\nwidget) throw. Parallel sub-agents are for parallel work, not for user\ninteraction, which is inherently sequential."
14348
+ },
14349
+ {
14350
+ "kind": "method",
14351
+ "name": "addInteractionInterceptor",
14352
+ "return": {
14353
+ "type": {
14354
+ "text": "() => void"
14355
+ }
14356
+ },
14357
+ "parameters": [
14358
+ {
14359
+ "name": "interceptor",
14360
+ "type": {
14361
+ "text": "InteractionInterceptor"
14362
+ }
14363
+ }
14364
+ ],
14365
+ "description": "{@inheritDoc AiDriver.addInteractionInterceptor}\n\nConsulted only on the root driver, where every interaction opens: a sub-agent's request\ntravels up to its root, carrying its owner path, and is offered there once."
14366
+ },
14367
+ {
14368
+ "kind": "method",
14369
+ "name": "presentInteraction",
14370
+ "return": {
14371
+ "type": {
14372
+ "text": "Promise<T>"
14373
+ }
14374
+ },
14375
+ "parameters": [
14376
+ {
14377
+ "name": "componentName",
14378
+ "type": {
14379
+ "text": "string"
14380
+ }
14381
+ },
14382
+ {
14383
+ "name": "data",
14384
+ "type": {
14385
+ "text": "unknown"
14386
+ }
14387
+ },
14388
+ {
14389
+ "name": "options",
14390
+ "optional": true,
14391
+ "type": {
14392
+ "text": "InteractionRequestOptions"
14393
+ }
14394
+ }
14395
+ ],
14396
+ "description": "{@inheritDoc AiDriver.presentInteraction}"
14397
+ },
14398
+ {
14399
+ "kind": "method",
14400
+ "name": "provideHandlerCapability",
14401
+ "return": {
14402
+ "type": {
14403
+ "text": "() => void"
14404
+ }
14405
+ },
14406
+ "parameters": [
14407
+ {
14408
+ "name": "name",
14409
+ "type": {
14410
+ "text": "string"
14411
+ }
14412
+ },
14413
+ {
14414
+ "name": "factory",
14415
+ "type": {
14416
+ "text": "HandlerCapabilityFactory"
14417
+ }
14418
+ }
14419
+ ],
14420
+ "description": "{@inheritDoc AiDriver.provideHandlerCapability}\n\nThrows if `name` is already provided: two decorators answering to one name would leave a\nhandler unable to tell which it is talking to."
14421
+ },
14422
+ {
14423
+ "kind": "method",
14424
+ "name": "capabilityScope",
14425
+ "privacy": "private",
14426
+ "return": {
14427
+ "type": {
14428
+ "text": "{\n factories: ReadonlyMap<string, HandlerCapabilityFactory>;\n scope: HandlerCapabilityScope;\n }"
14429
+ }
14430
+ },
14431
+ "description": "The capabilities this driver's handlers see, and who they see them as: the root's factories,\nthe root's flow owner, and the owner path from the root down to this driver's agent."
14432
+ },
14433
+ {
14434
+ "kind": "method",
14435
+ "name": "buildCapabilities",
14436
+ "privacy": "private",
14437
+ "return": {
14438
+ "type": {
14439
+ "text": "Partial<ChatHandlerCapabilities> | undefined"
13399
14440
  }
13400
- ],
13401
- "description": "Request a custom UI interaction. Emits a new message with the interaction.\nTool handlers can call this to pause execution until the user completes the UI interaction.\n\nIf a host requester is wired (sub-agent case), the call delegates upward\nso the interaction lives on the parent — the main UI is only listening to\nthe root driver. Only one interaction may be in flight at any time on a\ngiven root: concurrent calls (e.g. two parallel sub-agents both spawning a\nwidget) throw. Parallel sub-agents are for parallel work, not for user\ninteraction, which is inherently sequential."
14441
+ },
14442
+ "description": "Build `context.capabilities` for one handler call, or `undefined` when nothing is provided."
13402
14443
  },
13403
14444
  {
13404
14445
  "kind": "method",
@@ -13536,6 +14577,13 @@
13536
14577
  "type": {
13537
14578
  "text": "ChatAttachment[]"
13538
14579
  }
14580
+ },
14581
+ {
14582
+ "name": "options",
14583
+ "optional": true,
14584
+ "type": {
14585
+ "text": "SendMessageOptions"
14586
+ }
13539
14587
  }
13540
14588
  ]
13541
14589
  },
@@ -13559,6 +14607,13 @@
13559
14607
  "text": "{ traces: ChatMessage[][] }"
13560
14608
  },
13561
14609
  "description": "Optional per-tool-call accumulator. When provided, every\nsub-agent call's trace is **appended** here rather than written to shared\ninstance state, so parallel tool calls each capture their own traces\nindependently.\n\nDeliberately a list of traces, not one slot. A single handler may call\n`requestSubAgent` more than once — a code-driven scheduler dispatching a\ndependency graph, a retry of a timed-out child, any fan-out helper — and a\nsingle slot kept only the last, so every other child ran, was billed by the\nprovider, and then vanished from history. The loss was silent in the worst\nway: `sumUsage` and `usageRows` both recurse into the trace, so they summed a\ntruncated input and still agreed with each other. Measured at 1 of 7 traces\nkept on a seven-way fan-out, reporting ~2.3x under the real cost, with the\nerror growing as the fan-out widens."
14610
+ },
14611
+ {
14612
+ "name": "decisions",
14613
+ "optional": true,
14614
+ "type": {
14615
+ "text": "DelegatedDecision[]"
14616
+ }
13562
14617
  }
13563
14618
  ],
13564
14619
  "description": "Builds the context object passed to every tool handler call.\nCentralised here so fold shortcut dispatch and the main tool loop use the\nsame context without duplication."
@@ -13704,6 +14759,24 @@
13704
14759
  }
13705
14760
  ],
13706
14761
  "events": [
14762
+ {
14763
+ "name": "flow-owner-changed",
14764
+ "type": {
14765
+ "text": "CustomEvent"
14766
+ }
14767
+ },
14768
+ {
14769
+ "name": "agent-released",
14770
+ "type": {
14771
+ "text": "CustomEvent"
14772
+ }
14773
+ },
14774
+ {
14775
+ "name": "agent-changed",
14776
+ "type": {
14777
+ "text": "CustomEvent"
14778
+ }
14779
+ },
13707
14780
  {
13708
14781
  "name": "provider-changed",
13709
14782
  "type": {
@@ -14101,6 +15174,91 @@
14101
15174
  "privacy": "private",
14102
15175
  "description": "Wired with `hostRequester`: an outcome goes where its interaction went."
14103
15176
  },
15177
+ {
15178
+ "kind": "field",
15179
+ "name": "interceptors",
15180
+ "type": {
15181
+ "text": "InteractionInterceptor[]"
15182
+ },
15183
+ "privacy": "private",
15184
+ "readonly": true,
15185
+ "default": "[]",
15186
+ "description": "Told about every interaction this controller opens (GENC-1660), in registration order. Only\nthe root's are ever consulted: a sub-agent's request is opened by the root."
15187
+ },
15188
+ {
15189
+ "kind": "method",
15190
+ "name": "addInterceptor",
15191
+ "return": {
15192
+ "type": {
15193
+ "text": "() => void"
15194
+ }
15195
+ },
15196
+ "parameters": [
15197
+ {
15198
+ "name": "interceptor",
15199
+ "type": {
15200
+ "text": "InteractionInterceptor"
15201
+ }
15202
+ }
15203
+ ],
15204
+ "description": "See `ChatDriver.addInteractionInterceptor`. Returns a remover."
15205
+ },
15206
+ {
15207
+ "kind": "method",
15208
+ "name": "offer",
15209
+ "privacy": "private",
15210
+ "return": {
15211
+ "type": {
15212
+ "text": "InteractionHolder"
15213
+ }
15214
+ },
15215
+ "parameters": [
15216
+ {
15217
+ "name": "handle",
15218
+ "type": {
15219
+ "text": "InteractionContextHandle"
15220
+ }
15221
+ },
15222
+ {
15223
+ "name": "opened",
15224
+ "type": {
15225
+ "text": "Omit<InteractionOpened, 'ctx' | 'hold'>"
15226
+ }
15227
+ }
15228
+ ],
15229
+ "description": "Offer a new interaction to the interceptors and record who ends up holding it.\n\n**Outermost first.** A decorator installs its interceptor on the driver it wraps when it is\nconstructed, and decorators are constructed innermost first, so registration order is\ninnermost first and the walk runs it backwards. The first claim wins; later interceptors still\nsee the interaction but their `hold` does nothing. A claim on a `userOnly` interaction is\nrefused.\n\nRuns before the interaction's message is appended, so the widget's first render already knows\nits holder and nothing appears only to disappear."
15230
+ },
15231
+ {
15232
+ "kind": "method",
15233
+ "name": "holdFor",
15234
+ "privacy": "private",
15235
+ "return": {
15236
+ "type": {
15237
+ "text": "InteractionHold"
15238
+ }
15239
+ },
15240
+ "parameters": [
15241
+ {
15242
+ "name": "handle",
15243
+ "type": {
15244
+ "text": "InteractionContextHandle"
15245
+ }
15246
+ },
15247
+ {
15248
+ "name": "interceptor",
15249
+ "type": {
15250
+ "text": "InteractionInterceptor"
15251
+ }
15252
+ },
15253
+ {
15254
+ "name": "isClaimant",
15255
+ "type": {
15256
+ "text": "() => boolean"
15257
+ }
15258
+ }
15259
+ ],
15260
+ "description": "The InteractionHold for one interceptor: it applies only while that one is the claimant."
15261
+ },
14104
15262
  {
14105
15263
  "kind": "method",
14106
15264
  "name": "setHostRequester",
@@ -14160,9 +15318,23 @@
14160
15318
  "type": {
14161
15319
  "text": "(interactionId: string) => void"
14162
15320
  }
15321
+ },
15322
+ {
15323
+ "name": "innerPath",
15324
+ "default": "[]",
15325
+ "type": {
15326
+ "text": "readonly string[]"
15327
+ }
15328
+ },
15329
+ {
15330
+ "name": "onSettled",
15331
+ "optional": true,
15332
+ "type": {
15333
+ "text": "(settled: InteractionSettled) => void"
15334
+ }
14163
15335
  }
14164
15336
  ],
14165
- "description": "See `ChatDriver.requestInteraction`. `onOpened` is told the id of the interaction this call\nopens, wherever it opens it, so the caller can later record its outcome and no other's."
15337
+ "description": "See `ChatDriver.requestInteraction`. `onOpened` is told the id of the interaction this call\nopens, wherever it opens it, so the caller can later record its outcome and no other's.\n`innerPath` is the owner path a sub-agent's request has gathered on its way up, and\n`onSettled` is told who answered, wherever it resolves (GENC-1660)."
14166
15338
  },
14167
15339
  {
14168
15340
  "kind": "method",
@@ -14948,7 +16120,7 @@
14948
16120
  },
14949
16121
  {
14950
16122
  "kind": "class",
14951
- "description": "Orchestrates multiple specialist agents. Sits between `FoundationAiAssistant`\nand `ChatDriver`, classifying each user message and routing it to the right\nspecialist — each with its own focused system prompt, tools, and primer.",
16123
+ "description": "Orchestrates multiple specialist agents. Sits between `FoundationAiAssistant`\nand `ChatDriver`, classifying each user message and routing it to the right\nspecialist — each with its own focused system prompt, tools, and primer.\n\nA BaseDriverDecorator over the `ChatDriver` it constructs (GENC-1485, GENC-1660): every\n`AiDriver` member and event it does not override passes straight through. Its inner driver is\nalways one it built, never a composition choice, because routing reaches `ChatDriver`-only\nmembers (`installAgent`, `agentSession`, `getHistory`, `setProviderHistoryTransform`, the\nrelease flag and the `report…` exits). Other decorators go outside it.",
14952
16124
  "name": "OrchestratingDriver",
14953
16125
  "members": [
14954
16126
  {
@@ -14958,8 +16130,8 @@
14958
16130
  "text": "ChatDriver"
14959
16131
  },
14960
16132
  "privacy": "private",
14961
- "readonly": true,
14962
- "default": "new ChatDriver(providerRegistry, {\n maxToolIterations: options.maxToolIterations,\n maxFoldOperations: options.maxFoldOperations,\n maxTurnSnapshots: options.maxTurnSnapshots,\n sessionKey: this.sessionKey,\n activityBus: options.activityBus,\n budgetExhaustedMessage: options.budgetExhaustedMessage,\n aiServiceErrorMessages: options.aiServiceErrorMessages,\n // This driver releases after the turn so it can fire the host-facing events; see the option.\n deferAgentRelease: true,\n })"
16133
+ "description": "The inner driver this one built and routes onto.",
16134
+ "readonly": true
14963
16135
  },
14964
16136
  {
14965
16137
  "kind": "field",
@@ -15056,198 +16228,290 @@
15056
16228
  },
15057
16229
  {
15058
16230
  "kind": "method",
15059
- "name": "resolveInteraction",
16231
+ "name": "createInner",
16232
+ "privacy": "private",
16233
+ "static": true,
15060
16234
  "return": {
15061
16235
  "type": {
15062
- "text": "void"
16236
+ "text": "ChatDriver"
15063
16237
  }
15064
16238
  },
15065
16239
  "parameters": [
15066
16240
  {
15067
- "name": "interactionId",
16241
+ "name": "providerRegistry",
15068
16242
  "type": {
15069
- "text": "string"
16243
+ "text": "AIProviderRegistry"
15070
16244
  }
15071
16245
  },
15072
16246
  {
15073
- "name": "result",
16247
+ "name": "agents",
15074
16248
  "type": {
15075
- "text": "unknown"
16249
+ "text": "AgentConfig[]"
16250
+ }
16251
+ },
16252
+ {
16253
+ "name": "options",
16254
+ "default": "{}",
16255
+ "type": {
16256
+ "text": "ConstructorParameters<typeof OrchestratingDriver>[2]"
15076
16257
  }
15077
16258
  }
15078
- ]
16259
+ ],
16260
+ "description": "Build the inner driver, after validating the agents' providers, so a bad config throws before\na driver exists to subscribe to the registry. Static because it runs before `super()`."
15079
16261
  },
15080
16262
  {
15081
16263
  "kind": "method",
15082
- "name": "recordExternalCost",
16264
+ "name": "getSuggestions",
15083
16265
  "return": {
15084
16266
  "type": {
15085
- "text": "boolean"
16267
+ "text": "Promise<string[]>"
15086
16268
  }
15087
16269
  },
15088
16270
  "parameters": [
15089
16271
  {
15090
- "name": "interactionId",
16272
+ "name": "history",
16273
+ "type": {
16274
+ "text": "ChatMessage[]"
16275
+ }
16276
+ },
16277
+ {
16278
+ "name": "prompt",
15091
16279
  "type": {
15092
16280
  "text": "string"
15093
16281
  }
15094
16282
  },
15095
16283
  {
15096
- "name": "costUsd",
16284
+ "name": "count",
15097
16285
  "type": {
15098
16286
  "text": "number"
15099
16287
  }
16288
+ },
16289
+ {
16290
+ "name": "allAgentInfo",
16291
+ "optional": true,
16292
+ "type": {
16293
+ "text": "AllAgentSummary[]"
16294
+ }
15100
16295
  }
15101
16296
  ],
15102
- "description": "Delegated for the same reason as `resolveInteraction`: the history that owns the\ninteraction — and therefore the cost — lives on the wrapped driver, not here."
16297
+ "inheritedFrom": {
16298
+ "name": "BaseDriverDecorator",
16299
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16300
+ }
15103
16301
  },
15104
16302
  {
15105
16303
  "kind": "method",
15106
- "name": "getInteractionContext",
16304
+ "name": "sendMessage",
15107
16305
  "return": {
15108
16306
  "type": {
15109
- "text": "InteractionContext | undefined"
16307
+ "text": "Promise<ChatDriverResult>"
15110
16308
  }
15111
16309
  },
15112
16310
  "parameters": [
15113
16311
  {
15114
- "name": "interactionId",
16312
+ "name": "input",
15115
16313
  "type": {
15116
16314
  "text": "string"
15117
16315
  }
16316
+ },
16317
+ {
16318
+ "name": "attachments",
16319
+ "optional": true,
16320
+ "type": {
16321
+ "text": "ChatAttachment[]"
16322
+ }
16323
+ },
16324
+ {
16325
+ "name": "options",
16326
+ "optional": true,
16327
+ "type": {
16328
+ "text": "SendMessageOptions"
16329
+ }
15118
16330
  }
15119
- ]
15120
- },
15121
- {
15122
- "kind": "method",
15123
- "name": "isBusy",
15124
- "return": {
15125
- "type": {
15126
- "text": "boolean"
15127
- }
16331
+ ],
16332
+ "inheritedFrom": {
16333
+ "name": "BaseDriverDecorator",
16334
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
15128
16335
  }
15129
16336
  },
15130
16337
  {
15131
16338
  "kind": "method",
15132
- "name": "getActiveProviderName",
16339
+ "name": "runOrchestratedTurn",
16340
+ "privacy": "private",
15133
16341
  "return": {
15134
16342
  "type": {
15135
- "text": "string"
16343
+ "text": "Promise<ChatDriverResult>"
15136
16344
  }
15137
16345
  },
15138
- "description": "Currently active provider name from the underlying ChatDriver."
16346
+ "parameters": [
16347
+ {
16348
+ "name": "input",
16349
+ "type": {
16350
+ "text": "string"
16351
+ }
16352
+ },
16353
+ {
16354
+ "name": "attachments",
16355
+ "type": {
16356
+ "text": "ChatAttachment[] | undefined"
16357
+ }
16358
+ },
16359
+ {
16360
+ "name": "options",
16361
+ "type": {
16362
+ "text": "SendMessageOptions | undefined"
16363
+ }
16364
+ },
16365
+ {
16366
+ "name": "delegated",
16367
+ "type": {
16368
+ "text": "Pick<ChatMessage, 'delegate' | 'delegateFraming'>"
16369
+ }
16370
+ }
16371
+ ]
15139
16372
  },
15140
16373
  {
15141
16374
  "kind": "method",
15142
- "name": "setPinnedAgent",
16375
+ "name": "canRestoreFlowOwner",
15143
16376
  "return": {
15144
16377
  "type": {
15145
- "text": "void"
16378
+ "text": "boolean"
15146
16379
  }
15147
16380
  },
15148
16381
  "parameters": [
15149
16382
  {
15150
- "name": "name",
16383
+ "name": "agentName",
15151
16384
  "type": {
15152
- "text": "string | null"
16385
+ "text": "string"
15153
16386
  }
15154
16387
  }
15155
16388
  ],
15156
- "description": "Pins routing to a specific agent by name. While pinned, the classifier is\nskipped and the continuation tool is hidden from the agent's tool list, so\nthe agent cannot quietly hand back to the orchestrator. Pass `null` to\nreturn to automatic routing."
16389
+ "description": "{@inheritDoc AiDriver.canRestoreFlowOwner}",
16390
+ "inheritedFrom": {
16391
+ "name": "BaseDriverDecorator",
16392
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16393
+ }
15157
16394
  },
15158
16395
  {
15159
16396
  "kind": "method",
15160
- "name": "setFlowOwner",
16397
+ "name": "canResumeRestoredFlow",
15161
16398
  "return": {
15162
16399
  "type": {
15163
- "text": "void"
16400
+ "text": "boolean"
15164
16401
  }
15165
16402
  },
15166
16403
  "parameters": [
15167
16404
  {
15168
- "name": "name",
16405
+ "name": "agentName",
15169
16406
  "type": {
15170
- "text": "string | null"
16407
+ "text": "string"
15171
16408
  }
15172
16409
  }
15173
16410
  ],
15174
- "description": "Set the flow-owner lock — used to restore state from Redux on driver\nreconstruction (agents-array changes, pop-in/pop-out). Not intended for\nexternal callers to manipulate routing; that's what `setPinnedAgent` is\nfor."
16411
+ "description": "{@inheritDoc AiDriver.canResumeRestoredFlow}",
16412
+ "inheritedFrom": {
16413
+ "name": "BaseDriverDecorator",
16414
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16415
+ }
15175
16416
  },
15176
16417
  {
15177
16418
  "kind": "method",
15178
- "name": "loadHistory",
16419
+ "name": "resumeRestoredFlow",
15179
16420
  "return": {
15180
16421
  "type": {
15181
- "text": "void"
16422
+ "text": "Promise<ChatDriverResult | undefined>"
15182
16423
  }
15183
16424
  },
15184
16425
  "parameters": [
15185
16426
  {
15186
- "name": "messages",
16427
+ "name": "resumeNote",
16428
+ "optional": true,
15187
16429
  "type": {
15188
- "text": "ChatMessage[]"
16430
+ "text": "string"
15189
16431
  }
15190
16432
  }
15191
- ]
16433
+ ],
16434
+ "description": "{@inheritDoc AiDriver.resumeRestoredFlow}\n\nInstalls the flow owner directly rather than through `handleTurn`: there is no user message\nto classify, and a restored lock already says which agent owns the flow. `continueFromHistory`\non its own would not do — it neither routes nor installs, so it would run on whatever agent\nhappened to be installed (none, straight after a reload).\n\nLooks the owner up in its own agents list, so the `agent` argument is not needed and is\nignored; and does not delegate to the inner driver's resume, which would install the agent\nundecorated.",
16435
+ "inheritedFrom": {
16436
+ "name": "BaseDriverDecorator",
16437
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16438
+ }
15192
16439
  },
15193
16440
  {
15194
16441
  "kind": "method",
15195
- "name": "getRawHistory",
16442
+ "name": "getContextReserve",
15196
16443
  "return": {
15197
16444
  "type": {
15198
- "text": "readonly ChatMessage[]"
16445
+ "text": "ContextReserveResolution | undefined"
16446
+ }
16447
+ },
16448
+ "description": "{@inheritDoc AiDriver.getContextReserve}\n\nTwo cases, because routing has two modes and they need opposite answers.",
16449
+ "inheritedFrom": {
16450
+ "name": "BaseDriverDecorator",
16451
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16452
+ }
16453
+ },
16454
+ {
16455
+ "kind": "method",
16456
+ "name": "installAgent",
16457
+ "privacy": "private",
16458
+ "return": {
16459
+ "type": {
16460
+ "text": "Promise<void>"
16461
+ }
16462
+ },
16463
+ "parameters": [
16464
+ {
16465
+ "name": "agent",
16466
+ "type": {
16467
+ "text": "AgentConfig"
16468
+ }
15199
16469
  }
15200
- }
16470
+ ]
15201
16471
  },
15202
16472
  {
15203
16473
  "kind": "method",
15204
- "name": "getHistoryForPersistence",
16474
+ "name": "releaseActiveAgent",
16475
+ "privacy": "private",
15205
16476
  "return": {
15206
16477
  "type": {
15207
- "text": "ChatMessage[]"
16478
+ "text": "Promise<void>"
15208
16479
  }
15209
16480
  },
15210
- "description": "Delegates to the inner ChatDriver — condensation policies are registered there, so it\nis the only object that knows which payloads the model has stopped seeing (GENC-1566).\n\n**This forward is the whole feature, for any multi-agent host.** `AiDriver` declares\n`getHistoryForPersistence` optional, and `SessionPersister` calls it through `?.`, falling back\nto OrchestratingDriver.getRawHistory. This class does NOT extend `ChatDriver` — it\nwraps one — so without an explicit forward the optional call resolves to `undefined` and every\nhost on an orchestrating driver silently keeps persisting the full payloads. Measured exactly\nthat way on a Create run: 10 `context.condensed` events fired, and the session still stored\nthe two largest `read_context` results at 26,907 and 23,060 chars. The method existed, the\ntests passed, and the feature did nothing."
16481
+ "description": "Release the current stateful agent: fire `onDeactivate` and clear the flow-owner lock. The\ninner driver raises the events from the session's transition, so the host (and Redux) reflect\nthe unlocked state (GENC-1485). Called automatically when a tool handler invokes\n`context.releaseAgent`.\n\nThe user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user\nwho explicitly picked this agent should stay on it after the flow ends,\nso the next message starts a fresh flow on the same agent rather than\nrouting through the classifier."
15211
16482
  },
15212
16483
  {
15213
16484
  "kind": "method",
15214
- "name": "getCondensePoliciesForPersistence",
16485
+ "name": "cancel",
15215
16486
  "return": {
15216
16487
  "type": {
15217
- "text": "PersistedCondensePolicy[]"
16488
+ "text": "void"
15218
16489
  }
15219
16490
  },
15220
- "parameters": [
15221
- {
15222
- "name": "messages",
15223
- "type": {
15224
- "text": "readonly ChatMessage[]"
15225
- }
15226
- }
15227
- ],
15228
- "description": "Delegates to the inner ChatDriver, which owns the condensation registry and clocks\n(GENC-1632). The forward matters for the same reason as `getHistoryForPersistence` above: the\npersister reaches it through `?.`, so a missing forward would compile, pass the ChatDriver\ntests, and do nothing for every orchestrated host — which is every host with an agents array."
16491
+ "description": "Stop the current turn (user \"stop\" button). Cancels the inner chat driver's\nin-flight request and breaks the handoff loop so no further agent turn or\nclassify runs. The driver stays usable for the next message.",
16492
+ "inheritedFrom": {
16493
+ "name": "BaseDriverDecorator",
16494
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16495
+ }
15229
16496
  },
15230
16497
  {
15231
16498
  "kind": "method",
15232
- "name": "restoreCondensePolicies",
16499
+ "name": "dispose",
15233
16500
  "return": {
15234
16501
  "type": {
15235
- "text": "{\n restored: number;\n awaitingReactivation: number;\n }"
16502
+ "text": "Promise<void>"
15236
16503
  }
15237
16504
  },
15238
- "parameters": [
15239
- {
15240
- "name": "entries",
15241
- "type": {
15242
- "text": "readonly unknown[] | undefined"
15243
- }
15244
- }
15245
- ],
15246
- "description": "Delegates to the inner ChatDriver (GENC-1632). The deferred activation stamp it\nperforms is taken by the inner driver's `installAgent`, which this driver's own\n`installAgent` always reaches, so nothing extra is needed here."
16505
+ "description": "Fire `onDeactivate` on the current active agent and abort any pending\nlifecycle work. Called by the host on session teardown so machines can\nrelease resources cleanly.",
16506
+ "inheritedFrom": {
16507
+ "name": "BaseDriverDecorator",
16508
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16509
+ }
15247
16510
  },
15248
16511
  {
15249
16512
  "kind": "method",
15250
- "name": "setContextGuard",
16513
+ "name": "recordRouting",
16514
+ "privacy": "private",
15251
16515
  "return": {
15252
16516
  "type": {
15253
16517
  "text": "void"
@@ -15255,515 +16519,709 @@
15255
16519
  },
15256
16520
  "parameters": [
15257
16521
  {
15258
- "name": "policy",
15259
- "optional": true,
16522
+ "name": "decision",
15260
16523
  "type": {
15261
- "text": "ContextGuardPolicy"
16524
+ "text": "RoutingDecision"
16525
+ }
16526
+ },
16527
+ {
16528
+ "name": "agent",
16529
+ "type": {
16530
+ "text": "AgentConfig"
16531
+ }
16532
+ },
16533
+ {
16534
+ "name": "detail",
16535
+ "default": "{}",
16536
+ "type": {
16537
+ "text": "{\n index?: number;\n durationMs?: number;\n attempts?: number;\n error?: string;\n response?: ChatMessage;\n excluded?: readonly string[];\n /**\n * What was actually on the ballot for this classification, in ballot order — so `index`\n * indexes it. Omitted on the decisions that hold no vote at all.\n */\n ballot?: readonly string[];\n }"
15262
16538
  }
15263
16539
  }
15264
16540
  ],
15265
- "description": "{@inheritDoc AiDriver.setContextGuard}"
16541
+ "description": "Record one routing decision on the debug timeline — what was chosen, why, what it cost, and\n(on a failure) what went wrong.\n\nThis is the event whose absence made GENC-1573 undiagnosable. `classify` reaches the fallback\nby four different routes and every one of them only warned to the console; because it runs\noutside any ChatDriver turn it produces no `kind:'turn'` entry either, so both the decision\nand its spend were invisible in an exported log.\n\n`usage` is why the spend line exists at all. A classifier call is a real billed request that\nnever enters the transcript, and `sessionUsage` is derived from the transcript — so classifier\nspend is absent from the session total by construction. Recording it per decision keeps it\nauditable and tells a reconciler exactly where the gap comes from: sum these to close it."
15266
16542
  },
15267
16543
  {
15268
16544
  "kind": "method",
15269
- "name": "compact",
16545
+ "name": "classifierTranscript",
16546
+ "privacy": "private",
15270
16547
  "return": {
15271
16548
  "type": {
15272
- "text": "Promise<ChatMessage | null>"
16549
+ "text": "string"
15273
16550
  }
15274
16551
  },
15275
16552
  "parameters": [
15276
16553
  {
15277
- "name": "options",
15278
- "optional": true,
16554
+ "name": "history",
15279
16555
  "type": {
15280
- "text": "CompactionPlanOptions"
16556
+ "text": "ChatMessage[]"
15281
16557
  }
15282
16558
  }
15283
16559
  ],
15284
- "description": "{@inheritDoc AiDriver.compact}"
16560
+ "description": "The recent-conversation excerpt for the classifier prompt, bounded in size as well as in\nmessage count — see the `CLASSIFIER_MESSAGE_CHARS` docblock for why."
15285
16561
  },
15286
16562
  {
15287
16563
  "kind": "method",
15288
- "name": "canCompact",
16564
+ "name": "classify",
16565
+ "privacy": "private",
15289
16566
  "return": {
15290
16567
  "type": {
15291
- "text": "boolean"
16568
+ "text": "Promise<RoutingOutcome>"
15292
16569
  }
15293
16570
  },
15294
16571
  "parameters": [
15295
16572
  {
15296
- "name": "options",
16573
+ "name": "input",
16574
+ "type": {
16575
+ "text": "string"
16576
+ }
16577
+ },
16578
+ {
16579
+ "name": "history",
16580
+ "type": {
16581
+ "text": "ChatMessage[]"
16582
+ }
16583
+ },
16584
+ {
16585
+ "name": "contextAgent",
15297
16586
  "optional": true,
15298
16587
  "type": {
15299
- "text": "CompactionPlanOptions"
16588
+ "text": "AgentConfig"
16589
+ }
16590
+ },
16591
+ {
16592
+ "name": "excludeAgents",
16593
+ "default": "[]",
16594
+ "type": {
16595
+ "text": "readonly string[]"
15300
16596
  }
15301
16597
  }
15302
- ],
15303
- "description": "{@inheritDoc AiDriver.canCompact}"
16598
+ ]
15304
16599
  },
15305
16600
  {
15306
16601
  "kind": "method",
15307
- "name": "getPreambleTokens",
16602
+ "name": "resolvePinnedAgent",
16603
+ "privacy": "private",
15308
16604
  "return": {
15309
16605
  "type": {
15310
- "text": "number | undefined"
16606
+ "text": "AgentConfig | undefined"
15311
16607
  }
15312
16608
  },
15313
- "description": "{@inheritDoc AiDriver.getPreambleTokens}"
16609
+ "description": "Returns the agent that the current turn is locked to, if any. Flow-owner\n(transient, set by stateful-agent activation) wins over user-pin (sticky,\nset by picker click). `undefined` falls back to the classifier.\n\nLogs and returns `undefined` if a name no longer matches the agents array\n— protects against stale state after an agents-array reassignment."
15314
16610
  },
15315
16611
  {
15316
16612
  "kind": "method",
15317
- "name": "getCompactionPlan",
16613
+ "name": "appendInlineMessage",
16614
+ "privacy": "private",
15318
16615
  "return": {
15319
16616
  "type": {
15320
- "text": "CompactionProjection | null"
16617
+ "text": "void"
15321
16618
  }
15322
16619
  },
15323
16620
  "parameters": [
15324
16621
  {
15325
- "name": "options",
15326
- "optional": true,
16622
+ "name": "content",
15327
16623
  "type": {
15328
- "text": "CompactionPlanOptions"
16624
+ "text": "string"
15329
16625
  }
15330
16626
  }
15331
16627
  ],
15332
- "description": "{@inheritDoc AiDriver.getCompactionPlan}"
16628
+ "description": "Append prose the ORCHESTRATOR wrote — the handoff cap, the no-specialist apology. Flagged\n`excludeFromProvider` for the same reason the driver's own notices are: it is not something a\nmodel said, and feeding it back invites the model to echo it. See\n`ChatMessage.excludeFromProvider`."
16629
+ },
16630
+ {
16631
+ "kind": "field",
16632
+ "name": "detachers",
16633
+ "type": {
16634
+ "text": "Array<() => void>"
16635
+ },
16636
+ "privacy": "private",
16637
+ "readonly": true,
16638
+ "default": "[]",
16639
+ "description": "Undoes this layer's hold on its inner driver; see BaseDriverDecorator.detach.",
16640
+ "inheritedFrom": {
16641
+ "name": "BaseDriverDecorator",
16642
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16643
+ }
15333
16644
  },
15334
16645
  {
15335
16646
  "kind": "method",
15336
- "name": "getTurnSnapshots",
16647
+ "name": "detach",
15337
16648
  "return": {
15338
16649
  "type": {
15339
- "text": "ReadonlyArray<TurnSnapshot>"
16650
+ "text": "void"
15340
16651
  }
15341
16652
  },
15342
- "description": "Delegates to the inner ChatDriver — turns are captured there."
16653
+ "description": "Let go of the inner driver WITHOUT disposing it: stop forwarding its events, and undo whatever\na subclass registered through BaseDriverDecorator.onDetach (its interceptors, its\ncapabilities, its own listeners). For dropping or replacing a decorator while the driver it\nwrapped lives on — otherwise the old layer keeps re-dispatching every event and is never\ncollected. Idempotent. `dispose()` detaches too.",
16654
+ "inheritedFrom": {
16655
+ "name": "BaseDriverDecorator",
16656
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16657
+ }
15343
16658
  },
15344
16659
  {
15345
16660
  "kind": "method",
15346
- "name": "getExternalDiagnostics",
16661
+ "name": "onDetach",
16662
+ "privacy": "protected",
15347
16663
  "return": {
15348
16664
  "type": {
15349
- "text": "ReadonlyArray<DiagnosticEntry>"
16665
+ "text": "void"
15350
16666
  }
15351
16667
  },
15352
- "description": "Delegates to the inner ChatDriver — interactions resolve there, so it holds the buffer."
16668
+ "parameters": [
16669
+ {
16670
+ "name": "undo",
16671
+ "type": {
16672
+ "text": "() => void"
16673
+ }
16674
+ }
16675
+ ],
16676
+ "description": "Register something BaseDriverDecorator.detach must undo.",
16677
+ "inheritedFrom": {
16678
+ "name": "BaseDriverDecorator",
16679
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16680
+ }
15353
16681
  },
15354
16682
  {
15355
16683
  "kind": "method",
15356
- "name": "getSuggestions",
16684
+ "name": "continueFromHistory",
15357
16685
  "return": {
15358
16686
  "type": {
15359
- "text": "Promise<string[]>"
16687
+ "text": "Promise<ChatDriverResult>"
15360
16688
  }
15361
16689
  },
15362
16690
  "parameters": [
15363
16691
  {
15364
- "name": "history",
16692
+ "name": "transientPrimer",
16693
+ "optional": true,
15365
16694
  "type": {
15366
16695
  "text": "ChatMessage[]"
15367
16696
  }
15368
16697
  },
15369
16698
  {
15370
- "name": "prompt",
16699
+ "name": "options",
16700
+ "optional": true,
15371
16701
  "type": {
15372
- "text": "string"
16702
+ "text": "ContinueFromHistoryOptions"
15373
16703
  }
15374
- },
16704
+ }
16705
+ ],
16706
+ "inheritedFrom": {
16707
+ "name": "BaseDriverDecorator",
16708
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16709
+ }
16710
+ },
16711
+ {
16712
+ "kind": "method",
16713
+ "name": "resolveInteraction",
16714
+ "return": {
16715
+ "type": {
16716
+ "text": "void"
16717
+ }
16718
+ },
16719
+ "parameters": [
15375
16720
  {
15376
- "name": "count",
16721
+ "name": "interactionId",
15377
16722
  "type": {
15378
- "text": "number"
16723
+ "text": "string"
15379
16724
  }
15380
16725
  },
15381
16726
  {
15382
- "name": "allAgentInfo",
15383
- "optional": true,
16727
+ "name": "result",
15384
16728
  "type": {
15385
- "text": "AllAgentSummary[]"
16729
+ "text": "unknown"
15386
16730
  }
15387
16731
  }
15388
- ]
16732
+ ],
16733
+ "inheritedFrom": {
16734
+ "name": "BaseDriverDecorator",
16735
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16736
+ }
15389
16737
  },
15390
16738
  {
15391
16739
  "kind": "method",
15392
- "name": "sendMessage",
16740
+ "name": "recordExternalCost",
15393
16741
  "return": {
15394
16742
  "type": {
15395
- "text": "Promise<ChatDriverResult>"
16743
+ "text": "boolean"
15396
16744
  }
15397
16745
  },
15398
16746
  "parameters": [
15399
16747
  {
15400
- "name": "input",
16748
+ "name": "interactionId",
15401
16749
  "type": {
15402
16750
  "text": "string"
15403
16751
  }
15404
16752
  },
15405
16753
  {
15406
- "name": "attachments",
15407
- "optional": true,
16754
+ "name": "costUsd",
15408
16755
  "type": {
15409
- "text": "ChatAttachment[]"
16756
+ "text": "number"
15410
16757
  }
15411
16758
  }
15412
- ]
16759
+ ],
16760
+ "inheritedFrom": {
16761
+ "name": "BaseDriverDecorator",
16762
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16763
+ }
15413
16764
  },
15414
16765
  {
15415
16766
  "kind": "method",
15416
- "name": "runOrchestratedTurn",
15417
- "privacy": "private",
16767
+ "name": "getInteractionContext",
15418
16768
  "return": {
15419
16769
  "type": {
15420
- "text": "Promise<ChatDriverResult>"
16770
+ "text": "InteractionContext | undefined"
15421
16771
  }
15422
16772
  },
15423
16773
  "parameters": [
15424
16774
  {
15425
- "name": "input",
16775
+ "name": "interactionId",
15426
16776
  "type": {
15427
16777
  "text": "string"
15428
16778
  }
15429
- },
15430
- {
15431
- "name": "attachments",
15432
- "optional": true,
15433
- "type": {
15434
- "text": "ChatAttachment[]"
15435
- }
15436
16779
  }
15437
- ]
16780
+ ],
16781
+ "inheritedFrom": {
16782
+ "name": "BaseDriverDecorator",
16783
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16784
+ }
15438
16785
  },
15439
16786
  {
15440
16787
  "kind": "method",
15441
- "name": "continueFromHistory",
16788
+ "name": "loadHistory",
15442
16789
  "return": {
15443
16790
  "type": {
15444
- "text": "Promise<ChatDriverResult>"
16791
+ "text": "void"
15445
16792
  }
15446
16793
  },
15447
16794
  "parameters": [
15448
16795
  {
15449
- "name": "transientPrimer",
15450
- "optional": true,
16796
+ "name": "messages",
15451
16797
  "type": {
15452
16798
  "text": "ChatMessage[]"
15453
16799
  }
15454
- },
16800
+ }
16801
+ ],
16802
+ "inheritedFrom": {
16803
+ "name": "BaseDriverDecorator",
16804
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16805
+ }
16806
+ },
16807
+ {
16808
+ "kind": "method",
16809
+ "name": "primeRestoredAgentState",
16810
+ "return": {
16811
+ "type": {
16812
+ "text": "void"
16813
+ }
16814
+ },
16815
+ "parameters": [
15455
16816
  {
15456
- "name": "options",
15457
- "optional": true,
16817
+ "name": "snapshots",
15458
16818
  "type": {
15459
- "text": "ContinueFromHistoryOptions"
16819
+ "text": "Record<string, unknown>"
15460
16820
  }
15461
16821
  }
15462
- ]
16822
+ ],
16823
+ "inheritedFrom": {
16824
+ "name": "BaseDriverDecorator",
16825
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16826
+ }
16827
+ },
16828
+ {
16829
+ "kind": "method",
16830
+ "name": "getRawHistory",
16831
+ "return": {
16832
+ "type": {
16833
+ "text": "readonly ChatMessage[]"
16834
+ }
16835
+ },
16836
+ "inheritedFrom": {
16837
+ "name": "BaseDriverDecorator",
16838
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16839
+ }
16840
+ },
16841
+ {
16842
+ "kind": "method",
16843
+ "name": "getHistoryForPersistence",
16844
+ "return": {
16845
+ "type": {
16846
+ "text": "ChatMessage[]"
16847
+ }
16848
+ },
16849
+ "inheritedFrom": {
16850
+ "name": "BaseDriverDecorator",
16851
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16852
+ }
15463
16853
  },
15464
16854
  {
15465
16855
  "kind": "method",
15466
- "name": "primeRestoredAgentState",
16856
+ "name": "getCondensePoliciesForPersistence",
15467
16857
  "return": {
15468
16858
  "type": {
15469
- "text": "void"
16859
+ "text": "PersistedCondensePolicy[]"
15470
16860
  }
15471
16861
  },
15472
16862
  "parameters": [
15473
16863
  {
15474
- "name": "snapshots",
16864
+ "name": "messages",
15475
16865
  "type": {
15476
- "text": "Record<string, unknown>"
16866
+ "text": "readonly ChatMessage[]"
15477
16867
  }
15478
16868
  }
15479
16869
  ],
15480
- "description": "{@inheritDoc AiDriver.primeRestoredAgentState}"
16870
+ "inheritedFrom": {
16871
+ "name": "BaseDriverDecorator",
16872
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16873
+ }
15481
16874
  },
15482
16875
  {
15483
16876
  "kind": "method",
15484
- "name": "canRestoreFlowOwner",
16877
+ "name": "restoreCondensePolicies",
15485
16878
  "return": {
15486
16879
  "type": {
15487
- "text": "boolean"
16880
+ "text": "{\n restored: number;\n awaitingReactivation: number;\n }"
15488
16881
  }
15489
16882
  },
15490
16883
  "parameters": [
15491
16884
  {
15492
- "name": "agentName",
16885
+ "name": "entries",
15493
16886
  "type": {
15494
- "text": "string"
16887
+ "text": "readonly unknown[] | undefined"
15495
16888
  }
15496
16889
  }
15497
16890
  ],
15498
- "description": "{@inheritDoc AiDriver.canRestoreFlowOwner}"
16891
+ "inheritedFrom": {
16892
+ "name": "BaseDriverDecorator",
16893
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16894
+ }
15499
16895
  },
15500
16896
  {
15501
16897
  "kind": "method",
15502
- "name": "canResumeRestoredFlow",
16898
+ "name": "getActiveAgent",
15503
16899
  "return": {
15504
16900
  "type": {
15505
- "text": "boolean"
16901
+ "text": "AgentConfig | undefined"
16902
+ }
16903
+ },
16904
+ "inheritedFrom": {
16905
+ "name": "BaseDriverDecorator",
16906
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16907
+ }
16908
+ },
16909
+ {
16910
+ "kind": "method",
16911
+ "name": "setPinnedAgent",
16912
+ "return": {
16913
+ "type": {
16914
+ "text": "void"
15506
16915
  }
15507
16916
  },
15508
16917
  "parameters": [
15509
16918
  {
15510
- "name": "agentName",
16919
+ "name": "name",
15511
16920
  "type": {
15512
- "text": "string"
16921
+ "text": "string | null"
15513
16922
  }
15514
16923
  }
15515
16924
  ],
15516
- "description": "{@inheritDoc AiDriver.canResumeRestoredFlow}"
16925
+ "inheritedFrom": {
16926
+ "name": "BaseDriverDecorator",
16927
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16928
+ }
15517
16929
  },
15518
16930
  {
15519
16931
  "kind": "method",
15520
- "name": "resumeRestoredFlow",
16932
+ "name": "setFlowOwner",
15521
16933
  "return": {
15522
16934
  "type": {
15523
- "text": "Promise<ChatDriverResult | undefined>"
16935
+ "text": "void"
15524
16936
  }
15525
16937
  },
15526
16938
  "parameters": [
15527
16939
  {
15528
- "name": "resumeNote",
15529
- "optional": true,
16940
+ "name": "name",
15530
16941
  "type": {
15531
- "text": "string"
16942
+ "text": "string | null"
15532
16943
  }
15533
16944
  }
15534
16945
  ],
15535
- "description": "{@inheritDoc AiDriver.resumeRestoredFlow}\n\nInstalls the flow owner directly rather than through `handleTurn`: there is no user message\nto classify, and a restored lock already says which agent owns the flow. `continueFromHistory`\non its own would not do — it neither routes nor installs, so it would run on whatever agent\nhappened to be installed (none, straight after a reload)."
16946
+ "inheritedFrom": {
16947
+ "name": "BaseDriverDecorator",
16948
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16949
+ }
15536
16950
  },
15537
16951
  {
15538
16952
  "kind": "method",
15539
- "name": "getContextReserve",
16953
+ "name": "getFlowOwner",
15540
16954
  "return": {
15541
16955
  "type": {
15542
- "text": "ContextReserveResolution | undefined"
16956
+ "text": "string | null"
15543
16957
  }
15544
16958
  },
15545
- "description": "{@inheritDoc AiDriver.getContextReserve}\n\nTwo cases, because routing has two modes and they need opposite answers."
16959
+ "inheritedFrom": {
16960
+ "name": "BaseDriverDecorator",
16961
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16962
+ }
15546
16963
  },
15547
16964
  {
15548
16965
  "kind": "method",
15549
- "name": "getActiveAgent",
16966
+ "name": "addInteractionInterceptor",
15550
16967
  "return": {
15551
16968
  "type": {
15552
- "text": "AgentConfig | undefined"
16969
+ "text": "() => void"
15553
16970
  }
15554
16971
  },
15555
- "description": "{@inheritDoc AiDriver.getActiveAgent}"
16972
+ "parameters": [
16973
+ {
16974
+ "name": "interceptor",
16975
+ "type": {
16976
+ "text": "InteractionInterceptor"
16977
+ }
16978
+ }
16979
+ ],
16980
+ "description": "Forwarded, so an interceptor installed on any layer lands on the driver that opens the\ninteractions. A decorator that answers interactions installs its own on `this.inner`.",
16981
+ "inheritedFrom": {
16982
+ "name": "BaseDriverDecorator",
16983
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
16984
+ }
15556
16985
  },
15557
16986
  {
15558
16987
  "kind": "method",
15559
- "name": "installAgent",
15560
- "privacy": "private",
16988
+ "name": "presentInteraction",
15561
16989
  "return": {
15562
16990
  "type": {
15563
- "text": "Promise<void>"
16991
+ "text": "Promise<T>"
15564
16992
  }
15565
16993
  },
15566
16994
  "parameters": [
15567
16995
  {
15568
- "name": "agent",
16996
+ "name": "componentName",
15569
16997
  "type": {
15570
- "text": "AgentConfig"
16998
+ "text": "string"
16999
+ }
17000
+ },
17001
+ {
17002
+ "name": "data",
17003
+ "type": {
17004
+ "text": "unknown"
17005
+ }
17006
+ },
17007
+ {
17008
+ "name": "options",
17009
+ "optional": true,
17010
+ "type": {
17011
+ "text": "InteractionRequestOptions"
15571
17012
  }
15572
17013
  }
15573
- ]
17014
+ ],
17015
+ "inheritedFrom": {
17016
+ "name": "BaseDriverDecorator",
17017
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17018
+ }
15574
17019
  },
15575
17020
  {
15576
17021
  "kind": "method",
15577
- "name": "releaseActiveAgent",
15578
- "privacy": "private",
17022
+ "name": "provideHandlerCapability",
15579
17023
  "return": {
15580
17024
  "type": {
15581
- "text": "Promise<void>"
17025
+ "text": "() => void"
15582
17026
  }
15583
17027
  },
15584
- "description": "Release the current stateful agent: fire `onDeactivate`, clear the\nflow-owner lock, dispatch events so the host (and Redux) reflect the\nunlocked state. Called automatically when a tool handler invokes\n`context.releaseAgent`.\n\nThe user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user\nwho explicitly picked this agent should stay on it after the flow ends,\nso the next message starts a fresh flow on the same agent rather than\nrouting through the classifier."
17028
+ "parameters": [
17029
+ {
17030
+ "name": "name",
17031
+ "type": {
17032
+ "text": "string"
17033
+ }
17034
+ },
17035
+ {
17036
+ "name": "factory",
17037
+ "type": {
17038
+ "text": "HandlerCapabilityFactory"
17039
+ }
17040
+ }
17041
+ ],
17042
+ "inheritedFrom": {
17043
+ "name": "BaseDriverDecorator",
17044
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17045
+ }
15585
17046
  },
15586
17047
  {
15587
17048
  "kind": "method",
15588
- "name": "cancel",
17049
+ "name": "getPreambleTokens",
15589
17050
  "return": {
15590
17051
  "type": {
15591
- "text": "void"
17052
+ "text": "number | undefined"
15592
17053
  }
15593
17054
  },
15594
- "description": "Stop the current turn (user \"stop\" button). Cancels the inner chat driver's\nin-flight request and breaks the handoff loop so no further agent turn or\nclassify runs. The driver stays usable for the next message."
17055
+ "inheritedFrom": {
17056
+ "name": "BaseDriverDecorator",
17057
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17058
+ }
15595
17059
  },
15596
17060
  {
15597
17061
  "kind": "method",
15598
- "name": "dispose",
17062
+ "name": "setContextGuard",
15599
17063
  "return": {
15600
17064
  "type": {
15601
- "text": "Promise<void>"
17065
+ "text": "void"
15602
17066
  }
15603
17067
  },
15604
- "description": "Fire `onDeactivate` on the current active agent and abort any pending\nlifecycle work. Called by the host on session teardown so machines can\nrelease resources cleanly."
17068
+ "parameters": [
17069
+ {
17070
+ "name": "policy",
17071
+ "optional": true,
17072
+ "type": {
17073
+ "text": "ContextGuardPolicy"
17074
+ }
17075
+ }
17076
+ ],
17077
+ "inheritedFrom": {
17078
+ "name": "BaseDriverDecorator",
17079
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17080
+ }
15605
17081
  },
15606
17082
  {
15607
17083
  "kind": "method",
15608
- "name": "recordRouting",
15609
- "privacy": "private",
17084
+ "name": "compact",
15610
17085
  "return": {
15611
17086
  "type": {
15612
- "text": "void"
17087
+ "text": "Promise<ChatMessage | null>"
15613
17088
  }
15614
17089
  },
15615
17090
  "parameters": [
15616
17091
  {
15617
- "name": "decision",
15618
- "type": {
15619
- "text": "RoutingDecision"
15620
- }
15621
- },
15622
- {
15623
- "name": "agent",
15624
- "type": {
15625
- "text": "AgentConfig"
15626
- }
15627
- },
15628
- {
15629
- "name": "detail",
15630
- "default": "{}",
17092
+ "name": "options",
17093
+ "optional": true,
15631
17094
  "type": {
15632
- "text": "{\n index?: number;\n durationMs?: number;\n attempts?: number;\n error?: string;\n response?: ChatMessage;\n excluded?: readonly string[];\n /**\n * What was actually on the ballot for this classification, in ballot order — so `index`\n * indexes it. Omitted on the decisions that hold no vote at all.\n */\n ballot?: readonly string[];\n }"
17095
+ "text": "CompactionPlanOptions"
15633
17096
  }
15634
17097
  }
15635
17098
  ],
15636
- "description": "Record one routing decision on the debug timeline — what was chosen, why, what it cost, and\n(on a failure) what went wrong.\n\nThis is the event whose absence made GENC-1573 undiagnosable. `classify` reaches the fallback\nby four different routes and every one of them only warned to the console; because it runs\noutside any ChatDriver turn it produces no `kind:'turn'` entry either, so both the decision\nand its spend were invisible in an exported log.\n\n`usage` is why the spend line exists at all. A classifier call is a real billed request that\nnever enters the transcript, and `sessionUsage` is derived from the transcript — so classifier\nspend is absent from the session total by construction. Recording it per decision keeps it\nauditable and tells a reconciler exactly where the gap comes from: sum these to close it."
17099
+ "inheritedFrom": {
17100
+ "name": "BaseDriverDecorator",
17101
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17102
+ }
15637
17103
  },
15638
17104
  {
15639
17105
  "kind": "method",
15640
- "name": "classifierTranscript",
15641
- "privacy": "private",
17106
+ "name": "getCompactionPlan",
15642
17107
  "return": {
15643
17108
  "type": {
15644
- "text": "string"
17109
+ "text": "CompactionProjection | null"
15645
17110
  }
15646
17111
  },
15647
17112
  "parameters": [
15648
17113
  {
15649
- "name": "history",
17114
+ "name": "options",
17115
+ "optional": true,
15650
17116
  "type": {
15651
- "text": "ChatMessage[]"
17117
+ "text": "CompactionPlanOptions"
15652
17118
  }
15653
17119
  }
15654
17120
  ],
15655
- "description": "The recent-conversation excerpt for the classifier prompt, bounded in size as well as in\nmessage count — see the `CLASSIFIER_MESSAGE_CHARS` docblock for why."
17121
+ "inheritedFrom": {
17122
+ "name": "BaseDriverDecorator",
17123
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17124
+ }
15656
17125
  },
15657
17126
  {
15658
17127
  "kind": "method",
15659
- "name": "classify",
15660
- "privacy": "private",
17128
+ "name": "canCompact",
15661
17129
  "return": {
15662
17130
  "type": {
15663
- "text": "Promise<RoutingOutcome>"
17131
+ "text": "boolean"
15664
17132
  }
15665
17133
  },
15666
17134
  "parameters": [
15667
17135
  {
15668
- "name": "input",
15669
- "type": {
15670
- "text": "string"
15671
- }
15672
- },
15673
- {
15674
- "name": "history",
15675
- "type": {
15676
- "text": "ChatMessage[]"
15677
- }
15678
- },
15679
- {
15680
- "name": "contextAgent",
17136
+ "name": "options",
15681
17137
  "optional": true,
15682
17138
  "type": {
15683
- "text": "AgentConfig"
15684
- }
15685
- },
15686
- {
15687
- "name": "excludeAgents",
15688
- "default": "[]",
15689
- "type": {
15690
- "text": "readonly string[]"
17139
+ "text": "CompactionPlanOptions"
15691
17140
  }
15692
17141
  }
15693
- ]
17142
+ ],
17143
+ "inheritedFrom": {
17144
+ "name": "BaseDriverDecorator",
17145
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17146
+ }
15694
17147
  },
15695
17148
  {
15696
17149
  "kind": "method",
15697
- "name": "resolvePinnedAgent",
15698
- "privacy": "private",
17150
+ "name": "isBusy",
15699
17151
  "return": {
15700
17152
  "type": {
15701
- "text": "AgentConfig | undefined"
17153
+ "text": "boolean"
15702
17154
  }
15703
17155
  },
15704
- "description": "Returns the agent that the current turn is locked to, if any. Flow-owner\n(transient, set by stateful-agent activation) wins over user-pin (sticky,\nset by picker click). `undefined` falls back to the classifier.\n\nLogs and returns `undefined` if a name no longer matches the agents array\n— protects against stale state after an agents-array reassignment."
17156
+ "inheritedFrom": {
17157
+ "name": "BaseDriverDecorator",
17158
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
17159
+ }
15705
17160
  },
15706
17161
  {
15707
17162
  "kind": "method",
15708
- "name": "appendInlineMessage",
15709
- "privacy": "private",
17163
+ "name": "getTurnSnapshots",
15710
17164
  "return": {
15711
17165
  "type": {
15712
- "text": "void"
17166
+ "text": "ReadonlyArray<TurnSnapshot>"
15713
17167
  }
15714
17168
  },
15715
- "parameters": [
15716
- {
15717
- "name": "content",
15718
- "type": {
15719
- "text": "string"
15720
- }
15721
- }
15722
- ],
15723
- "description": "Append prose the ORCHESTRATOR wrote — the handoff cap, the no-specialist apology. Flagged\n`excludeFromProvider` for the same reason the driver's own notices are: it is not something a\nmodel said, and feeding it back invites the model to echo it. See\n`ChatMessage.excludeFromProvider`."
15724
- }
15725
- ],
15726
- "events": [
15727
- {
15728
- "name": "history-updated",
15729
- "type": {
15730
- "text": "CustomEvent"
17169
+ "inheritedFrom": {
17170
+ "name": "BaseDriverDecorator",
17171
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
15731
17172
  }
15732
17173
  },
15733
17174
  {
15734
- "name": "orchestrating-start",
15735
- "type": {
15736
- "text": "CustomEvent"
17175
+ "kind": "method",
17176
+ "name": "getExternalDiagnostics",
17177
+ "return": {
17178
+ "type": {
17179
+ "text": "ReadonlyArray<DiagnosticEntry>"
17180
+ }
17181
+ },
17182
+ "description": "The inner driver's entries. Diagnostics compose down the stack: a decorator with entries of\nits own overrides this and appends them to `super.getExternalDiagnostics()`.",
17183
+ "inheritedFrom": {
17184
+ "name": "BaseDriverDecorator",
17185
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
15737
17186
  }
15738
17187
  },
15739
17188
  {
15740
- "name": "flow-owner-changed",
15741
- "type": {
15742
- "text": "CustomEvent"
17189
+ "kind": "method",
17190
+ "name": "getActiveProviderName",
17191
+ "return": {
17192
+ "type": {
17193
+ "text": "string"
17194
+ }
17195
+ },
17196
+ "inheritedFrom": {
17197
+ "name": "BaseDriverDecorator",
17198
+ "module": "src/components/ai-driver/base-driver-decorator.ts"
15743
17199
  }
15744
- },
17200
+ }
17201
+ ],
17202
+ "events": [
15745
17203
  {
15746
- "name": "orchestrating-stop",
17204
+ "name": "history-updated",
15747
17205
  "type": {
15748
17206
  "text": "CustomEvent"
15749
17207
  }
15750
17208
  },
15751
17209
  {
15752
- "name": "agent-changed",
17210
+ "name": "orchestrating-start",
15753
17211
  "type": {
15754
17212
  "text": "CustomEvent"
15755
17213
  }
15756
17214
  },
15757
17215
  {
15758
- "name": "agent-released",
17216
+ "name": "orchestrating-stop",
15759
17217
  "type": {
15760
17218
  "text": "CustomEvent"
15761
17219
  }
15762
17220
  }
15763
17221
  ],
15764
17222
  "superclass": {
15765
- "name": "EventTarget",
15766
- "module": "src/components/orchestrating-driver/orchestrating-driver.ts"
17223
+ "name": "BaseDriverDecorator",
17224
+ "module": "/src/components/ai-driver/base-driver-decorator"
15767
17225
  }
15768
17226
  }
15769
17227
  ],