@genesislcap/ai-assistant 14.490.0 → 14.491.0-FUI-2567.1

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 (171) hide show
  1. package/dist/ai-assistant.api.json +6737 -3591
  2. package/dist/ai-assistant.d.ts +879 -183
  3. package/dist/custom-elements.json +1799 -650
  4. package/dist/dts/components/ai-driver/ai-driver.d.ts +32 -8
  5. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  6. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts +2 -0
  7. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts.map +1 -0
  8. package/dist/dts/components/chat-driver/chat-driver.d.ts +13 -29
  9. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  10. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +0 -7
  11. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
  12. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +15 -4
  13. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  14. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts +2 -0
  15. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts.map +1 -0
  16. package/dist/dts/components/popout-manager/popout-manager.d.ts +37 -9
  17. package/dist/dts/components/popout-manager/popout-manager.d.ts.map +1 -1
  18. package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
  19. package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
  20. package/dist/dts/config/config.d.ts +70 -0
  21. package/dist/dts/config/config.d.ts.map +1 -1
  22. package/dist/dts/config/define-stateful-agent.d.ts +73 -1
  23. package/dist/dts/config/define-stateful-agent.d.ts.map +1 -1
  24. package/dist/dts/index.d.ts +1 -0
  25. package/dist/dts/index.d.ts.map +1 -1
  26. package/dist/dts/main/main.d.ts +337 -91
  27. package/dist/dts/main/main.d.ts.map +1 -1
  28. package/dist/dts/main/main.styles.d.ts.map +1 -1
  29. package/dist/dts/main/main.template.d.ts.map +1 -1
  30. package/dist/dts/main/main.types.d.ts +55 -9
  31. package/dist/dts/main/main.types.d.ts.map +1 -1
  32. package/dist/dts/main/popout-interaction-gate.test.d.ts +2 -0
  33. package/dist/dts/main/popout-interaction-gate.test.d.ts.map +1 -0
  34. package/dist/dts/react.d.ts +3 -2
  35. package/dist/dts/state/ai-assistant-slice.d.ts +73 -0
  36. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  37. package/dist/dts/state/ai-assistant-slice.test.d.ts +2 -0
  38. package/dist/dts/state/ai-assistant-slice.test.d.ts.map +1 -0
  39. package/dist/dts/state/debug-event-log.d.ts +1 -1
  40. package/dist/dts/state/debug-event-log.d.ts.map +1 -1
  41. package/dist/dts/state/persistence/diagnostics-cursors.d.ts +51 -0
  42. package/dist/dts/state/persistence/diagnostics-cursors.d.ts.map +1 -0
  43. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts +2 -0
  44. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts.map +1 -0
  45. package/dist/dts/state/persistence/diagnostics.d.ts +65 -0
  46. package/dist/dts/state/persistence/diagnostics.d.ts.map +1 -0
  47. package/dist/dts/state/persistence/diagnostics.test.d.ts +2 -0
  48. package/dist/dts/state/persistence/diagnostics.test.d.ts.map +1 -0
  49. package/dist/dts/state/persistence/index.d.ts +7 -0
  50. package/dist/dts/state/persistence/index.d.ts.map +1 -0
  51. package/dist/dts/state/persistence/persister-registry.d.ts +22 -0
  52. package/dist/dts/state/persistence/persister-registry.d.ts.map +1 -0
  53. package/dist/dts/state/persistence/session-persistence-provider.d.ts +132 -0
  54. package/dist/dts/state/persistence/session-persistence-provider.d.ts.map +1 -0
  55. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts +2 -0
  56. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts.map +1 -0
  57. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts +2 -0
  58. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts.map +1 -0
  59. package/dist/dts/state/persistence/session-persister.d.ts +173 -0
  60. package/dist/dts/state/persistence/session-persister.d.ts.map +1 -0
  61. package/dist/dts/state/persistence/session-persister.test.d.ts +2 -0
  62. package/dist/dts/state/persistence/session-persister.test.d.ts.map +1 -0
  63. package/dist/dts/state/persistence/session-snapshot.d.ts +218 -0
  64. package/dist/dts/state/persistence/session-snapshot.d.ts.map +1 -0
  65. package/dist/dts/state/persistence/session-snapshot.test.d.ts +2 -0
  66. package/dist/dts/state/persistence/session-snapshot.test.d.ts.map +1 -0
  67. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts +2 -0
  68. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts.map +1 -0
  69. package/dist/dts/state/session-store.d.ts +6 -0
  70. package/dist/dts/state/session-store.d.ts.map +1 -1
  71. package/dist/dts/utils/history-transform.d.ts +38 -0
  72. package/dist/dts/utils/history-transform.d.ts.map +1 -1
  73. package/dist/dts/utils/history-transform.test.d.ts +2 -0
  74. package/dist/dts/utils/history-transform.test.d.ts.map +1 -0
  75. package/dist/dts/utils/message-partition.d.ts +27 -0
  76. package/dist/dts/utils/message-partition.d.ts.map +1 -1
  77. package/dist/dts/utils/resolve-preference-baseline.d.ts +43 -0
  78. package/dist/dts/utils/resolve-preference-baseline.d.ts.map +1 -0
  79. package/dist/dts/utils/resolve-preference-baseline.test.d.ts +2 -0
  80. package/dist/dts/utils/resolve-preference-baseline.test.d.ts.map +1 -0
  81. package/dist/dts/utils/with-timeout.d.ts +12 -0
  82. package/dist/dts/utils/with-timeout.d.ts.map +1 -0
  83. package/dist/esm/components/chat-driver/chat-driver.compact.test.js +93 -0
  84. package/dist/esm/components/chat-driver/chat-driver.js +101 -65
  85. package/dist/esm/components/chat-driver/chat-driver.test.js +0 -45
  86. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +17 -35
  87. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +0 -62
  88. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +32 -14
  89. package/dist/esm/components/orchestrating-driver/orchestrating-driver.pin.test.js +61 -0
  90. package/dist/esm/components/popout-manager/popout-manager.js +76 -27
  91. package/dist/esm/components/settings-modal/settings-modal.styles.js +24 -0
  92. package/dist/esm/components/settings-modal/settings-modal.template.js +14 -1
  93. package/dist/esm/config/define-stateful-agent.js +81 -0
  94. package/dist/esm/index.js +1 -0
  95. package/dist/esm/main/main.js +929 -223
  96. package/dist/esm/main/main.styles.js +55 -0
  97. package/dist/esm/main/main.template.js +94 -10
  98. package/dist/esm/main/main.types.js +27 -0
  99. package/dist/esm/main/popout-interaction-gate.test.js +58 -0
  100. package/dist/esm/state/ai-assistant-slice.js +85 -25
  101. package/dist/esm/state/ai-assistant-slice.test.js +85 -0
  102. package/dist/esm/state/debug-event-log.js +10 -0
  103. package/dist/esm/state/persistence/diagnostics-cursors.js +47 -0
  104. package/dist/esm/state/persistence/diagnostics-cursors.test.js +30 -0
  105. package/dist/esm/state/persistence/diagnostics.js +81 -0
  106. package/dist/esm/state/persistence/diagnostics.test.js +72 -0
  107. package/dist/esm/state/persistence/index.js +3 -0
  108. package/dist/esm/state/persistence/persister-registry.js +48 -0
  109. package/dist/esm/state/persistence/session-persistence-provider.js +95 -0
  110. package/dist/esm/state/persistence/session-persistence-provider.test.js +110 -0
  111. package/dist/esm/state/persistence/session-persistence.integration.test.js +109 -0
  112. package/dist/esm/state/persistence/session-persister.js +642 -0
  113. package/dist/esm/state/persistence/session-persister.test.js +314 -0
  114. package/dist/esm/state/persistence/session-snapshot.js +240 -0
  115. package/dist/esm/state/persistence/session-snapshot.test.js +235 -0
  116. package/dist/esm/state/persistence/stateful-restore.e2e.test.js +149 -0
  117. package/dist/esm/utils/history-transform.js +101 -0
  118. package/dist/esm/utils/history-transform.test.js +137 -0
  119. package/dist/esm/utils/message-partition.js +30 -2
  120. package/dist/esm/utils/message-partition.test.js +20 -1
  121. package/dist/esm/utils/resolve-preference-baseline.js +53 -0
  122. package/dist/esm/utils/resolve-preference-baseline.test.js +69 -0
  123. package/dist/esm/utils/with-timeout.js +28 -0
  124. package/dist/react.cjs +6 -1
  125. package/dist/react.mjs +6 -1
  126. package/dist/tsconfig.tsbuildinfo +1 -1
  127. package/docs/migration-GENC-1351.md +172 -0
  128. package/package.json +17 -17
  129. package/src/components/ai-driver/ai-driver.ts +36 -17
  130. package/src/components/chat-driver/chat-driver.compact.test.ts +131 -0
  131. package/src/components/chat-driver/chat-driver.test.ts +0 -71
  132. package/src/components/chat-driver/chat-driver.ts +103 -70
  133. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +0 -81
  134. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +18 -41
  135. package/src/components/orchestrating-driver/orchestrating-driver.pin.test.ts +96 -0
  136. package/src/components/orchestrating-driver/orchestrating-driver.ts +36 -18
  137. package/src/components/popout-manager/popout-manager.ts +82 -28
  138. package/src/components/settings-modal/settings-modal.styles.ts +24 -0
  139. package/src/components/settings-modal/settings-modal.template.ts +27 -1
  140. package/src/config/config.ts +71 -0
  141. package/src/config/define-stateful-agent.ts +134 -0
  142. package/src/index.ts +1 -0
  143. package/src/main/main.styles.ts +55 -0
  144. package/src/main/main.template.ts +121 -11
  145. package/src/main/main.ts +937 -199
  146. package/src/main/main.types.ts +59 -10
  147. package/src/main/popout-interaction-gate.test.ts +92 -0
  148. package/src/state/ai-assistant-slice.test.ts +114 -0
  149. package/src/state/ai-assistant-slice.ts +134 -25
  150. package/src/state/debug-event-log.ts +22 -1
  151. package/src/state/persistence/diagnostics-cursors.test.ts +53 -0
  152. package/src/state/persistence/diagnostics-cursors.ts +80 -0
  153. package/src/state/persistence/diagnostics.test.ts +95 -0
  154. package/src/state/persistence/diagnostics.ts +110 -0
  155. package/src/state/persistence/index.ts +10 -0
  156. package/src/state/persistence/persister-registry.ts +55 -0
  157. package/src/state/persistence/session-persistence-provider.test.ts +136 -0
  158. package/src/state/persistence/session-persistence-provider.ts +202 -0
  159. package/src/state/persistence/session-persistence.integration.test.ts +134 -0
  160. package/src/state/persistence/session-persister.test.ts +428 -0
  161. package/src/state/persistence/session-persister.ts +689 -0
  162. package/src/state/persistence/session-snapshot.test.ts +327 -0
  163. package/src/state/persistence/session-snapshot.ts +340 -0
  164. package/src/state/persistence/stateful-restore.e2e.test.ts +221 -0
  165. package/src/utils/history-transform.test.ts +197 -0
  166. package/src/utils/history-transform.ts +117 -0
  167. package/src/utils/message-partition.test.ts +27 -1
  168. package/src/utils/message-partition.ts +41 -2
  169. package/src/utils/resolve-preference-baseline.test.ts +84 -0
  170. package/src/utils/resolve-preference-baseline.ts +80 -0
  171. package/src/utils/with-timeout.ts +24 -0
