@genesislcap/ai-assistant 15.27.0 → 15.27.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.
@@ -1696,7 +1696,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
1696
1696
  if (agents.length === 1 && !agents[0].fallback) {
1697
1697
  // Preset for getters that read activeAgent before the first turn
1698
1698
  // (e.g. chatInputDuringExecution). The orchestrator will overwrite
1699
- // this via `agent-changed` once applyAgent fires on first sendMessage.
1699
+ // this via `agent-changed` once installAgent fires on first sendMessage.
1700
1700
  this.activeAgent = agents[0];
1701
1701
  }
1702
1702
  return new OrchestratingDriver(this.providerRegistry, agents, {
@@ -2697,11 +2697,11 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2697
2697
  * @internal
2698
2698
  */
2699
2699
  get contextReserve() {
2700
- var _a, _b, _c, _d;
2700
+ var _a, _b, _c;
2701
2701
  const resolved = (_b = (_a = this.driver) === null || _a === void 0 ? void 0 : _a.getContextReserve) === null || _b === void 0 ? void 0 : _b.call(_a);
2702
2702
  if (resolved)
2703
2703
  return resolved;
2704
- const active = (_d = (_c = this.driver) === null || _c === void 0 ? void 0 : _c.getActiveAgent) === null || _d === void 0 ? void 0 : _d.call(_c);
2704
+ const active = (_c = this.driver) === null || _c === void 0 ? void 0 : _c.getActiveAgent();
2705
2705
  const declared = active === null || active === void 0 ? void 0 : active.contextReserve;
2706
2706
  if (declared == null)
2707
2707
  return undefined;
@@ -2945,18 +2945,18 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2945
2945
  */
2946
2946
  clearSession() {
2947
2947
  return __awaiter(this, void 0, void 0, function* () {
2948
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
2948
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2949
2949
  if ((_a = this.driver) === null || _a === void 0 ? void 0 : _a.isBusy())
2950
2950
  return; // the menu only opens when idle; defensive
2951
2951
  const key = this.getStateKey();
2952
2952
  // Capture the live agent + its serialized state BEFORE teardown — the
2953
2953
  // machine is stopped below, so this is the host's last chance to learn what
2954
2954
  // the agent was working on (so it can tidy up external state if it wants).
2955
- const liveActive = (_c = (_b = this.driver) === null || _b === void 0 ? void 0 : _b.getActiveAgent) === null || _c === void 0 ? void 0 : _c.call(_b);
2956
- const agentName = (_d = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _d !== void 0 ? _d : (_e = this.activeAgent) === null || _e === void 0 ? void 0 : _e.name;
2955
+ const liveActive = (_b = this.driver) === null || _b === void 0 ? void 0 : _b.getActiveAgent();
2956
+ const agentName = (_c = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _c !== void 0 ? _c : (_d = this.activeAgent) === null || _d === void 0 ? void 0 : _d.name;
2957
2957
  let agentSnapshot;
2958
2958
  try {
2959
- agentSnapshot = (_f = liveActive === null || liveActive === void 0 ? void 0 : liveActive.serialize) === null || _f === void 0 ? void 0 : _f.call(liveActive);
2959
+ agentSnapshot = (_e = liveActive === null || liveActive === void 0 ? void 0 : liveActive.serialize) === null || _e === void 0 ? void 0 : _e.call(liveActive);
2960
2960
  }
2961
2961
  catch (e) {
2962
2962
  logger.error('Session clear: agent serialize() failed:', e);
@@ -2967,7 +2967,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2967
2967
  const discarded = this.sessionUsage;
2968
2968
  if (this.driver)
2969
2969
  this.teardownDriver(key);
2970
- (_g = this._sessionRef) === null || _g === void 0 ? void 0 : _g.actions.aiAssistant.resetSession();
2970
+ (_f = this._sessionRef) === null || _f === void 0 ? void 0 : _f.actions.aiAssistant.resetSession();
2971
2971
  // `resetSession` zeroes the store-backed `contextTokens`; the gate's held copy
2972
2972
  // is ours to drop. Leaving it would carry a block into a conversation that no
2973
2973
  // longer exists — the one thing `contextGate` must never do (GENC-1582).
@@ -2979,7 +2979,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2979
2979
  // Restore-core is owned by the shared per-session persister (GENC-1351 §5.10):
2980
2980
  // it drops the snapshot + resets the carry-forward cache. Diagnostics is
2981
2981
  // append-only and handled separately below.
2982
- yield ((_h = this.persister()) === null || _h === void 0 ? void 0 : _h.clearRestoreCore());
2982
+ yield ((_g = this.persister()) === null || _g === void 0 ? void 0 : _g.clearRestoreCore());
2983
2983
  this.confirmingClear = false;
2984
2984
  this.composerMenus.closeImmediate();
2985
2985
  this.logMeta('session.cleared', { agentName });
@@ -2988,8 +2988,8 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2988
2988
  // forward-capture cursors and flush, so the just-logged `session.cleared`
2989
2989
  // event becomes the stream's roll boundary: exactly like a page reload, the
2990
2990
  // new conversation begins a fresh section within the same lifetime log (§5.8).
2991
- resetDiagnosticsCursorsFor((_j = this.getStateKey()) !== null && _j !== void 0 ? _j : '');
2992
- (_k = this.persister()) === null || _k === void 0 ? void 0 : _k.scheduleDiagnostics();
2991
+ resetDiagnosticsCursorsFor((_h = this.getStateKey()) !== null && _h !== void 0 ? _h : '');
2992
+ (_j = this.persister()) === null || _j === void 0 ? void 0 : _j.scheduleDiagnostics();
2993
2993
  this.dispatchEvent(new CustomEvent(AiAssistantEvent.SessionCleared, {
2994
2994
  bubbles: true,
2995
2995
  composed: true,
@@ -3305,11 +3305,11 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
3305
3305
  * `onActivate` ran, so only its `getDebugSnapshot` closure holds state.
3306
3306
  */
3307
3307
  buildMetaSnapshot() {
3308
- var _a, _b, _c;
3308
+ var _a, _b;
3309
3309
  return buildMetaSnapshot({
3310
3310
  agents: this.agents,
3311
3311
  activeAgent: this.activeAgent,
3312
- liveActiveAgent: (_b = (_a = this.driver) === null || _a === void 0 ? void 0 : _a.getActiveAgent) === null || _b === void 0 ? void 0 : _b.call(_a),
3312
+ liveActiveAgent: (_a = this.driver) === null || _a === void 0 ? void 0 : _a.getActiveAgent(),
3313
3313
  activeFoldStack: this.driver instanceof ChatDriver ? this.driver.getActiveFoldNames() : undefined,
3314
3314
  activeModel: this.activeModel,
3315
3315
  contextTokens: this.contextTokens,
@@ -3317,7 +3317,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
3317
3317
  sessionCostUsd: this.sessionCostUsd,
3318
3318
  sessionTokensConsumed: this.sessionTokensConsumed,
3319
3319
  sessionUsage: this.sessionUsage,
3320
- debugState: (_c = this.debugStateFactory) === null || _c === void 0 ? void 0 : _c.call(this),
3320
+ debugState: (_b = this.debugStateFactory) === null || _b === void 0 ? void 0 : _b.call(this),
3321
3321
  host: window.location.host,
3322
3322
  });
3323
3323
  }
@@ -270,7 +270,7 @@ export class SessionPersister {
270
270
  */
271
271
  persistSession() {
272
272
  return __awaiter(this, void 0, void 0, function* () {
273
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
273
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
274
274
  const { provider, enabled } = this.deps.getPersistence();
275
275
  if (!enabled || !provider)
276
276
  return;
@@ -288,7 +288,7 @@ export class SessionPersister {
288
288
  // mints fresh configs (the showcase) would otherwise hand us a never-activated
289
289
  // closure whose `serialize()` returns undefined.
290
290
  const liveSnapshots = {};
291
- const liveActive = (_a = driver === null || driver === void 0 ? void 0 : driver.getActiveAgent) === null || _a === void 0 ? void 0 : _a.call(driver);
291
+ const liveActive = driver === null || driver === void 0 ? void 0 : driver.getActiveAgent();
292
292
  if (liveActive === null || liveActive === void 0 ? void 0 : liveActive.serialize) {
293
293
  try {
294
294
  const snap = liveActive.serialize();
@@ -344,7 +344,7 @@ export class SessionPersister {
344
344
  sessionKey: key,
345
345
  host: window.location.host,
346
346
  messages: messages.slice(0, Math.min(boundary, messages.length)),
347
- activeAgentName: (_b = s.activeAgent) === null || _b === void 0 ? void 0 : _b.name,
347
+ activeAgentName: (_a = s.activeAgent) === null || _a === void 0 ? void 0 : _a.name,
348
348
  pinnedAgentName: s.pinnedAgentName,
349
349
  flowOwnerAgentName: null,
350
350
  sessionCostUsd: s.sessionUsage.costUsd,
@@ -365,7 +365,7 @@ export class SessionPersister {
365
365
  return;
366
366
  }
367
367
  this.logMeta('persistence.suppressed', {
368
- agent: (_c = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _c !== void 0 ? _c : null,
368
+ agent: (_b = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _b !== void 0 ? _b : null,
369
369
  isFlowOwner: s.flowOwnerAgentName === (liveActive === null || liveActive === void 0 ? void 0 : liveActive.name),
370
370
  frozeAtMessages: frozen.messages.length,
371
371
  custom: !!(liveActive === null || liveActive === void 0 ? void 0 : liveActive.notResumableMessage),
@@ -382,7 +382,7 @@ export class SessionPersister {
382
382
  // event was added to the resumable branch — so a session that broke while an agent was
383
383
  // mid-flow still reported nothing at all, which is precisely the blind spot the
384
384
  // user-facing warning exists to cover.
385
- this.recordSaveOutcome(false, (_d = e === null || e === void 0 ? void 0 : e.message) !== null && _d !== void 0 ? _d : String(e));
385
+ this.recordSaveOutcome(false, (_c = e === null || e === void 0 ? void 0 : e.message) !== null && _c !== void 0 ? _c : String(e));
386
386
  // Deliberately NOT reporting from here. This is the non-resumable branch and a reload is
387
387
  // the modal's only action, so advising one now would destroy the in-flight work the
388
388
  // snapshot could not carry. `recordSaveOutcome` has already set `_brokenSince`, so the
@@ -395,11 +395,11 @@ export class SessionPersister {
395
395
  this.logMeta('persistence.save-failed', {
396
396
  messages: frozen.messages.length,
397
397
  agents: [],
398
- active: (_e = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _e !== void 0 ? _e : null,
398
+ active: (_d = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _d !== void 0 ? _d : null,
399
399
  hasSerialize: !!(liveActive === null || liveActive === void 0 ? void 0 : liveActive.serialize),
400
400
  frozen: true,
401
- error: (_f = e === null || e === void 0 ? void 0 : e.name) !== null && _f !== void 0 ? _f : typeof e,
402
- message: (_g = e === null || e === void 0 ? void 0 : e.message) !== null && _g !== void 0 ? _g : String(e),
401
+ error: (_e = e === null || e === void 0 ? void 0 : e.name) !== null && _e !== void 0 ? _e : typeof e,
402
+ message: (_f = e === null || e === void 0 ? void 0 : e.message) !== null && _f !== void 0 ? _f : String(e),
403
403
  });
404
404
  logger.error('Session save (interrupted marker) failed:', e);
405
405
  }
@@ -411,7 +411,7 @@ export class SessionPersister {
411
411
  sessionKey: key,
412
412
  host: window.location.host,
413
413
  messages: [...messages],
414
- activeAgentName: (_h = s.activeAgent) === null || _h === void 0 ? void 0 : _h.name,
414
+ activeAgentName: (_g = s.activeAgent) === null || _g === void 0 ? void 0 : _g.name,
415
415
  pinnedAgentName: s.pinnedAgentName,
416
416
  flowOwnerAgentName: s.flowOwnerAgentName,
417
417
  sessionCostUsd: s.sessionUsage.costUsd,
@@ -435,7 +435,7 @@ export class SessionPersister {
435
435
  const captured = {
436
436
  messages: messages.length,
437
437
  agents: Object.keys(agentSnapshots),
438
- active: (_j = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _j !== void 0 ? _j : null,
438
+ active: (_h = liveActive === null || liveActive === void 0 ? void 0 : liveActive.name) !== null && _h !== void 0 ? _h : null,
439
439
  hasSerialize: !!(liveActive === null || liveActive === void 0 ? void 0 : liveActive.serialize),
440
440
  agentSnapshots,
441
441
  };
@@ -454,7 +454,7 @@ export class SessionPersister {
454
454
  // means the body never reached the wire at all, because a provider builds its
455
455
  // request body inside the `fetch` init, so a throw while serialising creates no
456
456
  // request and leaves no server-side trace.
457
- this.recordSaveOutcome(false, (_k = e === null || e === void 0 ? void 0 : e.message) !== null && _k !== void 0 ? _k : String(e));
457
+ this.recordSaveOutcome(false, (_j = e === null || e === void 0 ? void 0 : e.message) !== null && _j !== void 0 ? _j : String(e));
458
458
  // Report straight away rather than waiting for the next attempt: we are on the resumable
459
459
  // path, so a reload is safe NOW, and a one-cycle delay is a second of extra exposure for
460
460
  // no benefit.
@@ -471,8 +471,8 @@ export class SessionPersister {
471
471
  agents: captured.agents,
472
472
  active: captured.active,
473
473
  hasSerialize: captured.hasSerialize,
474
- error: (_l = e === null || e === void 0 ? void 0 : e.name) !== null && _l !== void 0 ? _l : typeof e,
475
- message: (_m = e === null || e === void 0 ? void 0 : e.message) !== null && _m !== void 0 ? _m : String(e),
474
+ error: (_k = e === null || e === void 0 ? void 0 : e.name) !== null && _k !== void 0 ? _k : typeof e,
475
+ message: (_l = e === null || e === void 0 ? void 0 : e.message) !== null && _l !== void 0 ? _l : String(e),
476
476
  });
477
477
  logger.error('Session save failed:', e);
478
478
  }
@@ -487,7 +487,7 @@ export class SessionPersister {
487
487
  */
488
488
  restore() {
489
489
  return __awaiter(this, void 0, void 0, function* () {
490
- var _a, _b, _c, _d, _e;
490
+ var _a, _b, _c, _d;
491
491
  const { provider } = this.deps.getPersistence();
492
492
  if (!provider)
493
493
  return;
@@ -536,8 +536,8 @@ export class SessionPersister {
536
536
  // Prime each stateful agent's persisted machine/state image FIRST — before
537
537
  // seeding Redux or driver history — so the image is waiting on the driver no
538
538
  // matter when the agent re-activates. The flow-owner lock self-heals on
539
- // activation (OrchestratingDriver.applyAgent re-sets it).
540
- (_a = driver === null || driver === void 0 ? void 0 : driver.primeRestoredAgentState) === null || _a === void 0 ? void 0 : _a.call(driver, snapshot.agentSnapshots);
539
+ // activation (OrchestratingDriver.installAgent re-sets it).
540
+ driver === null || driver === void 0 ? void 0 : driver.primeRestoredAgentState(snapshot.agentSnapshots);
541
541
  // Seed the carry-forward cache so the immediate post-restore autosave (fired
542
542
  // by the history change below, before any agent re-activates) preserves the
543
543
  // machine snapshot instead of overwriting it with an empty one (GENC-1351).
@@ -550,7 +550,7 @@ export class SessionPersister {
550
550
  // actually came back (meta events reset on reload, so this is the only window
551
551
  // onto the saved→loaded handoff).
552
552
  this.logMeta('persistence.restored', {
553
- agents: Object.keys((_b = snapshot.agentSnapshots) !== null && _b !== void 0 ? _b : {}),
553
+ agents: Object.keys((_a = snapshot.agentSnapshots) !== null && _a !== void 0 ? _a : {}),
554
554
  agentSnapshots: snapshot.agentSnapshots,
555
555
  pinnedAgentName: snapshot.pinnedAgentName,
556
556
  flowOwnerAgentName: snapshot.flowOwnerAgentName,
@@ -618,7 +618,7 @@ export class SessionPersister {
618
618
  }
619
619
  const messages = [...trimmed, ...trailers];
620
620
  ref.actions.aiAssistant.loadSession(toLoadSessionPayload(Object.assign(Object.assign({}, snapshot), { messages })));
621
- (_c = driver === null || driver === void 0 ? void 0 : driver.loadHistory) === null || _c === void 0 ? void 0 : _c.call(driver, [...messages]);
621
+ (_b = driver === null || driver === void 0 ? void 0 : driver.loadHistory) === null || _b === void 0 ? void 0 : _b.call(driver, [...messages]);
622
622
  // Interrupted flow (§5.9): pre-fill the composer with the prompt that started
623
623
  // the flow so "send it again" is one keystroke, and record it for observability.
624
624
  if (snapshot.interrupted) {
@@ -641,8 +641,8 @@ export class SessionPersister {
641
641
  // driver's routing agrees with the restored (picker-locked) store state.
642
642
  const orchestrator = asOrchestrator(driver);
643
643
  if (orchestrator) {
644
- orchestrator.setPinnedAgent((_d = snapshot.pinnedAgentName) !== null && _d !== void 0 ? _d : null);
645
- orchestrator.setFlowOwner((_e = snapshot.flowOwnerAgentName) !== null && _e !== void 0 ? _e : null);
644
+ orchestrator.setPinnedAgent((_c = snapshot.pinnedAgentName) !== null && _c !== void 0 ? _c : null);
645
+ orchestrator.setFlowOwner((_d = snapshot.flowOwnerAgentName) !== null && _d !== void 0 ? _d : null);
646
646
  }
647
647
  logger.debug('Session restored', { key, messages: messages.length });
648
648
  }
@@ -1,47 +1,26 @@
1
1
  # Migration Guide — GENC-1580 (agent lifecycle leaves the orchestrator)
2
2
 
3
- > ## ⚠️ Partly shipped — the rename has not landed, so do not action the rename
3
+ > ## ⚠️ §4 only is still unshipped
4
4
  >
5
- > The structural half of this change is in. What is **not** in is the one part that would break
6
- > you: `ChatDriver.applyAgent` has not been renamed. It was held back deliberately, to land alone
7
- > as the last PR of GENC-1580 — the rename cannot be caught by a compiler in a JavaScript caller,
8
- > so it is worth scheduling rather than absorbing alongside a refactor.
5
+ > Everything in this guide is now true **except §4** (`pinned-changed`), which carries its own
6
+ > notice. The breaking rename landed in this release.
9
7
  >
10
- > **What the package you are reading this in actually has:**
8
+ > **Self-check, whatever version you are on:** if `ChatDriver.installAgent` exists, you are on the
9
+ > breaking release and §1–§3 and §5 apply. If only `applyAgent` exists, you are on an earlier one
10
+ > and should not touch your calls.
11
11
  >
12
- > | This guide says | Actually available today |
13
- > | --- | --- |
14
- > | `ChatDriver.activateAgent` | ✓ — available, fires the lifecycle hooks |
15
- > | `ChatDriver.agentSession` | ✓ — available (`@internal`) |
16
- > | `getActiveAgent` / `primeRestoredAgentState` implemented by both drivers | ✓ — and `setPinnedAgent` / `setFlowOwner` are on the interface now, all four **optional** |
17
- > | `ChatDriver.installAgent` | ✗ — **still `ChatDriver.applyAgent`, unchanged** |
18
- > | `pinned-changed` is dispatched | ✗ — still dead wiring (§7.2 of `code-health.md`) |
12
+ > **What breaks:** `ChatDriver.applyAgent` is gone (§1), and four `AiDriver` members are now
13
+ > required rather than optional (§3). Both are compile-time breaks in TypeScript; §1 is **not**
14
+ > caught by a compiler in a JavaScript caller, which is why it shipped alone.
19
15
  >
20
- > **Self-check, whatever version you are on:** if `installAgent` is not present on `ChatDriver`,
21
- > the rename has not landed and you should not touch your `applyAgent` calls. §4 is also **not**
22
- > shipped — it carries its own notice. Everything else here is current.
16
+ > **This ships as a MINOR version, not a major.** The AI surface is `@beta` and has no external
17
+ > consumers — the only callers are migrated by hand alongside the release. So a `^15` range will
18
+ > pick this up: if you call `ChatDriver` directly or implement `AiDriver`, read §1 and §3 before
19
+ > upgrading rather than relying on the version number to warn you.
23
20
  >
24
- > **Nothing you have today breaks on this release.** `applyAgent` keeps working, and every
25
- > `AiDriver` member this ticket touched is declared optional, so an implementor of that interface
26
- > is unaffected too.
27
- >
28
- > GENC-1580 ships in more than one PR. The first delivered an internal decomposition of `main.ts`
29
- > only — five feature controllers, no public API change of any kind. **The agent-lifecycle work
30
- > described here comes in a later PR under the same ticket**, and this notice is removed in the
31
- > same change that makes the guide true.
32
- >
33
- > ---
34
- >
35
- > When it does land: **config-compatible — no changes required to `chatConfig`, `agents`, or
36
- > `persistence`.** No element property, attribute, event or `::part()` changes. Nothing about which
37
- > driver runs your agents changes, so **no runtime behaviour changes**.
38
- >
39
- > You will need to read this if you call `ChatDriver` directly, or if you implement `AiDriver`
40
- > yourself. If you only configure the element, there is nothing here for you.
41
- >
42
- > All surfaces below are `@beta` unless marked otherwise.
43
-
44
- ---
21
+ > **Config-compatible** — no changes to `chatConfig`, `agents` or `persistence`, no element
22
+ > property, attribute, event or `::part()` changes, and **no runtime behaviour changes**. You only
23
+ > need this guide if you call `ChatDriver` directly or implement `AiDriver` yourself.
45
24
 
46
25
  ## Why
47
26
 
@@ -67,8 +46,9 @@ longer depends on which driver you got.
67
46
 
68
47
  ### 1. `ChatDriver.applyAgent` is renamed to `installAgent`
69
48
 
70
- **This is the only breaking change in this release.** `applyAgent` was public `@beta` and appeared
71
- in the published type rollup.
49
+ `applyAgent` was public `@beta` and appeared in the published type rollup, so this is a
50
+ compile-time break for a TypeScript caller — and a silent one for a JavaScript caller, which is
51
+ why it shipped in a release of its own.
72
52
 
73
53
  ```ts
74
54
  // before
@@ -97,6 +77,10 @@ name `installAgent` says what it does, so the next reader has to choose delibera
97
77
  state to restore, `activateAgent`. If you are re-applying a per-turn tool surface on an agent
98
78
  already activated, `installAgent`.
99
79
 
80
+ `OrchestratingDriver`'s own private `applyAgent` was renamed to match. It was never public, so
81
+ there is nothing to do — but the two are related, and leaving them differently named would have
82
+ been worse than the original footgun.
83
+
100
84
  ### 2. `ChatDriver.agentSession` is exposed
101
85
 
102
86
  ```ts
@@ -123,27 +107,90 @@ which is the coupling that used to span two classes.
123
107
 
124
108
  | Member | Was | Now |
125
109
  | --- | --- | --- |
126
- | `getActiveAgent?()` | `OrchestratingDriver` only | both |
127
- | `setPinnedAgent?(name)` | not on the interface | both |
128
- | `setFlowOwner?(name)` | not on the interface | both |
129
- | `primeRestoredAgentState?(snapshots)` | `OrchestratingDriver` only | both |
110
+ | `getActiveAgent()` | `OrchestratingDriver` only, optional | both, **required** |
111
+ | `setPinnedAgent(name)` | not on the interface | both, **required** |
112
+ | `setFlowOwner(name)` | not on the interface | both, **required** |
113
+ | `primeRestoredAgentState(snapshots)` | `OrchestratingDriver` only, optional | both, **required** |
114
+
115
+ **If you only call these, there is nothing to do** — and you can now drop your guards, because the
116
+ interface finally promises what both shipped drivers have always provided:
117
+
118
+ ```ts
119
+ // before
120
+ const reserve = driver.getActiveAgent?.()?.contextReserve;
130
121
 
131
- **Nothing to do, and nothing breaks.** All four are declared **optional**, so a third party
132
- implementing `AiDriver` is unaffected: two were already optional and two are new additions. Keep
133
- your `?.()` guards — they still type-check, and the interface makes no promise a caller can lean
134
- on yet.
122
+ // after
123
+ const reserve = driver.getActiveAgent()?.contextReserve;
124
+ ```
135
125
 
136
- The `?` comes off in the same release as the `installAgent` rename, which is the only breaking
137
- change in this ticket. That is when the guards can go:
126
+ **If you implement `AiDriver` yourself, this is the break.** Add the four methods. Each has a
127
+ sensible inert form if your driver has nothing to route between — record the value and return it,
128
+ or no-op:
138
129
 
139
130
  ```ts
140
- // after the breaking release, not before
141
- const reserve = driver.getActiveAgent().contextReserve;
131
+ import type { AgentConfig } from '@genesislcap/ai-assistant';
132
+
133
+ class MyDriver implements AiDriver {
134
+ // …your existing members — `AiDriver` extends `EventTarget` and has 11 required
135
+ // members in total, so this fragment alone will not compile…
136
+
137
+ private active?: AgentConfig;
138
+
139
+ getActiveAgent() {
140
+ return this.active;
141
+ }
142
+ setPinnedAgent(_name: string | null) {}
143
+ setFlowOwner(_name: string | null) {}
144
+ primeRestoredAgentState(_snapshots: Record<string, unknown>) {}
145
+ }
142
146
  ```
143
147
 
144
- Fourteen members on `AiDriver` remain optional. They are a separate problem, and `ai-driver.ts`
145
- still carries a doc comment claiming the element depends only on this interface, which is not yet
146
- true.
148
+ <details>
149
+ <summary>A complete inert implementation, if you want one to start from</summary>
150
+
151
+ `AiDriver` extends `EventTarget` and has 11 required members. This compiles as written — the four
152
+ above plus the seven that were already required:
153
+
154
+ ```ts
155
+ import type { ChatAttachment, ChatDriverResult, ChatMessage } from '@genesislcap/foundation-ai';
156
+ import type { AgentConfig, AiDriver } from '@genesislcap/ai-assistant';
157
+
158
+ class MyDriver extends EventTarget implements AiDriver {
159
+ private active?: AgentConfig;
160
+
161
+ // the four this release promotes
162
+ getActiveAgent() {
163
+ return this.active;
164
+ }
165
+ setPinnedAgent(_name: string | null) {}
166
+ setFlowOwner(_name: string | null) {}
167
+ primeRestoredAgentState(_snapshots: Record<string, unknown>) {}
168
+
169
+ // the seven that were already required
170
+ async sendMessage(_input: string, _attachments?: ChatAttachment[]): Promise<ChatDriverResult> {
171
+ return { reason: 'done' };
172
+ }
173
+ async continueFromHistory(_primer?: ChatMessage[]): Promise<ChatDriverResult> {
174
+ return { reason: 'done' };
175
+ }
176
+ resolveInteraction(_interactionId: string, _result: unknown) {}
177
+ loadHistory(_messages: ChatMessage[]) {}
178
+ isBusy() {
179
+ return false;
180
+ }
181
+ async getSuggestions(): Promise<string[]> {
182
+ return [];
183
+ }
184
+ getActiveProviderName() {
185
+ return 'my-provider';
186
+ }
187
+ }
188
+ ```
189
+
190
+ </details>
191
+
192
+ Fourteen of `AiDriver`'s 25 members remain optional — compaction, diagnostics, interactions,
193
+ `cancel`/`dispose`. Feature-detect those as before; this release changes nothing about them.
147
194
 
148
195
  ### 4. `pinned-changed` — still not dispatched, still nothing to do
149
196
 
@@ -182,8 +229,9 @@ behaviour on the session-restore path and deserves its own release to be judged
182
229
  a release.
183
230
  - `OrchestratingDriver`'s routing: classification, handoffs, the handoff cap, terminality rules,
184
231
  the continuation tool, and per-agent history masking.
185
- - `OrchestratingDriver.applyAgent` — it was already private, and stays private. It shrinks, but its
186
- name and signature are not part of any contract.
232
+ - `OrchestratingDriver`'s installer — private before and after, so its rename to `installAgent`
233
+ (§1) is invisible to you; it also shrank, but neither its name nor its signature is part of any
234
+ contract.
187
235
  - Session persistence and restore, including the shape written to the persistence provider.
188
236
 
189
237
  ## Known remaining: sub-agent lifecycle
@@ -208,8 +256,10 @@ This release also splits the two largest modules. None of it is consumer-visible
208
256
  package's entry points (`.`, `./config`, `./chat-driver`, `./react`), but if you deep-import from
209
257
  `dist/esm/...` — which is unsupported — paths have moved:
210
258
 
211
- - `chat-driver.ts` sheds its turn-failure taxonomy, interaction handling, turn recording, turn
212
- setup, fold machinery and provider resolution into sibling modules.
259
+ - `chat-driver.ts` sheds its turn-failure taxonomy (`turn-failures.ts`), interaction handling
260
+ (`interaction-controller.ts`), turn recording and diagnostics (`turn-recorder.ts`), fold
261
+ machinery (`fold-controller.ts`) and its constants (`chat-driver.constants.ts`) into sibling
262
+ modules. Turn setup and provider resolution were considered and **not** extracted.
213
263
  - `main.ts` sheds the settings modal, composer controls, cost ledger, budget latching, context gate
214
264
  and diagnostics assembly into controllers.
215
265
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/ai-assistant",
3
3
  "description": "Genesis AI Assistant micro-frontend",
4
- "version": "15.27.0",
4
+ "version": "15.27.1",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -74,26 +74,26 @@
74
74
  }
75
75
  },
76
76
  "devDependencies": {
77
- "@genesislcap/foundation-testing": "15.27.0",
78
- "@genesislcap/genx": "15.27.0",
79
- "@genesislcap/rollup-builder": "15.27.0",
80
- "@genesislcap/ts-builder": "15.27.0",
81
- "@genesislcap/uvu-playwright-builder": "15.27.0",
82
- "@genesislcap/vite-builder": "15.27.0",
83
- "@genesislcap/webpack-builder": "15.27.0",
77
+ "@genesislcap/foundation-testing": "15.27.1",
78
+ "@genesislcap/genx": "15.27.1",
79
+ "@genesislcap/rollup-builder": "15.27.1",
80
+ "@genesislcap/ts-builder": "15.27.1",
81
+ "@genesislcap/uvu-playwright-builder": "15.27.1",
82
+ "@genesislcap/vite-builder": "15.27.1",
83
+ "@genesislcap/webpack-builder": "15.27.1",
84
84
  "@types/dompurify": "^3.0.5",
85
85
  "@types/marked": "^5.0.2",
86
86
  "esbuild": "0.25.12"
87
87
  },
88
88
  "dependencies": {
89
- "@genesislcap/foundation-ai": "15.27.0",
90
- "@genesislcap/foundation-logger": "15.27.0",
91
- "@genesislcap/foundation-notifications": "15.27.0",
92
- "@genesislcap/foundation-redux": "15.27.0",
93
- "@genesislcap/foundation-ui": "15.27.0",
94
- "@genesislcap/foundation-utils": "15.27.0",
95
- "@genesislcap/rapid-design-system": "15.27.0",
96
- "@genesislcap/web-core": "15.27.0",
89
+ "@genesislcap/foundation-ai": "15.27.1",
90
+ "@genesislcap/foundation-logger": "15.27.1",
91
+ "@genesislcap/foundation-notifications": "15.27.1",
92
+ "@genesislcap/foundation-redux": "15.27.1",
93
+ "@genesislcap/foundation-ui": "15.27.1",
94
+ "@genesislcap/foundation-utils": "15.27.1",
95
+ "@genesislcap/rapid-design-system": "15.27.1",
96
+ "@genesislcap/web-core": "15.27.1",
97
97
  "dompurify": "^3.3.1",
98
98
  "marked": "^17.0.3"
99
99
  },