@@ -8,8 +8,8 @@ import type { ChatConfig } from '@genesislcap/foundation-ai';
8
8
  import type { ChatDriverResult } from '@genesislcap/foundation-ai';
9
9
  import type { ChatInputDuringExecutionMode } from '@genesislcap/foundation-ai';
10
10
  import type { ChatMessage } from '@genesislcap/foundation-ai';
11
- import { ChatToolChoice } from '@genesislcap/foundation-ai';
12
- import { ChatToolDefinition } from '@genesislcap/foundation-ai';
11
+ import type { ChatToolChoice } from '@genesislcap/foundation-ai';
12
+ import type { ChatToolDefinition } from '@genesislcap/foundation-ai';
13
13
  import type { ChatToolHandlers } from '@genesislcap/foundation-ai';
14
14
  import { GenesisElement } from '@genesislcap/web-core';
15
15
  import type { InteractionRequestOptions } from '@genesislcap/foundation-ai';
@@ -217,6 +217,40 @@ export declare interface AgentLifecycleContext {
217
217
  previousAgentName?: string;
218
218
  /** Aborted if the session disconnects mid-activation. */
219
219
  signal: AbortSignal;
220
+ /**
221
+ * A previously-serialized state image for this agent (from the agent's
222
+ * `serialize`), passed only on `onActivate` when resuming a persisted
223
+ * session. A stateful
224
+ * agent's `init` rebuilds from it — e.g. via `restoreMachine` — or starts
225
+ * fresh when it is `undefined`.
226
+ */
227
+ restored?: unknown;
228
+ }
229
+
230
+ /**
231
+ * Context passed to an agent's `resumable` predicate (GENC-1351 §5.9), evaluated
232
+ * at session-save time on the active agent (which, for a locked guided flow, is
233
+ * the flow-owner).
234
+ *
235
+ * @beta
236
+ */
237
+ export declare interface AgentPersistenceContext {
238
+ /** The agent being consulted. */
239
+ agentName: string;
240
+ /** The assistant session key — stable across reloads. */
241
+ sessionKey: string;
242
+ /**
243
+ * This agent's just-serialized state image — the value its `serialize()`
244
+ * returned for this save (or the carried-forward image). Inspect the machine
245
+ * step here. Derive the decision from this (not live closure state): the
246
+ * predicate must be a pure function of the state so a resumable snapshot stays
247
+ * resumable when re-evaluated on restore.
248
+ */
249
+ state: unknown;
250
+ /** Full conversation transcript at save time. */
251
+ messages: ReadonlyArray<ChatMessage>;
252
+ /** True when this agent currently owns the guided flow (`name === flowOwnerAgentName`). */
253
+ isFlowOwner: boolean;
220
254
  }
221
255
 
222
256
  /**
@@ -306,6 +340,41 @@ export declare interface AiAssistantAnimationDef {
306
340
  category: string;
307
341
  }
308
342
 
343
+ /**
344
+ * DOM events the `<foundation-ai-assistant>` element dispatches to its host.
345
+ * All bubble and are `composed` (they cross the shadow boundary), so a host can
346
+ * listen on the element — or any ancestor — to react. Use these constants to
347
+ * wire listeners instead of hand-typing event-name strings.
348
+ *
349
+ * An `as const` map (not a TS `enum` — this package ships none), so it serves as
350
+ * both the listener-name value and, via the same-named type below, a union.
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * import { AiAssistantEvent, type SessionClearedDetail } from '@genesislcap/ai-assistant';
355
+ *
356
+ * el.addEventListener(AiAssistantEvent.SessionCleared, (e) => {
357
+ * const { agentName, agentSnapshot } = (e as CustomEvent<SessionClearedDetail>).detail;
358
+ * // …clean up external state the agent was working on…
359
+ * });
360
+ * ```
361
+ *
362
+ * @beta
363
+ */
364
+ export declare const AiAssistantEvent: {
365
+ /** Session wiped via the lifecycle menu's Clear. `detail`: `SessionClearedDetail`. */
366
+ readonly SessionCleared: "session-cleared";
367
+ /** Header pressed in `popout-mode="expand"` (drag-to-popout hosts). `detail`: `ChatHeaderMouseDownDetail`. */
368
+ readonly ChatHeaderMouseDown: "chat-header-mousedown";
369
+ };
370
+
371
+ /**
372
+ * One of the `AiAssistantEvent` event-name values.
373
+ *
374
+ * @beta
375
+ */
376
+ export declare type AiAssistantEvent = (typeof AiAssistantEvent)[keyof typeof AiAssistantEvent];
377
+
309
378
  /**
310
379
  * State of the AI assistant component.
311
380
  *
@@ -355,12 +424,18 @@ export declare interface AiChatWidget {
355
424
  */
356
425
  export declare interface AiDockProvider {
357
426
  /**
358
- * Called when the user expands the bubble. The provider is responsible for inserting
359
- * the element into its layout. State is held in the session store and restored
360
- * automatically when the element connects.
427
+ * Called when the user expands the bubble. The provider **moves the given
428
+ * element** into its layout — e.g. via a layout factory that re-parents it into
429
+ * a pane. It is the ONE live assistant element, so it must be moved by reference,
430
+ * **not cloned** (a clone would be a second, dead instance). Session state
431
+ * survives the move because it lives in module-level registries, not the element.
361
432
  */
362
433
  onDock(element: HTMLElement): Promise<void>;
363
- /** Called when the user collapses the panel or before navigation. */
434
+ /**
435
+ * Called when the user collapses the panel or before navigation. By the time
436
+ * this runs the manager has already moved the assistant element back into the
437
+ * bubble, so the provider only needs to tear down its now-empty layout slot.
438
+ */
364
439
  onUndock(): Promise<void>;
365
440
  /**
366
441
  * Called by `collapseIfDocked` before navigation. The provider is responsible for
@@ -401,10 +476,41 @@ export declare interface AiDriver extends EventTarget {
401
476
  * Seed the driver's message history (called on state restore / pop-in).
402
477
  */
403
478
  loadHistory(messages: ChatMessage[]): void;
479
+ /**
480
+ * Prime per-agent restored state for a session being rehydrated. Each entry
481
+ * (keyed by agent name) is handed to that agent's `onActivate`/`init` as
482
+ * `ctx.restored` on its next activation, then consumed. Optional — only
483
+ * multi-agent drivers that own agent activation implement it.
484
+ */
485
+ primeRestoredAgentState?(snapshots: Record<string, unknown>): void;
404
486
  /**
405
487
  * Return the full, unredacted conversation history.
406
488
  */
407
489
  getRawHistory?(): readonly ChatMessage[];
490
+ /**
491
+ * The live `AgentConfig` currently active in the driver — the instance whose
492
+ * `onActivate` ran, so its `serialize`/`getDebugSnapshot` closures hold live
493
+ * state. Prefer this over looking the agent up in the host's `agents` array,
494
+ * which may mint fresh, never-activated configs. Optional — only drivers that
495
+ * retain the active config implement it.
496
+ */
497
+ getActiveAgent?(): AgentConfig | undefined;
498
+ /**
499
+ * Destructively compact older conversation turns into a single
500
+ * `compacted-summary` message (GENC-1351 §5.7), summarizing them via the
501
+ * default provider's one-shot `prompt()`. Atomic: history changes only on
502
+ * success. Resolves to the created summary message, or `null` when there is
503
+ * nothing worth compacting or the provider cannot summarize. Optional — only
504
+ * drivers that own history implement it.
505
+ */
506
+ compact?(): Promise<ChatMessage | null>;
507
+ /**
508
+ * Whether `compact()` would compact something right now — a summarizable run of
509
+ * older turns exists behind a clean boundary. The UI gates its "Compact"
510
+ * affordance on this, so the button reflects the exact history `compact()` acts
511
+ * on (GENC-1351). Optional — only history-owning drivers implement it.
512
+ */
513
+ canCompact?(): boolean;
408
514
  /**
409
515
  * Returns true if the driver is currently processing a request.
410
516
  */
@@ -440,14 +546,6 @@ export declare interface AiDriver extends EventTarget {
440
546
  * Optional; the host wires the stop button to it when present.
441
547
  */
442
548
  cancel?(): void;
443
- /** Register an assistant view (host) wired to this driver. */
444
- registerHost?(hostId: string): void;
445
- /** Deregister a host on unwire/disconnect. */
446
- unregisterHost?(hostId: string): void;
447
- /** Record the host whose subtree the user most recently interacted in (owner election signal). */
448
- setActiveHost?(hostId: string): void;
449
- /** First-writer-wins latch: the first host to claim an interaction id runs its side effect. */
450
- claimSideEffect?(interactionId: string, hostId: string): boolean;
451
549
  }
452
550
 
453
551
  /**
@@ -528,6 +626,20 @@ export declare const ANIMATION_DEFS: {
528
626
  };
529
627
  };
530
628
 
629
+ /**
630
+ * Reassemble diagnostic entries into the `{ readme, timeline, meta }` debug-log
631
+ * shape (GENC-1351 §5.8). `timeline` = the `message`/`turn`/`event` entries sorted
632
+ * by ISO `timestamp` (kind-rank tie-break). `meta` = the **newest** `meta-snapshot`'s
633
+ * block only — a "state at export" photo, matching the single-page log. The older
634
+ * snapshots are intentionally dropped: the per-turn evolution they would show is
635
+ * already in the timeline (`turn.agentSnapshot` + `context.updated` events), so
636
+ * keeping them would just repeat the bulky, near-static `agentSummary`. `readme` =
637
+ * the passed (current) constant. Pure — reused for both the live current-page log
638
+ * and the reassembled lifetime log stitched from the persisted stream, so both
639
+ * come out shape-identical.
640
+ */
641
+ export declare function assembleDebugLog(entries: DiagnosticEntry[], readme: readonly string[]): DebugLog;
642
+
531
643
  /**
532
644
  * App-supplied settings for the platform settings modal UI Builder section.
533
645
  * Register a concrete implementation on the DI token at app bootstrap; the
@@ -742,6 +854,43 @@ declare interface BaseAgentConfig {
742
854
  * @beta
743
855
  */
744
856
  getDebugSnapshot?: () => unknown;
857
+ /**
858
+ * Returns a lossless, JSON-serializable image of the agent's state for session
859
+ * persistence — distinct from `getDebugSnapshot`, which may abbreviate for
860
+ * readability. Pass the value back to the agent on restore (as
861
+ * `ctx.restored` in its `init`) to resume. {@link defineStatefulAgent} wires a
862
+ * default that snapshots any machine-shaped state via `getPersistedSnapshot()`
863
+ * and returns plain state as-is.
864
+ *
865
+ * @beta
866
+ */
867
+ serialize?: () => unknown;
868
+ /**
869
+ * Whether the agent's current state is resumable — i.e. worth persisting so the
870
+ * session can be restored mid-flow (GENC-1351 §5.9). Consulted at save time on
871
+ * the active agent (the flow-owner during a locked flow). Return `true`
872
+ * (or leave the field absent) to persist and resume normally; return `false`
873
+ * for a point that shouldn't be resumed — the session is not persisted, and on
874
+ * return the assistant shows {@link AgentConfig.notResumableMessage} (or a
875
+ * default) and pre-fills the composer with the prompt that started the flow so
876
+ * the user can re-run it.
877
+ *
878
+ * Only consulted when the user has persistence enabled. Must be a **pure
879
+ * function of `ctx.state`** — it may be re-evaluated on a restored snapshot, so
880
+ * a resumable state must stay resumable. A thrown error is treated as `true`
881
+ * (fail-safe: never drop a resumable chat on a predicate bug).
882
+ *
883
+ * @beta
884
+ */
885
+ resumable?: (ctx: AgentPersistenceContext) => boolean | Promise<boolean>;
886
+ /**
887
+ * Message shown on return when {@link AgentConfig.resumable} returned `false`
888
+ * and the user comes back to a non-resumable session (GENC-1351 §5.9). Falls
889
+ * back to a built-in default when omitted.
890
+ *
891
+ * @beta
892
+ */
893
+ notResumableMessage?: string;
745
894
  }
746
895
 
747
896
  export { CachePolicy }
@@ -776,12 +925,6 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
776
925
  /** Epoch ms when the current turn loop began — drives the `turn.end` duration. */
777
926
  private turnStartedAt;
778
927
  private pendingInteractions;
779
- /** Assistant host ids currently wired to this driver, in registration (insertion) order. */
780
- private hosts;
781
- /** The host whose subtree the user most recently interacted in — the primary owner signal. */
782
- private lastActiveHostId?;
783
- /** Interaction id → host id that claimed its side effect (first-writer-wins latch). */
784
- private startedSideEffects;
785
928
  private systemPrompt?;
786
929
  /**
787
930
  * Resolved tool definitions visible to the LLM. Folds mutate this in place
@@ -1195,33 +1338,23 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1195
1338
  /** Returns the current fold stack names for debugging. */
1196
1339
  getActiveFoldNames(): string[];
1197
1340
  getSuggestions(history: ChatMessage[], prompt: string, count: number, allAgentInfo?: AllAgentSummary[]): Promise<string[]>;
1198
- isBusy(): boolean;
1199
- /** Register an assistant view (host) wired to this shared driver. @internal */
1200
- registerHost(hostId: string): void;
1201
- /** Deregister a host on unwire/disconnect. @internal */
1202
- unregisterHost(hostId: string): void;
1203
- /**
1204
- * Record the host whose subtree the user most recently interacted in — the primary signal for
1205
- * electing which single view renders an interaction's widget. Sticky (unlike focus): it only
1206
- * moves when the user actually uses a different assistant view, not when they click elsewhere
1207
- * in the layout. @internal
1208
- */
1209
- setActiveHost(hostId: string): void;
1210
1341
  /**
1211
- * First-writer-wins latch guarding a side effect. A host claims before rendering the live
1212
- * widget: the first host to claim an interaction id gets `true` (run it); a *different* host
1213
- * gets `false` (render passive, do NOT re-fire). Re-claim by the SAME host returns `true`, so a
1214
- * benign re-render of the owner keeps its widget. Single-threaded, so check-and-set is race-free
1215
- * — the backstop that makes duplicate execution impossible even if owner election ever slips. @internal
1342
+ * Whether `compact()` would do anything right now — a summarizable run of older
1343
+ * turns exists behind a clean boundary. Uses the same `history` `compact()`
1344
+ * acts on, so the UI's gate can't disagree with the action (GENC-1351 follow-up).
1216
1345
  */
1217
- claimSideEffect(interactionId: string, hostId: string): boolean;
1346
+ canCompact(): boolean;
1218
1347
  /**
1219
- * Elect the single host that owns (renders + runs) an interaction's widget: the most-recently-
1220
- * active host if still connected, else a deterministic default (first registered host — `Set`
1221
- * preserves insertion order) so exactly one owner is always chosen. Returns `undefined` only when
1222
- * no host is registered, in which case the interaction renders everywhere (fail-open) not nowhere.
1348
+ * Destructively compact older turns into a single `compacted-summary` message
1349
+ * (GENC-1351 §5.7). Summarizes everything before a clean recent-tail boundary
1350
+ * via the registry-default provider's one-shot `prompt()`, then **atomically**
1351
+ * replaces the covered turns with one summary message at the head — history is
1352
+ * mutated only on success. Rolling: any existing head summary is folded into
1353
+ * the new one. Returns the created summary message, or `null` when there is
1354
+ * nothing worth compacting or the default provider cannot summarize.
1223
1355
  */
1224
- private electInteractionOwner;
1356
+ compact(): Promise<ChatMessage | null>;
1357
+ isBusy(): boolean;
1225
1358
  /**
1226
1359
  * Wire a parent driver as the host for this driver's interactions. When set,
1227
1360
  * `requestInteraction` delegates upward so the widget renders in (and
@@ -1330,11 +1463,11 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1330
1463
  }
1331
1464
 
1332
1465
  /**
1333
- * Detail payload for the `chat-header-mousedown` event fired by the assistant
1334
- * in `popout-mode="expand"`.
1466
+ * `detail` of the `AiAssistantEvent.ChatHeaderMouseDown` event.
1467
+ *
1335
1468
  * @beta
1336
1469
  */
1337
- export declare interface ChatHeaderMousedownEventDetail {
1470
+ export declare interface ChatHeaderMouseDownDetail {
1338
1471
  clientX: number;
1339
1472
  clientY: number;
1340
1473
  }
@@ -1439,6 +1572,13 @@ export declare function createToolFold(config: {
1439
1572
  exclusive?: boolean;
1440
1573
  }): ToolFoldResult;
1441
1574
 
1575
+ /** The reassembled debug log — the exact shape `getDebugLog()` returns. @public */
1576
+ export declare interface DebugLog {
1577
+ readme: readonly string[];
1578
+ timeline: DiagnosticEntry[];
1579
+ meta: unknown;
1580
+ }
1581
+
1442
1582
  /**
1443
1583
  * Animations enabled by default when a consumer opts into the animations
1444
1584
  * feature without specifying an explicit `enabled` list. Keeps the dots loading
@@ -1492,6 +1632,32 @@ export declare function defineAgent<const T extends AgentConfig>(config: T): T;
1492
1632
  */
1493
1633
  export declare function defineStatefulAgent<S>(opts: StatefulAgentInit<S>): AgentConfig;
1494
1634
 
1635
+ /**
1636
+ * One entry in the diagnostics stream — the debug-log timeline-entry union
1637
+ * (`message` / `turn` / `event`, as `getDebugLog()` builds) plus a point-in-time
1638
+ * **`meta-snapshot`** carrying the export-time `meta` block (agent summary,
1639
+ * context, active debug snapshot, …). Kept permissive: a provider stores and
1640
+ * returns these opaquely, and reassembly reads only `kind`, `timestamp` (for
1641
+ * ordering), and `meta` (on a snapshot).
1642
+ *
1643
+ * @public
1644
+ */
1645
+ export declare interface DiagnosticEntry {
1646
+ kind: 'message' | 'turn' | 'event' | 'meta-snapshot';
1647
+ /** ISO timestamp — string-comparable, so it orders the stream chronologically. */
1648
+ timestamp?: string;
1649
+ /** Present on `meta-snapshot` entries: the export-time `meta` block. */
1650
+ meta?: unknown;
1651
+ /**
1652
+ * Present on `meta-snapshot` entries: a stable signature of the near-static config
1653
+ * (excludes volatile timestamp/context/debug-snapshot), used by the forward-capture
1654
+ * delta to skip re-appending an unchanged block (GENC-1351 §5.8). Absent on other
1655
+ * kinds and on pre-signature entries (the delta falls back to the full `meta` then).
1656
+ */
1657
+ dedupSignature?: string;
1658
+ [key: string]: unknown;
1659
+ }
1660
+
1495
1661
  /**
1496
1662
  * Expands a flat list of tool definitions into a nested tree by resolving fold
1497
1663
  * metadata from the corresponding handlers. Use this for debug output so the
@@ -1535,7 +1701,8 @@ export declare interface FallbackAgentConfig extends BaseAgentConfig {
1535
1701
  * element `id`, or `'default'`). Snapshots run on tab close and when the session
1536
1702
  * title changes; hosts may call {@link FoundationAiAssistant.finalizeCostSession}.
1537
1703
  *
1538
- * @fires chat-header-mousedown - Fired when the user presses the chat header in `popout-mode="expand"` (for drag-to-popout hosts). Bubbles and composed. detail: `ChatHeaderMousedownEventDetail`
1704
+ * @fires chat-header-mousedown - Fired when the user presses the chat header in `popout-mode="expand"` (for drag-to-popout hosts). Bubbles and composed. detail: `ChatHeaderMouseDownDetail`
1705
+ * @fires session-cleared - Fired after the session is wiped (the lifecycle menu's Clear). The payload is captured before teardown so the host can clean up any external state the active agent was working on. Bubbles and composed. detail: `SessionClearedDetail`
1539
1706
  *
1540
1707
  * @beta
1541
1708
  */
@@ -1544,6 +1711,18 @@ export declare class FoundationAiAssistant extends GenesisElement {
1544
1711
  appSettingsProvider: AssistantAppSettingsProvider;
1545
1712
  designSystemPrefix: string;
1546
1713
  headerTitle: string;
1714
+ /**
1715
+ * Explicit session/persistence key (GENC-1351 §5.12). When set, this is the
1716
+ * session identity — the key for the driver/store/persister/diagnostics
1717
+ * registries and the `sessionKey` passed to the persistence provider — taking
1718
+ * precedence over the DOM `id`. A host that scopes persistence per-entity
1719
+ * (e.g. one chat session per project) binds this to a stable per-entity id;
1720
+ * changing it on a **connected** element re-keys the session in place — tears
1721
+ * down the current conversation and restores the new key's — via
1722
+ * `switchSession`. Falls back to `id` when unset,
1723
+ * so existing consumers are unaffected.
1724
+ */
1725
+ sessionKey?: string;
1547
1726
  imageSrc?: string;
1548
1727
  userImageSrc?: string;
1549
1728
  /**
@@ -1581,6 +1760,15 @@ export declare class FoundationAiAssistant extends GenesisElement {
1581
1760
  */
1582
1761
  agents?: AgentConfig[];
1583
1762
  chatConfig: ChatConfig;
1763
+ /**
1764
+ * Optional session persistence (GENC-1351). Off by default — set `enabled`
1765
+ * and a `provider` (e.g. a `WebStorageSessionProvider`) to have the assistant
1766
+ * autosave on idle and auto-restore the conversation on page reload. Exposed
1767
+ * as a top-level property (like `agents`/`providerRegistry`) rather than via
1768
+ * `chatConfig`, since the provider type lives in this package while
1769
+ * `ChatConfig` lives in `foundation-ai`.
1770
+ */
1771
+ persistence: SessionPersistenceConfig;
1584
1772
  /**
1585
1773
  * Resolved agent picker mode from `chatConfig.picker.mode`. Defaults to
1586
1774
  * `'disabled'`.
@@ -1597,6 +1785,14 @@ export declare class FoundationAiAssistant extends GenesisElement {
1597
1785
  debugStateFactory?: () => unknown;
1598
1786
  /** When set, enables Redux DevTools for this instance's session store. */
1599
1787
  debugRedux: boolean;
1788
+ /**
1789
+ * Pointer to this session's redux store. `@observable` (GENC-1351 §5.12) so that
1790
+ * **swapping** it — the in-place session switch/re-key (`switchSession`) —
1791
+ * forces every store-backed getter binding to re-evaluate against the NEW store.
1792
+ * Without this, FAST's per-store slice tracking would leave the view subscribed
1793
+ * to the old store after a swap and it wouldn't re-render. (A same-store reset via
1794
+ * redux actions re-renders without this; a store *swap* needs it.)
1795
+ */
1600
1796
  private _sessionRef?;
1601
1797
  get messages(): ChatMessage[];
1602
1798
  set messages(value: ChatMessage[]);
@@ -1612,6 +1808,15 @@ export declare class FoundationAiAssistant extends GenesisElement {
1612
1808
  * store-backed `state` changes.
1613
1809
  */
1614
1810
  get busy(): boolean;
1811
+ /**
1812
+ * True when a new send must be refused: a turn is running (`busy`), a page-reload
1813
+ * restore is loading history that would clobber it (`restoring`), or a manual
1814
+ * compaction is rewriting history (`compacting`). Mirrors the composer's
1815
+ * `?disabled` gate so a **programmatic** `send`/`submitMessage` (e.g. a host's
1816
+ * custom input while the built-in composer is hidden) can't slip past it and have
1817
+ * its message wiped when the restored/compacted history lands (GENC-1351 §6).
1818
+ */
1819
+ private get sendBlocked();
1615
1820
  /**
1616
1821
  * Re-runs `agentsChanged` if the live `agents` array no longer matches the
1617
1822
  * fingerprint of the currently installed driver. Used to apply swaps that
@@ -1619,6 +1824,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
1619
1824
  * driver returns to idle.
1620
1825
  */
1621
1826
  private applyDeferredAgentsSwap;
1827
+ /**
1828
+ * Apply a `session-key` re-key that arrived mid-turn and was deferred by
1829
+ * `switchSession`'s busy guard (GENC-1351 §5.12). Run at each point the driver
1830
+ * returns to idle, BEFORE `applyDeferredAgentsSwap` — a switch rebuilds the
1831
+ * driver against the current agents, so a pending agents-swap then no-ops.
1832
+ */
1833
+ private applyDeferredSessionSwitch;
1622
1834
  /**
1623
1835
  * Focus the main chat input if the user is still engaged with the assistant.
1624
1836
  * Deferred to the next frame so any conditional re-render (e.g. unhiding the
@@ -1654,6 +1866,27 @@ export declare class FoundationAiAssistant extends GenesisElement {
1654
1866
  */
1655
1867
  get agentPickerOpen(): boolean;
1656
1868
  set agentPickerOpen(value: boolean);
1869
+ /**
1870
+ * Whether the lifecycle (session) menu slide-out panel is open. Like the
1871
+ * agent picker, lives on the session store so bubble/popout stay in sync.
1872
+ */
1873
+ get sessionMenuOpen(): boolean;
1874
+ set sessionMenuOpen(value: boolean);
1875
+ /**
1876
+ * Whether a manual compaction is in flight (GENC-1351 §5.7). On the session
1877
+ * store (not element-local) so the "Compacting…" view + input-disable stay in
1878
+ * sync across pop-in/out, like the panel flags.
1879
+ */
1880
+ get compacting(): boolean;
1881
+ set compacting(value: boolean);
1882
+ /**
1883
+ * Whether a page-reload restore is in flight (GENC-1351 §6). On the session
1884
+ * store (like `compacting`) so the "Restoring…" view + input-disable stay in
1885
+ * sync across pop-in/out. Gating input while true prevents a mid-load send from
1886
+ * being clobbered when the restored history loads.
1887
+ */
1888
+ get restoring(): boolean;
1889
+ set restoring(value: boolean);
1657
1890
  /**
1658
1891
  * Name of the agent the user has pinned via the agent picker. `null` means
1659
1892
  * automatic routing (Auto). Persisted on the session store, so it survives
@@ -1747,6 +1980,14 @@ export declare class FoundationAiAssistant extends GenesisElement {
1747
1980
  appSettingsRevision: number;
1748
1981
  /** Whether the settings modal is open. */
1749
1982
  settingsOpen: boolean;
1983
+ /**
1984
+ * True while `downloadDebugLog` is building the log (GENC-1351 §5.8). Drives the
1985
+ * download button's disabled + spinner state — the lifetime-stitch path is async
1986
+ * (a provider `loadDiagnostics` round-trip), so without feedback a click looks
1987
+ * like nothing happened. Per-instance and transient (not shared/persisted): each
1988
+ * element has its own settings modal.
1989
+ */
1990
+ downloadingDebugLog: boolean;
1750
1991
  /** Active tab inside the settings modal. */
1751
1992
  settingsModalTab: SettingsModalTab;
1752
1993
  /** Bound to the design-system modal via the template ref. */
@@ -1774,10 +2015,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
1774
2015
  private _activeCostSessionTitle;
1775
2016
  /** Prevents double-finalize for the same session instance (pagehide + title change). */
1776
2017
  private _costSessionFinalizedForInstance;
1777
- /** Only the visible assistant instance should snapshot on tab close (bubble vs docked panel). */
1778
- private _finalizeCostSessionOnPageHide;
1779
2018
  private _handlePageHide;
1780
- private _syncFinalizeCostSessionOnPageHide;
1781
2019
  private haloStartPublished;
1782
2020
  /** Fingerprint of the agents array used to build the current driver. Used by agentsChanged to skip spurious rebuilds. */
1783
2021
  private _driverAgentsKey?;
@@ -1807,12 +2045,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
1807
2045
  * to avoid stealing focus from elsewhere on the page.
1808
2046
  */
1809
2047
  isEngaged: boolean;
1810
- /**
1811
- * GENC-1410 (Error 3): stable id for this assistant view. Reported to the shared driver so it
1812
- * can elect a single owner to render (and run) each interaction's widget when more than one view
1813
- * (e.g. a bubble and a docked panel) is wired to the same driver — so it fires once, not per view.
1814
- */
1815
- private readonly hostId;
1816
2048
  private _handleGlobalInteraction;
1817
2049
  /** True when the user has intentionally scrolled away from the bottom — suppresses auto-scroll. */
1818
2050
  private _userScrolledAway;
@@ -1840,6 +2072,43 @@ export declare class FoundationAiAssistant extends GenesisElement {
1840
2072
  * originating `send()` ran on a different element instance.
1841
2073
  */
1842
2074
  private _executionCompletionUnsub?;
2075
+ /**
2076
+ * True while UI preferences are being applied programmatically (config defaults
2077
+ * on connect, or a loaded-preferences restore) — suppresses the save hook so we
2078
+ * don't persist config defaults or re-persist what we just loaded. Cleared once
2079
+ * the apply completes; a genuine *user* toggle (menu switch) runs with it false
2080
+ * and saves (GENC-1351).
2081
+ */
2082
+ private _applyingPreferences;
2083
+ /**
2084
+ * Fresh-store flag (`= isNewStore`) captured in `connectedCallback`. Only a
2085
+ * freshly created store takes the config/stored-prefs baseline; an existing
2086
+ * store — a reload with a live in-realm store, or the popped-out second instance
2087
+ * — already holds the correct state, so `applyPreferenceBaseline` no-ops for it.
2088
+ */
2089
+ private _freshStore;
2090
+ /**
2091
+ * True once `connectedCallback` has fully wired the initial session (GENC-1351
2092
+ * §5.12). Guards `sessionKeyChanged` so the initial, pre-connect `session-key`
2093
+ * set doesn't trigger a re-key — only a genuine change to a connected element
2094
+ * switches the session. Reset on disconnect.
2095
+ */
2096
+ private _sessionActive;
2097
+ /**
2098
+ * The key of the session currently WIRED to this element (its driver/store/
2099
+ * `_sessionRef`). Authoritative for `getStateKey()` once a session is active,
2100
+ * because the `session-key` attr can run ahead of an actual switch: a mid-turn
2101
+ * rebind is deferred to idle (§5.12), and reading the attr in that window would
2102
+ * retarget autosave/diagnostics at a not-yet-activated key and silently drop the
2103
+ * rest of the live turn. Set by `activateSession`. Undefined before first connect.
2104
+ */
2105
+ private _activeStateKey?;
2106
+ /**
2107
+ * Target key of a `session-key` rebind that arrived mid-turn and was deferred
2108
+ * (§5.12). Applied by `applyDeferredSessionSwitch` once the driver goes idle. The
2109
+ * latest rebind wins if several land while busy. Undefined = no switch pending.
2110
+ */
2111
+ private _deferredSwitchToKey?;
1843
2112
  showHalo: 'no' | 'orchestrating' | 'agent';
1844
2113
  private syncShowHalo;
1845
2114
  /** True when there is a pending (unresolved) interaction. */
@@ -1897,11 +2166,75 @@ export declare class FoundationAiAssistant extends GenesisElement {
1897
2166
  * the driver or remove it from the registry.
1898
2167
  */
1899
2168
  private unwireDriver;
2169
+ /**
2170
+ * Tear down the session's currently-wired driver (GENC-1351): unwire this view, drop
2171
+ * the driver from the registry, and dispose it — which fires the active agent's
2172
+ * `onDeactivate` → `machine.stop()`. Shared by `agentsChanged`, `clearSession`, and
2173
+ * `switchSession` so the teardown stays identical. `unwireDriver` does not clear
2174
+ * `this.driver`, so the instance is still readable here for disposal.
2175
+ */
2176
+ private teardownDriver;
2177
+ /**
2178
+ * Build (or adopt from the registry) a fresh driver for `key` against the current
2179
+ * agents and wire it to this view (GENC-1351). Records the agents fingerprint so a
2180
+ * later `agentsChanged` skips a spurious rebuild. Shared by `agentsChanged` and
2181
+ * `clearSession`.
2182
+ */
2183
+ private rebuildDriver;
2184
+ /**
2185
+ * Wire up the session identified by `key` on this element: preference baseline,
2186
+ * driver (build/reuse) + wiring, restore (fresh store only), and the per-session
2187
+ * view state (splash, loading-resume, suggestions, context/provider status, cost
2188
+ * history). Extracted from `connectedCallback` (GENC-1351 §5.12) so an in-place
2189
+ * session switch (`switchSession`) can re-run exactly this against a new
2190
+ * key without re-adding element-lifetime listeners. Session-scoped only — the
2191
+ * element-lifetime wiring (DOM/document/window listeners, provider-registry
2192
+ * subscription, app-settings, pagehide) stays in `connectedCallback`.
2193
+ */
2194
+ private activateSession;
1900
2195
  connectedCallback(): void;
2196
+ /**
2197
+ * Switch this element to a different session **in place**, without a page reload
2198
+ * (GENC-1351 §5.12) — the load-a-different-project flow. Tears down the current
2199
+ * session's driver + per-key registries and restores the target key's session
2200
+ * (its transcript and any mid-flow stateful agent). Fired automatically by
2201
+ * `sessionKeyChanged` when a host rebinds
2202
+ * `session-key` on a connected element; may also be called directly.
2203
+ *
2204
+ * Clobber-safe: the outgoing session's pending autosave is cancelled BEFORE the
2205
+ * store swaps, so its transcript can never be written to the incoming session's
2206
+ * file. Idle-only — a switch while a turn is running is refused.
2207
+ *
2208
+ * @param oldKey - the state key being switched away from.
2209
+ * @param newKey - the state key to activate.
2210
+ * @public
2211
+ */
2212
+ switchSession(oldKey: string, newKey: string): void;
2213
+ /**
2214
+ * FAST reflection hook for the `session-key` attr: a change on a **connected**
2215
+ * element re-keys the session in place (GENC-1351 §5.12). Guarded by
2216
+ * `_sessionActive` so the initial, pre-connect set does not switch — the first
2217
+ * key is wired by `connectedCallback`.
2218
+ */
2219
+ sessionKeyChanged(_oldValue: string | undefined, _newValue: string | undefined): void;
1901
2220
  disconnectedCallback(): void;
1902
2221
  private resolveContextLimit;
1903
2222
  private loadProviderStatuses;
1904
2223
  chatConfigChanged(): void;
2224
+ /**
2225
+ * `persistence` (like `chatConfig`) can bind after `connectedCallback` — a host may
2226
+ * resolve the `provider` asynchronously (an auth token, a feature flag). The connect
2227
+ * path gates the whole persistence init on a provider being present *at connect*, and
2228
+ * nothing else re-triggers it — so a late provider would silently never restore the
2229
+ * session or load stored preferences (GENC-1351 #13). Mirror `chatConfigChanged`: when
2230
+ * a provider first appears on a connected element whose store is still fresh, run the
2231
+ * fresh-store init now. Guards keep it a one-shot:
2232
+ * - pre-connect sets are handled by `connectedCallback` (`_sessionActive` is false);
2233
+ * - a provider that was already present (incl. the internal `enabled`-only
2234
+ * reassignments) is a no-op (`oldValue?.provider`);
2235
+ * - a non-fresh (restored) store already holds its session (`_freshStore`).
2236
+ */
2237
+ persistenceChanged(oldValue: SessionPersistenceConfig | undefined, newValue: SessionPersistenceConfig | undefined): void;
1905
2238
  showingSplashChanged(): void;
1906
2239
  private syncShowingSplash;
1907
2240
  /**
@@ -1918,27 +2251,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
1918
2251
  private clearLoadingTimer;
1919
2252
  private stopLoadingTimer;
1920
2253
  /** Called when the user clicks the popout button. */
1921
- /**
1922
- * GENC-1410 (Error 3): claim the right to run a side-effecting interaction's server work for
1923
- * THIS view, via the shared driver's first-writer-wins latch. Returns `true` when this view may
1924
- * run it (first/owning claimant, or a re-render of the same view), `false` when another view
1925
- * already holds it. Fail-open when the driver has no latch. Called by `ai-chat-interaction-wrapper`.
1926
- */
1927
- claimSideEffect(interactionId: string): boolean;
1928
- /**
1929
- * GENC-1410 (Error 3): mark THIS view as the interaction owner on the shared driver, so it (not a
1930
- * sibling view sharing the same driver) is elected to render side-effecting widgets. The bubble
1931
- * and docked panel normally claim this automatically on the pop-in / dock lifecycle, but that
1932
- * lifecycle only runs once `popout-mode` is active. A host that establishes a view outside that
1933
- * lifecycle — e.g. a page that restores a docked assistant from a persisted layout before the
1934
- * pop-out affordance is enabled — calls this to seize ownership deterministically rather than
1935
- * leaving it to the last-interaction / first-registered fallback. Idempotent; safe to call on
1936
- * every layout (re)establish. No-op if the driver was never wired.
1937
- */
1938
- claimInteractionOwnership(): void;
1939
2254
  handlePopout(): void;
1940
2255
  /** Returns a cache key for this instance, or undefined if the component has no identity. */
1941
2256
  private getStateKey;
2257
+ /**
2258
+ * The session key implied by the element's current attributes (GENC-1351 §5.12).
2259
+ * An explicit `session-key` IS the complete session identity — the host fully
2260
+ * qualifies it (e.g. a per-project id), so `headerTitle` is NOT appended
2261
+ * (appending it was multi-window disambiguation, obsolete under the single-window
2262
+ * orchestration/pinning model for keyed hosts). Without a `session-key`, fall back
2263
+ * to `id` + `headerTitle` (the original scheme, kept so existing consumers — e.g. a
2264
+ * title-only key with no `id` — aren't re-keyed). This is the *desired* key;
2265
+ * `getStateKey` returns the *wired* key, which can lag during a deferred switch.
2266
+ */
2267
+ private computeStateKeyFromAttrs;
1942
2268
  /**
1943
2269
  * Record a meta/lifecycle event onto this session's debug-log timeline
1944
2270
  * (exported via {@link FoundationAiAssistant.getDebugLog}). Every event
@@ -1949,6 +2275,99 @@ export declare class FoundationAiAssistant extends GenesisElement {
1949
2275
  private logMeta;
1950
2276
  private readonly _agentPickerToggle;
1951
2277
  toggleAgentPicker(): void;
2278
+ /** Two-step confirm for the destructive Clear action; reset whenever the menu toggles. */
2279
+ confirmingClear: boolean;
2280
+ confirmingCompact: boolean;
2281
+ private readonly _sessionMenuToggle;
2282
+ /**
2283
+ * Whether the lifecycle-menu button shows. It occupies the stop-button slot,
2284
+ * so it only appears when idle (mutually exclusive with Stop). Gated on
2285
+ * persistence — the menu's actions (Compact, Clear, the "remember chat" toggle)
2286
+ * only make sense with a provider configured, so it's hidden entirely without
2287
+ * one. A host can additionally force it off via `chatConfig.ui.showSessionMenu:
2288
+ * false` even when a provider is present (e.g. a guided onboarding flow where
2289
+ * clearing/compacting mid-journey would be disruptive).
2290
+ */
2291
+ get sessionMenuEnabled(): boolean;
2292
+ /** The persistence toggle row renders only when a provider is configured. */
2293
+ get persistenceToggleable(): boolean;
2294
+ /** Whether persistence is currently active (on + a provider) for this session. */
2295
+ get persistenceActive(): boolean;
2296
+ /**
2297
+ * Whether the menu's "Compact" action would do anything (GENC-1351 §5.7).
2298
+ * Delegates to `driver.canCompact()` so the gate uses the exact same history
2299
+ * `compact()` acts on — one source of truth. Also reads `messages`, purely to
2300
+ * stay reactive as the conversation grows: the driver's history isn't a tracked
2301
+ * observable, so the button keys its re-evaluation off the Redux mirror.
2302
+ */
2303
+ get compactable(): boolean;
2304
+ toggleSessionMenu(): void;
2305
+ /**
2306
+ * Set persistence on/off for this session (the menu switch). Reassigns the
2307
+ * `persistence` object so the `@observable` fires. Turning it **off** drops the
2308
+ * stored snapshot; **on** captures the current chat immediately. Either way the
2309
+ * *choice* is remembered in the preferences blob (`persistPreferences`) — stored
2310
+ * independently of the snapshot, so it survives the clear and re-applies on the
2311
+ * next load (GENC-1351).
2312
+ */
2313
+ setPersistenceEnabled(enabled: boolean): void;
2314
+ /** Toggle persistence (convenience / programmatic). */
2315
+ togglePersistence(): void;
2316
+ /** Snapshot the user-changeable settings into a persistable preferences blob. */
2317
+ private gatherPreferences;
2318
+ /**
2319
+ * Persist the current UI preferences whenever the user changes a setting
2320
+ * (GENC-1351). Fires **regardless of "remember chat"** — display choices are the
2321
+ * user's, not tied to whether the chat is remembered — needing only a provider
2322
+ * that implements `savePreferences`. No-op while preferences are being applied
2323
+ * programmatically (`_applyingPreferences`), so config defaults and loaded prefs
2324
+ * aren't re-saved.
2325
+ */
2326
+ private persistPreferences;
2327
+ /**
2328
+ * Apply the effective UI-preference baseline onto the live toggles for a fresh
2329
+ * store (GENC-1351 #2). Precedence, lowest → highest:
2330
+ *
2331
+ * slice defaults < `chatConfig.ui` defaults < stored user preferences
2332
+ *
2333
+ * Recomputed from scratch (not layered) on every call, because its two inputs
2334
+ * arrive at unpredictable times: `chatConfig` is a parent-supplied `@observable`
2335
+ * that may bind *after* `connectedCallback`, and stored prefs load asynchronously.
2336
+ * So it re-runs from `connectedCallback`, `chatConfigChanged`, and once
2337
+ * `loadPreferences` resolves — whichever order they happen in, the final state is
2338
+ * the same. No-op once the store isn't fresh (an existing/popped-out store owns
2339
+ * its state) or the user has toggled something (their choice then wins, and
2340
+ * `chatConfig` doesn't change post-startup in practice). Save hook suppressed so
2341
+ * applying the baseline never persists as if it were a user choice.
2342
+ *
2343
+ * This is the fix for "nuking server prefs drops to all-disabled instead of the
2344
+ * `chatConfig` defaults": stored prefs are simply absent, so the baseline falls
2345
+ * through to the config layer — which the old apply-once-on-connect path missed
2346
+ * whenever `chatConfig` hadn't bound yet.
2347
+ */
2348
+ private applyPreferenceBaseline;
2349
+ /**
2350
+ * Compact the conversation (GENC-1351 §5.7) — the menu's "Compact" action.
2351
+ * Delegates to the driver, which summarizes older turns into a single
2352
+ * `compacted-summary` message (destructive, atomic). On success records a
2353
+ * high-importance `context.compacted` meta-event capturing the summary; the
2354
+ * normal `history-updated` → autosave path then persists the smaller
2355
+ * transcript. No-op when there is nothing to compact or the provider cannot
2356
+ * summarize (the driver returns `null`).
2357
+ */
2358
+ compactSession(): Promise<void>;
2359
+ /**
2360
+ * Clear the session — the "New chat" action (GENC-1351 §5.6). Tears down the
2361
+ * active driver (firing the stateful agent's `onDeactivate` → `machine.stop()`),
2362
+ * resets the store, rebuilds a fresh driver with no history, and wipes durable
2363
+ * storage. Mirrors `agentsChanged`'s teardown, minus the history reseed.
2364
+ *
2365
+ * Emits `session-cleared` (`SessionClearedDetail`, declared on the class `@fires`)
2366
+ * with `sessionKey`, `agentName`, and `agentSnapshot` captured *before* teardown, so
2367
+ * the host can clean up any external state the active agent was working on (generated
2368
+ * files, server-side scratch, etc.). Bubbles and composed.
2369
+ */
2370
+ clearSession(): Promise<void>;
1952
2371
  /**
1953
2372
  * Programmatically pin an agent by name. Returns `true` if the pin was
1954
2373
  * applied, `false` if the agent isn't in the configured agents array or
@@ -2038,68 +2457,53 @@ export declare class FoundationAiAssistant extends GenesisElement {
2038
2457
  toggleShowNarration(): void;
2039
2458
  toggleShowAgentSwitchIndicator(): void;
2040
2459
  setEnabledAnimations(animations: AiAssistantAnimation[]): void;
2041
- getDebugLog(): {
2042
- readme: readonly string[];
2043
- timeline: (TimelineMessage | {
2044
- systemPrompt: string;
2045
- turnIndex: string;
2046
- timestamp: string;
2047
- agentName?: string;
2048
- tailContext?: string;
2049
- toolNames: string[];
2050
- temperature?: number;
2051
- toolChoice?: ChatToolChoice;
2052
- agentLabel?: string;
2053
- agentSnapshot?: unknown;
2054
- kind: "turn";
2055
- } | {
2056
- index: number;
2057
- timestamp: string;
2058
- type: MetaEventType;
2059
- importance: MetaEventImportance;
2060
- detail?: Record<string, unknown>;
2061
- kind: "event";
2062
- })[];
2063
- meta: {
2064
- timestamp: string;
2065
- host: string;
2066
- agentSummary: {
2067
- toolDefinitions: string | ChatToolDefinition[];
2068
- name: string;
2069
- provider?: ProviderInput;
2070
- chatInputDuringExecution?: ChatInputDuringExecutionMode;
2071
- description?: string;
2072
- fallback?: true;
2073
- displayName?: SystemPromptInput;
2074
- systemPrompt?: SystemPromptInput;
2075
- temperature?: TemperatureInput;
2076
- toolChoice?: ToolChoiceInput;
2077
- cachePolicy?: CachePolicyInput;
2078
- tailContext?: TailContextInput;
2079
- onUnresolvedTool?: UnresolvedToolInput;
2080
- primerHistory?: ChatMessage[];
2081
- subAgents?: AgentConfig[];
2082
- manualSelection?: ManualSelectionConfig;
2083
- onActivate?: (ctx: AgentLifecycleContext) => void | Promise<void>;
2084
- onDeactivate?: (ctx: AgentLifecycleContext) => void | Promise<void>;
2085
- getDebugSnapshot?: () => unknown;
2086
- }[];
2087
- activeSystemPrompt: string;
2088
- activePrimerHistory: ChatMessage[];
2089
- activeFoldStack: string[];
2090
- context: {
2091
- model: string;
2092
- contextTokens: number;
2093
- contextLimit: number;
2094
- contextUsagePercent: number;
2095
- sessionCostUsd: number;
2096
- sessionTokensConsumed: number;
2097
- };
2098
- activeDebugSnapshot: unknown;
2099
- debug: unknown;
2100
- };
2101
- };
2102
- downloadDebugLog(): void;
2460
+ /** The live current-page debug log (`{ readme, timeline, meta }`). @public */
2461
+ getDebugLog(): DebugLog;
2462
+ /**
2463
+ * Build the current page's diagnostic entries (GENC-1351 §5.8) — the debug-log
2464
+ * timeline entries (`message`/`turn`/`event`) plus one `meta-snapshot` carrying
2465
+ * the export-time `meta` block. `getDebugLog()` reassembles these into the live
2466
+ * log; the persister's forward-capture path (`SessionPersister.flushDiagnostics`,
2467
+ * fed these via the `getDiagnosticEntries` dep) diffs them against what's already
2468
+ * been appended to the persisted stream.
2469
+ */
2470
+ private buildDiagnosticEntries;
2471
+ downloadDebugLog(): Promise<void>;
2472
+ /**
2473
+ * The log to download (GENC-1351 §5.8). When the provider persists diagnostics,
2474
+ * stitch the **whole lifetime** stream (every page load) rather than just the
2475
+ * current page's in-memory view: flush the current delta first so it's included,
2476
+ * then reassemble everything `loadDiagnostics` returns. Independent of "remember
2477
+ * chat" — diagnostics are a forensic stream keyed on provider capability, not on
2478
+ * whether the *chat* is remembered (like preferences). Falls back to the live
2479
+ * current-page log when diagnostics isn't available or the fetch fails.
2480
+ */
2481
+ private buildDownloadLog;
2482
+ /**
2483
+ * The shared per-session `SessionPersister` that owns restore-core
2484
+ * save/restore/freeze (GENC-1351 §5.10). Created once per `stateKey` and shared by
2485
+ * every element view (bubble + panel) via the registry, so there is a single save
2486
+ * timer + carry-forward cache — no per-view double-writes. The deps are getters so
2487
+ * the persister always reads the current driver/store/config and never holds a
2488
+ * stale reference. `undefined` before the session key is available.
2489
+ */
2490
+ private persister;
2491
+ /**
2492
+ * The `SessionPersister` dependency getters bound to THIS element + `key`.
2493
+ * The host-owned getters (`getPersistence`/`getAgentNames`/`getDiagnosticEntries`)
2494
+ * read the live element; `getDriver`/`getStore` resolve by key from the registries.
2495
+ * Rebound onto the shared persister on every activation (see `activateSession`) so
2496
+ * a remounted element becomes the live source, not the torn-down creator (GENC-1351 §5.10 / #7).
2497
+ */
2498
+ private buildPersisterDeps;
2499
+ /**
2500
+ * First-load persistence init for a freshly created store (GENC-1351). Restores
2501
+ * the user's remembered persistence-toggle choice — kept independently of the
2502
+ * session snapshot so it survives a clear; the host-configured `enabled` is only
2503
+ * the default for a first-ever load — then auto-restores the session when
2504
+ * persistence is (now) on.
2505
+ */
2506
+ private initPersistenceForNewStore;
2103
2507
  triggerFileInput(): void;
2104
2508
  handleFileSelect(e: Event): void;
2105
2509
  removeAttachment(attachment: ChatAttachment): void;
@@ -2142,6 +2546,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
2142
2546
  files?: File[];
2143
2547
  }): Promise<SubmitMessageResult>;
2144
2548
  private fetchSuggestions;
2549
+ /**
2550
+ * User text of the message currently being sent, stashed for the lifetime of `send()`.
2551
+ * `flow-owner-changed` fires while the driver routes this turn — before the message is
2552
+ * appended to raw history — so the flow-activation-prompt capture reads this rather than
2553
+ * a stale raw-history lookup. Cleared once the send settles. (GENC-1351 §5.9)
2554
+ */
2555
+ private _pendingSendPrompt?;
2145
2556
  private send;
2146
2557
  onChatHeaderMouseDown(e: MouseEvent): void;
2147
2558
  handleInteractionCompleted(e: Event): void;
@@ -2173,9 +2584,14 @@ export declare const FoundationAiAssistantTemplate: (designSystemPrefix: string)
2173
2584
  *
2174
2585
  * @remarks
2175
2586
  * Place this in the persistent app shell, wrapping a `foundation-ai-chat-bubble` that
2176
- * contains an AI assistant element in the `dialog-content` slot. The component will
2177
- * auto-create a matching collapse-mode element for docking into pages, and will
2178
- * control the expand button visibility based on whether a dock provider is registered.
2587
+ * contains an AI assistant element in the `dialog-content` slot.
2588
+ *
2589
+ * GENC-1351 (Option A — single element): rather than cloning a second assistant to
2590
+ * dock, this manager **moves the one real element** between the bubble and the page
2591
+ * layout. One connected instance means no dual-view coordination (no shared-driver
2592
+ * unwire/rewire, host election, or duplicated side-effects); the session survives the
2593
+ * move because the driver/store/persister live in module-level registries keyed by the
2594
+ * element's identity.
2179
2595
  *
2180
2596
  * Pages that support docking call `aiPopoutManager.registerDockProvider()` on mount
2181
2597
  * and `aiPopoutManager.deregisterDockProvider()` on unmount.
@@ -2194,12 +2610,29 @@ export declare const FoundationAiAssistantTemplate: (designSystemPrefix: string)
2194
2610
  export declare class FoundationAiPopoutManager extends GenesisElement {
2195
2611
  /** True when a dock provider is registered — controls expand button visibility. */
2196
2612
  canDock: boolean;
2197
- private collapseEl;
2613
+ /**
2614
+ * The single assistant element. Captured while it's in the bubble slot and held
2615
+ * across the move (it's the same instance whether docked or in the bubble), so
2616
+ * pop-in can re-home it even though it's no longer a child of this manager.
2617
+ */
2618
+ private assistantEl;
2198
2619
  private dockProvider;
2199
2620
  private isDocked;
2200
2621
  private unsubBus?;
2201
2622
  connectedCallback(): void;
2202
2623
  disconnectedCallback(): void;
2624
+ /** Locate the assistant element currently slotted into the bubble. */
2625
+ private findAssistant;
2626
+ /**
2627
+ * Register an externally-created assistant element and its docked state. For
2628
+ * single-element hosts that create the assistant directly in a layout pane (rather
2629
+ * than statically in the bubble), so the manager tracks the one element, whether it's
2630
+ * currently docked (for pop-in re-homing), reflects the right `popout-mode` on it
2631
+ * (drives the header button), and hides the empty bubble.
2632
+ */
2633
+ adoptAssistant(el: HTMLElement, docked: boolean): void;
2634
+ /** Hide the floating bubble unless it currently contains the assistant. */
2635
+ private syncBubbleVisibility;
2203
2636
  canDockChanged(): void;
2204
2637
  registerDockProvider(provider: AiDockProvider): void;
2205
2638
  deregisterDockProvider(): void;
@@ -2254,7 +2687,7 @@ declare interface InputOverride {
2254
2687
  */
2255
2688
  export declare type InteractionCompletedEventDetail = ChatInteractionEventsMap['interaction-completed'];
2256
2689
 
2257
- /** True when the collapse-mode assistant is active in the layout panel. */
2690
+ /** True when the assistant is active in the layout panel (docked) rather than the bubble. */
2258
2691
  export declare function isAiAssistantDocked(): boolean;
2259
2692
 
2260
2693
  /**
@@ -2285,44 +2718,6 @@ export declare interface ManualSelectionConfig {
2285
2718
  hint?: string;
2286
2719
  }
2287
2720
 
2288
- /**
2289
- * How much a reader should care about an event — lets a consumer (or an AI
2290
- * agent) filter the timeline: skip `low` UI/bookkeeping noise, skim `normal`
2291
- * session flow, or jump straight to `high` problem/limit signals when triaging.
2292
- */
2293
- declare type MetaEventImportance = 'high' | 'normal' | 'low';
2294
-
2295
- /**
2296
- * Module-level append-only timeline of meta/lifecycle events, keyed by session
2297
- * identity (the same `stateKey` space used by the driver registry and the
2298
- * session store).
2299
- *
2300
- * Why a module registry rather than the redux store or the driver:
2301
- * - **Survives the pop-in/pop-out element churn.** A single session can be
2302
- * emitted from up to two element instances (an `expand` bubble + a `collapse`
2303
- * panel) sharing one driver; keying by `stateKey` unifies them into one
2304
- * timeline.
2305
- * - **Survives driver rebuilds.** The driver is torn down and recreated when the
2306
- * host swaps the agents array (see `agentsChanged` / `deleteDriver`), so a
2307
- * driver-owned buffer would lose the timeline mid-session. The `stateKey`
2308
- * outlives the driver.
2309
- * - **Stays out of redux reactivity.** These appends never drive the UI, so
2310
- * there's no reason to pay immutable-reducer cost or flood the Redux DevTools
2311
- * action log with debug noise.
2312
- *
2313
- * Surfaced in `getDebugLog().timeline`, merged chronologically with messages and
2314
- * turn snapshots. Ring-buffered — the oldest non-`high` events are evicted first
2315
- * while `high`-importance failure events are retained — so a long-lived session
2316
- * stays bounded in normal use without ever dropping a failure signal.
2317
- *
2318
- * @internal
2319
- */
2320
- /**
2321
- * Catalogue of meta event names. This is the documented surface — extend it as
2322
- * new events are wired in (Tier 2/3 lifecycle, interaction, provider events).
2323
- */
2324
- declare type MetaEventType = 'assistant.connected' | 'assistant.disconnected' | 'assistant.popout' | 'assistant.popin' | 'driver.created' | 'driver.wired' | 'driver.unwired' | 'state.changed' | 'turn.start' | 'turn.end' | 'turn.retry' | 'turn.error' | 'tool.failed' | 'tool.unresolved' | 'subagent.started' | 'subagent.completed' | 'subagent.failed' | 'agent.handoff' | 'agent.pinned' | 'agent.unpinned' | 'provider.selected' | 'interaction.requested' | 'interaction.resolved' | 'context.updated' | 'context.threshold-crossed' | 'context.condensed' | 'panel.toggled' | 'attachment.added' | 'file.read-failed' | 'suggestions.failed';
2325
-
2326
2721
  export { ModelTagAppearance }
2327
2722
 
2328
2723
  /**
@@ -2368,6 +2763,13 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
2368
2763
  * choice.
2369
2764
  */
2370
2765
  private flowOwnerAgentName;
2766
+ /**
2767
+ * Per-agent restored state for a rehydrated session, keyed by agent name.
2768
+ * Primed by the host on restore; each entry is passed to that agent's
2769
+ * `onActivate` as `ctx.restored` on its next activation, then deleted so a
2770
+ * later re-activation starts fresh.
2771
+ */
2772
+ private restoredAgentStates;
2371
2773
  activeAgent?: AgentConfig;
2372
2774
  constructor(providerRegistry: AIProviderRegistry, agents: AgentConfig[], options?: {
2373
2775
  sessionKey?: string;
@@ -2380,10 +2782,6 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
2380
2782
  });
2381
2783
  resolveInteraction(interactionId: string, result: unknown): void;
2382
2784
  isBusy(): boolean;
2383
- registerHost(hostId: string): void;
2384
- unregisterHost(hostId: string): void;
2385
- setActiveHost(hostId: string): void;
2386
- claimSideEffect(interactionId: string, hostId: string): boolean;
2387
2785
  /** Currently active provider name from the underlying ChatDriver. */
2388
2786
  getActiveProviderName(): string;
2389
2787
  /**
@@ -2402,11 +2800,19 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
2402
2800
  setFlowOwner(name: string | null): void;
2403
2801
  loadHistory(messages: ChatMessage[]): void;
2404
2802
  getRawHistory(): readonly ChatMessage[];
2803
+ /** {@inheritDoc AiDriver.compact} */
2804
+ compact(): Promise<ChatMessage | null>;
2805
+ /** {@inheritDoc AiDriver.canCompact} */
2806
+ canCompact(): boolean;
2405
2807
  /** Delegates to the inner {@link ChatDriver} — turns are captured there. */
2406
2808
  getTurnSnapshots(): ReadonlyArray<TurnSnapshot>;
2407
2809
  getSuggestions(history: ChatMessage[], prompt: string, count: number, allAgentInfo?: AllAgentSummary[]): Promise<string[]>;
2408
2810
  sendMessage(input: string, attachments?: ChatAttachment[]): Promise<ChatDriverResult>;
2409
2811
  continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
2812
+ /** {@inheritDoc AiDriver.primeRestoredAgentState} */
2813
+ primeRestoredAgentState(snapshots: Record<string, unknown>): void;
2814
+ /** {@inheritDoc AiDriver.getActiveAgent} */
2815
+ getActiveAgent(): AgentConfig | undefined;
2410
2816
  private applyAgent;
2411
2817
  /**
2412
2818
  * Release the current stateful agent: fire `onDeactivate`, clear the
@@ -2445,6 +2851,75 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
2445
2851
  private appendInlineMessage;
2446
2852
  }
2447
2853
 
2854
+ /**
2855
+ * Schema version of the persisted session shape. Bump on an incompatible
2856
+ * change; a snapshot whose `version` doesn't match is discarded on load rather
2857
+ * than risk feeding a stale shape into restore.
2858
+ *
2859
+ * @public
2860
+ */
2861
+ export declare const PERSISTED_SESSION_VERSION = 1;
2862
+
2863
+ /**
2864
+ * A durable, restorable snapshot of one chat session — the *restorable core* of
2865
+ * the debug log (GENC-1351 §5.1/D1), captured losslessly and independently of
2866
+ * the diagnostic timeline. Written by a `SessionPersistenceProvider` and read
2867
+ * back on page-reload restore.
2868
+ *
2869
+ * @public
2870
+ */
2871
+ export declare interface PersistedSession {
2872
+ /** Schema version — see {@link PERSISTED_SESSION_VERSION}. */
2873
+ version: number;
2874
+ /** ISO timestamp the snapshot was written. */
2875
+ savedAt: string;
2876
+ /** `window.location.host` at save time — restore refuses a cross-host snapshot. */
2877
+ host: string;
2878
+ /** The session this snapshot belongs to (the assistant's state key). */
2879
+ sessionKey: string;
2880
+ /** Full conversation transcript (`driver.getRawHistory()`) — the source of truth. */
2881
+ messages: ChatMessage[];
2882
+ /** Name of the agent active at save time, if any. */
2883
+ activeAgentName?: string;
2884
+ /** User's pinned agent (sticky picker choice); `null` = Auto. */
2885
+ pinnedAgentName: string | null;
2886
+ /** Agent that owned a guided flow at save time; `null` = none. */
2887
+ flowOwnerAgentName: string | null;
2888
+ /** Aggregated USD cost across the session. */
2889
+ sessionCostUsd: number;
2890
+ /** Provider-reported context-window usage on the last turn. */
2891
+ contextTokens?: number;
2892
+ /** Context-window limit for the active model. */
2893
+ contextLimit?: number;
2894
+ /** Active model id (e.g. `claude-sonnet-4-6`). */
2895
+ activeModel?: string;
2896
+ /** Name of the provider used on the last turn. */
2897
+ activeProviderName?: string;
2898
+ /**
2899
+ * Per-stateful-agent serialized state images (each from an agent's
2900
+ * `serialize()`), keyed by agent name. Handed back to the agent's
2901
+ * `init({ restored })` on restore. Typically holds at most the active
2902
+ * flow-owner — other stateful agents are disposed when switched away. The
2903
+ * value shape is the agent's own, hence `unknown`.
2904
+ */
2905
+ agentSnapshots: Record<string, unknown>;
2906
+ /**
2907
+ * Interrupted-flow marker (GENC-1351 §5.9). Present when the session was saved
2908
+ * while an agent declared its current state non-resumable (`resumable === false`).
2909
+ * The **conversation is still persisted and restored in full** — only the running
2910
+ * agent's flow-state is dropped (`agentSnapshots` empty, `flowOwnerAgentName`
2911
+ * null), so restore does not rehydrate the mid-flight flow. On restore the
2912
+ * assistant appends `message` as a normal assistant bubble and pre-fills the
2913
+ * composer with `prompt` so the user can re-run it.
2914
+ */
2915
+ interrupted?: {
2916
+ /** Shown as an assistant message on return. */
2917
+ message: string;
2918
+ /** The prompt that started the flow, pre-filled into the composer on return. */
2919
+ prompt?: string;
2920
+ };
2921
+ }
2922
+
2448
2923
  /**
2449
2924
  * Controls the pop-out button shown in the assistant header.
2450
2925
  * - `"expand"` — assistant is embedded in the bubble dialog; button expands it to a layout panel.
@@ -2487,6 +2962,147 @@ declare interface ResolvedCostHistoryConfig {
2487
2962
  badgeLabel: string;
2488
2963
  }
2489
2964
 
2965
+ /**
2966
+ * Restore a foundation-state-machine instance from a snapshot produced by the
2967
+ * default serializer (or `machine.getPersistedSnapshot()`). Call inside a
2968
+ * stateful agent's `init` to resume the machine in its exact prior place:
2969
+ *
2970
+ * ```ts
2971
+ * init: ({ restored }) => {
2972
+ * const machine = new MyMachine();
2973
+ * restoreMachine(machine, restored); // resume, or no-op on a fresh start
2974
+ * return { machine };
2975
+ * }
2976
+ * ```
2977
+ *
2978
+ * No-ops when there is nothing to restore (a fresh session), leaving the
2979
+ * freshly-constructed machine in its initial state.
2980
+ *
2981
+ * @param machine - the machine to restore (already constructed/started).
2982
+ * @param restored - the agent's serialized image (`ctx.restored` in `init`).
2983
+ * @param key - the property the machine occupies in the snapshot; defaults to
2984
+ * `'machine'` to match the conventional `{ machine }` state bag.
2985
+ * @beta
2986
+ */
2987
+ export declare function restoreMachine(machine: {
2988
+ stop: () => void;
2989
+ start: (options?: {
2990
+ state?: unknown;
2991
+ }) => void;
2992
+ }, restored: unknown, key?: string): void;
2993
+
2994
+ /**
2995
+ * `detail` of the `AiAssistantEvent.SessionCleared` event. Captured *before*
2996
+ * teardown so a host can tidy up whatever the active agent was working on
2997
+ * (generated files, server-side scratch, …). `agentSnapshot` is the agent's
2998
+ * `serialize()` output (a state-machine image for machine-backed agents).
2999
+ *
3000
+ * @beta
3001
+ */
3002
+ export declare interface SessionClearedDetail {
3003
+ sessionKey: string | undefined;
3004
+ agentName: string | undefined;
3005
+ agentSnapshot: unknown;
3006
+ }
3007
+
3008
+ /**
3009
+ * Host-supplied persistence configuration, passed to the assistant via its
3010
+ * `persistence` property. Persistence stays inert unless `enabled` is true and
3011
+ * a `provider` is set — so existing consumers are unaffected (GENC-1351 D2/D3).
3012
+ *
3013
+ * @public
3014
+ */
3015
+ export declare interface SessionPersistenceConfig {
3016
+ /** Master switch — persistence does nothing unless this is true. */
3017
+ enabled?: boolean;
3018
+ /** Storage backend. Required for persistence to take effect. */
3019
+ provider?: SessionPersistenceProvider;
3020
+ }
3021
+
3022
+ /**
3023
+ * Storage backend for chat-session persistence. The library ships this
3024
+ * interface only and wires nothing by default; the host app provides an
3025
+ * implementation and opts in via `chatConfig` (GENC-1351 §5.2/D2). Every method
3026
+ * is async so a server-backed provider satisfies the same contract as a
3027
+ * browser-storage one.
3028
+ *
3029
+ * @public
3030
+ */
3031
+ export declare interface SessionPersistenceProvider {
3032
+ /**
3033
+ * Minimum milliseconds between autosaves for this backend — a throttle the
3034
+ * assistant applies to its "save on every transcript change" signal. `0`
3035
+ * means save as soon as the current tick settles (coalescing only writes
3036
+ * within the same tick), which is right for synchronous, cheap storage like
3037
+ * `sessionStorage`/`localStorage`. A network/server backend should set a
3038
+ * higher value (e.g. 1000–2000) so it doesn't hammer the wire mid tool-loop.
3039
+ *
3040
+ * **Required** — not optional — so every implementation makes a deliberate
3041
+ * choice for its own write-cost profile rather than inheriting a guess.
3042
+ */
3043
+ saveDebounceMs: number;
3044
+ /** Load the snapshot for `sessionKey`, or `null` if none / unreadable. */
3045
+ load(sessionKey: string): Promise<PersistedSession | null>;
3046
+ /** Persist `snapshot` for `sessionKey`, overwriting any existing one. */
3047
+ save(sessionKey: string, snapshot: PersistedSession): Promise<void>;
3048
+ /** Remove any stored snapshot for `sessionKey`. */
3049
+ clear(sessionKey: string): Promise<void>;
3050
+ /**
3051
+ * Load the user's remembered UI preferences for `sessionKey` (GENC-1351) — the
3052
+ * "remember chat" toggle plus the cog-menu display switches. Stored
3053
+ * **independently of the session snapshot** (survives `clear`) and **independently
3054
+ * of the `enabled` toggle** — a user's display choices persist whether or not
3055
+ * they remember the chat. `undefined` means none were ever saved → the host's
3056
+ * configured `chatConfig.ui` defaults apply. Only the fields the user has changed
3057
+ * are present; absent fields keep their config default. Optional: a provider that
3058
+ * omits it simply doesn't remember preferences across reloads.
3059
+ */
3060
+ loadPreferences?(sessionKey: string): Promise<SessionPreferences | undefined>;
3061
+ /** Persist the user's UI preferences for `sessionKey` (kept across `clear`). */
3062
+ savePreferences?(sessionKey: string, preferences: SessionPreferences): Promise<void>;
3063
+ /**
3064
+ * Server-saved diagnostics (GENC-1351 §5.8) — a **forward-only append** stream
3065
+ * that lets the downloadable debug log span the whole session lifetime, not just
3066
+ * the current page load. Optional and **capacious-backends-only**: `WebStorage`
3067
+ * omits it (the log dwarfs the restore core), so the feature is simply off there.
3068
+ * The element emits *what happened* (new entries since the last flush); the
3069
+ * provider owns *how it's filed* (one object per session / per run / per day —
3070
+ * its call). Best-effort: a failed write must never affect the chat.
3071
+ *
3072
+ * Captured **independently of the `enabled` toggle** (like preferences): the
3073
+ * forensic log is orthogonal to whether the *chat* is remembered, so it runs
3074
+ * whenever a provider implements this — even with "remember chat" off.
3075
+ */
3076
+ appendDiagnostics?(sessionKey: string, entries: DiagnosticEntry[]): Promise<void>;
3077
+ /** Load the full diagnostics stream for `sessionKey` (all appends, any run), for download-reassembly. */
3078
+ loadDiagnostics?(sessionKey: string): Promise<DiagnosticEntry[]>;
3079
+ /** Hard-delete the diagnostics stream for `sessionKey`. Host/GDPR use — NOT wired to the UI Clear. */
3080
+ clearDiagnostics?(sessionKey: string): Promise<void>;
3081
+ /**
3082
+ * Min ms between diagnostics appends — coarser than `saveDebounceMs` because
3083
+ * the log is heavy. Defaults to a sensible coarse value if omitted.
3084
+ */
3085
+ diagnosticsDebounceMs?: number;
3086
+ }
3087
+
3088
+ /**
3089
+ * User-changeable settings that persist across reloads, independently of the chat
3090
+ * snapshot and of the "remember chat" toggle (GENC-1351). All optional — an absent
3091
+ * field keeps its `chatConfig.ui` default rather than being forced off.
3092
+ *
3093
+ * @public
3094
+ */
3095
+ export declare interface SessionPreferences {
3096
+ /** "Remember chat" — whether the session snapshot itself is persisted. */
3097
+ enabled?: boolean;
3098
+ showToolCalls?: boolean;
3099
+ showThinkingSteps?: boolean;
3100
+ showNarration?: boolean;
3101
+ showAgentSwitchIndicator?: boolean;
3102
+ /** Enabled loading-animation ids (`AiAssistantAnimation` values, as plain strings). */
3103
+ animations?: string[];
3104
+ }
3105
+
2490
3106
  /** Settings vs Cost tab identifiers for the modal tab bar. */
2491
3107
  declare type SettingsModalTab = 'settings' | 'cost';
2492
3108
 
@@ -2576,6 +3192,10 @@ export declare interface StatefulAgentInit<S> {
2576
3192
  /**
2577
3193
  * Construct the agent-scoped state. Called by the framework when this agent
2578
3194
  * becomes active (`onActivate`). Awaited before the agent's first turn runs.
3195
+ *
3196
+ * On a persisted-session restore, `ctx.restored` carries this agent's previous
3197
+ * `serialize` output — rebuild from it (e.g. via `restoreMachine`), or start
3198
+ * fresh when it is `undefined`.
2579
3199
  */
2580
3200
  init: (ctx: AgentLifecycleContext) => S | Promise<S>;
2581
3201
  /**
@@ -2689,8 +3309,49 @@ export declare interface StatefulAgentInit<S> {
2689
3309
  * Return value must be JSON-serializable.
2690
3310
  */
2691
3311
  getDebugSnapshot?: (state: S) => unknown;
3312
+ /**
3313
+ * Lossless serializer for session persistence — infra-agnostic. Return any
3314
+ * JSON/structured-clone-safe image of `state`.
3315
+ * • Machine agents (`{ machine }`): omit — the default snapshots every
3316
+ * machine-shaped property via `getPersistedSnapshot()`.
3317
+ * • Plain-data agents (e.g. `{ plan, cursor }`): omit too — the default
3318
+ * returns plain state as-is. Provide one only when the state holds
3319
+ * something the default can't capture.
3320
+ * Distinct from `getDebugSnapshot` (which may be lossy by design). Pair with
3321
+ * `init` reading `ctx.restored` to rebuild.
3322
+ */
3323
+ serialize?: (state: S) => unknown;
3324
+ /**
3325
+ * Whether the agent's current state is resumable — worth persisting so the
3326
+ * session restores mid-flow (GENC-1351 §5.9). Receives the **live** state bag
3327
+ * `S` (like the other per-turn resolvers), so a machine agent gates on its step
3328
+ * directly: `resumable: ({ state }) => state.machine.state !== 'confirm'`.
3329
+ * Return `false` for a point that shouldn't resume — the session isn't persisted,
3330
+ * and on return the assistant shows {@link StatefulAgentInit.notResumableMessage}
3331
+ * (or a default) and pre-fills the composer with the prompt that started the flow.
3332
+ * Only consulted with persistence enabled; a thrown error is treated as `true`
3333
+ * (fail-safe — never drop a resumable chat on a predicate bug).
3334
+ */
3335
+ resumable?: (ctx: StatefulAgentPersistenceContext<S>) => boolean | Promise<boolean>;
3336
+ /**
3337
+ * Message shown on return when {@link StatefulAgentInit.resumable} returned
3338
+ * `false`. Falls back to a built-in default when omitted.
3339
+ */
3340
+ notResumableMessage?: string;
2692
3341
  }
2693
3342
 
3343
+ /**
3344
+ * Context passed to a stateful agent's `resumable` predicate (GENC-1351 §5.9) —
3345
+ * the standard {@link AgentPersistenceContext} but with `state` as the **live**
3346
+ * state bag `S` (not the serialized image), so a machine agent can gate on its
3347
+ * current step directly.
3348
+ *
3349
+ * @beta
3350
+ */
3351
+ export declare type StatefulAgentPersistenceContext<S> = Omit<AgentPersistenceContext, 'state'> & {
3352
+ state: S;
3353
+ };
3354
+
2694
3355
  /**
2695
3356
  * A brief, professional fallback agent. States what is available without
2696
3357
  * elaboration. The `{{agents}}` placeholder is replaced at runtime by
@@ -3001,4 +3662,39 @@ export declare type UnresolvedToolInput = (ctx: UnresolvedToolContext) => string
3001
3662
  /** Person silhouette — default user avatar. */
3002
3663
  export declare const userIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\" fill=\"currentColor\"/></svg>";
3003
3664
 
3665
+ /**
3666
+ * A {@link SessionPersistenceProvider} backed by a Web Storage area. Both
3667
+ * `window.sessionStorage` and `window.localStorage` implement the `Storage`
3668
+ * API, so one class serves both: point it at `sessionStorage` for
3669
+ * refresh-survival that clears on tab close (the showcase default, O6), or at
3670
+ * `localStorage` for durable local persistence.
3671
+ *
3672
+ * Opt-in: a host constructs it and passes it through `chatConfig`; the library
3673
+ * never instantiates it on its own.
3674
+ *
3675
+ * @public
3676
+ */
3677
+ export declare class WebStorageSessionProvider implements SessionPersistenceProvider {
3678
+ private readonly storage;
3679
+ private readonly keyPrefix;
3680
+ /**
3681
+ * Web storage is synchronous and cheap, so the default is `0` — save as
3682
+ * soon as the tick settles, no throttle. Override via `options.saveDebounceMs`
3683
+ * (e.g. if pointing at `localStorage` and you'd rather coalesce large writes).
3684
+ */
3685
+ readonly saveDebounceMs: number;
3686
+ constructor(storage: Storage, options?: {
3687
+ keyPrefix?: string;
3688
+ saveDebounceMs?: number;
3689
+ });
3690
+ private storageKey;
3691
+ /** Preferences key, namespaced apart from the session key so `clear` leaves it. */
3692
+ private preferencesKey;
3693
+ load(sessionKey: string): Promise<PersistedSession | null>;
3694
+ save(sessionKey: string, snapshot: PersistedSession): Promise<void>;
3695
+ clear(sessionKey: string): Promise<void>;
3696
+ loadPreferences(sessionKey: string): Promise<SessionPreferences | undefined>;
3697
+ savePreferences(sessionKey: string, preferences: SessionPreferences): Promise<void>;
3698
+ }
3699
+
3004
3700
  export { }