@genesislcap/ai-assistant 14.494.0 → 14.496.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/ai-assistant.api.json +588 -214
  2. package/dist/ai-assistant.d.ts +157 -6
  3. package/dist/chat-driver.cjs +5608 -0
  4. package/dist/chat-driver.cjs.map +7 -0
  5. package/dist/chat-driver.mjs +5566 -0
  6. package/dist/chat-driver.mjs.map +7 -0
  7. package/dist/custom-elements.json +713 -333
  8. package/dist/dts/channel/ai-activity-bus.d.ts +36 -0
  9. package/dist/dts/channel/ai-activity-bus.d.ts.map +1 -1
  10. package/dist/dts/channel/ai-activity-channel.d.ts +10 -1
  11. package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
  12. package/dist/dts/chat-driver-node.d.ts +28 -0
  13. package/dist/dts/chat-driver-node.d.ts.map +1 -0
  14. package/dist/dts/components/chat-driver/chat-driver.d.ts +66 -5
  15. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  16. package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +1 -1
  17. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +3 -0
  18. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  19. package/dist/dts/config/config.d.ts +37 -2
  20. package/dist/dts/config/config.d.ts.map +1 -1
  21. package/dist/dts/index.d.ts +1 -0
  22. package/dist/dts/index.d.ts.map +1 -1
  23. package/dist/dts/main/main.d.ts.map +1 -1
  24. package/dist/dts/state/debug-event-log.d.ts +8 -12
  25. package/dist/dts/state/debug-event-log.d.ts.map +1 -1
  26. package/dist/esm/channel/ai-activity-bus.js +48 -12
  27. package/dist/esm/chat-driver-node.js +33 -0
  28. package/dist/esm/components/chat-driver/chat-driver.js +77 -25
  29. package/dist/esm/components/chat-driver/chat-driver.test.js +160 -36
  30. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +7 -1
  31. package/dist/esm/main/main.js +8 -1
  32. package/dist/esm/main/popout-interaction-gate.test.js +6 -10
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +29 -19
  35. package/scripts/build-chat-driver-node.mjs +42 -0
  36. package/src/channel/ai-activity-bus.ts +64 -10
  37. package/src/channel/ai-activity-channel.ts +9 -1
  38. package/src/chat-driver-node.ts +54 -0
  39. package/src/components/chat-driver/chat-driver.test.ts +237 -50
  40. package/src/components/chat-driver/chat-driver.ts +151 -39
  41. package/src/components/orchestrating-driver/orchestrating-driver.ts +10 -11
  42. package/src/config/config.ts +40 -1
  43. package/src/index.ts +1 -0
  44. package/src/main/main.ts +8 -11
  45. package/src/main/popout-interaction-gate.test.ts +6 -11
  46. package/src/state/debug-event-log.ts +9 -18
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/flowing-waves-indicator.ts","../src/components/halo-overlay.ts","../src/components/plasma-orb-indicator.ts","../src/components/waves-indicator.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.compact.test.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.pin.test.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/components/settings-modal/index.ts","../src/components/settings-modal/settings-modal.styles.ts","../src/components/settings-modal/settings-modal.template.ts","../src/config/config.ts","../src/config/define-stateful-agent.test.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/main/popout-interaction-gate.test.ts","../src/provider/ai-provider-switcher.ts","../src/provider/assistant-app-settings.ts","../src/state/ai-assistant-slice.test.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.test.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.test.ts","../src/state/driver-registry.ts","../src/state/interaction-context.test.ts","../src/state/interaction-context.ts","../src/state/session-store.ts","../src/state/persistence/diagnostics-cursors.test.ts","../src/state/persistence/diagnostics-cursors.ts","../src/state/persistence/diagnostics.test.ts","../src/state/persistence/diagnostics.ts","../src/state/persistence/index.ts","../src/state/persistence/persister-registry.ts","../src/state/persistence/session-persistence-provider.test.ts","../src/state/persistence/session-persistence-provider.ts","../src/state/persistence/session-persistence.integration.test.ts","../src/state/persistence/session-persister.test.ts","../src/state/persistence/session-persister.ts","../src/state/persistence/session-snapshot.test.ts","../src/state/persistence/session-snapshot.ts","../src/state/persistence/stateful-restore.e2e.test.ts","../src/styles/ai-colours.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/types/interaction-context.ts","../src/utils/animated-panel-toggle.ts","../src/utils/animation-exclusivity.test.ts","../src/utils/animation-exclusivity.ts","../src/utils/collect-session-models.test.ts","../src/utils/collect-session-models.ts","../src/utils/condense-history.test.ts","../src/utils/condense-history.ts","../src/utils/cost-session-history.test.ts","../src/utils/cost-session-history.ts","../src/utils/derive-cost-session-title.test.ts","../src/utils/derive-cost-session-title.ts","../src/utils/flatten-sub-agent-messages.test.ts","../src/utils/flatten-sub-agent-messages.ts","../src/utils/history-transform.test.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/resolve-cost-history-config.test.ts","../src/utils/resolve-cost-history-config.ts","../src/utils/resolve-preference-baseline.test.ts","../src/utils/resolve-preference-baseline.ts","../src/utils/strip-agent-handlers.test.ts","../src/utils/strip-agent-handlers.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/sum-tokens.test.ts","../src/utils/sum-tokens.ts","../src/utils/tool-fold.ts","../src/utils/with-timeout.ts"],"version":"5.9.2"}
1
+ {"root":["../src/chat-driver-node.ts","../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/flowing-waves-indicator.ts","../src/components/halo-overlay.ts","../src/components/plasma-orb-indicator.ts","../src/components/waves-indicator.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.compact.test.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.pin.test.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/components/settings-modal/index.ts","../src/components/settings-modal/settings-modal.styles.ts","../src/components/settings-modal/settings-modal.template.ts","../src/config/config.ts","../src/config/define-stateful-agent.test.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/main/popout-interaction-gate.test.ts","../src/provider/ai-provider-switcher.ts","../src/provider/assistant-app-settings.ts","../src/state/ai-assistant-slice.test.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.test.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.test.ts","../src/state/driver-registry.ts","../src/state/interaction-context.test.ts","../src/state/interaction-context.ts","../src/state/session-store.ts","../src/state/persistence/diagnostics-cursors.test.ts","../src/state/persistence/diagnostics-cursors.ts","../src/state/persistence/diagnostics.test.ts","../src/state/persistence/diagnostics.ts","../src/state/persistence/index.ts","../src/state/persistence/persister-registry.ts","../src/state/persistence/session-persistence-provider.test.ts","../src/state/persistence/session-persistence-provider.ts","../src/state/persistence/session-persistence.integration.test.ts","../src/state/persistence/session-persister.test.ts","../src/state/persistence/session-persister.ts","../src/state/persistence/session-snapshot.test.ts","../src/state/persistence/session-snapshot.ts","../src/state/persistence/stateful-restore.e2e.test.ts","../src/styles/ai-colours.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/types/interaction-context.ts","../src/utils/animated-panel-toggle.ts","../src/utils/animation-exclusivity.test.ts","../src/utils/animation-exclusivity.ts","../src/utils/collect-session-models.test.ts","../src/utils/collect-session-models.ts","../src/utils/condense-history.test.ts","../src/utils/condense-history.ts","../src/utils/cost-session-history.test.ts","../src/utils/cost-session-history.ts","../src/utils/derive-cost-session-title.test.ts","../src/utils/derive-cost-session-title.ts","../src/utils/flatten-sub-agent-messages.test.ts","../src/utils/flatten-sub-agent-messages.ts","../src/utils/history-transform.test.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/resolve-cost-history-config.test.ts","../src/utils/resolve-cost-history-config.ts","../src/utils/resolve-preference-baseline.test.ts","../src/utils/resolve-preference-baseline.ts","../src/utils/strip-agent-handlers.test.ts","../src/utils/strip-agent-handlers.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/sum-tokens.test.ts","../src/utils/sum-tokens.ts","../src/utils/tool-fold.ts","../src/utils/with-timeout.ts"],"version":"5.9.2"}
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": "14.494.0",
4
+ "version": "14.496.0",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -27,6 +27,11 @@
27
27
  "types": "./dist/dts/config/index.d.ts",
28
28
  "default": "./dist/esm/config/index.js"
29
29
  },
30
+ "./chat-driver": {
31
+ "types": "./dist/dts/chat-driver-node.d.ts",
32
+ "import": "./dist/chat-driver.mjs",
33
+ "require": "./dist/chat-driver.cjs"
34
+ },
30
35
  "./package.json": "./package.json",
31
36
  "./react": {
32
37
  "types": "./dist/dts/react.d.ts",
@@ -38,11 +43,15 @@
38
43
  "*": {
39
44
  "config": [
40
45
  "./dist/dts/config/index.d.ts"
46
+ ],
47
+ "chat-driver": [
48
+ "./dist/dts/chat-driver-node.d.ts"
41
49
  ]
42
50
  }
43
51
  },
44
52
  "scripts": {
45
- "build": "genx build -b ts",
53
+ "build": "genx build -b ts && node ./scripts/build-chat-driver-node.mjs",
54
+ "build:chat-driver": "node ./scripts/build-chat-driver-node.mjs",
46
55
  "build:webpack": "genx build",
47
56
  "build:webpack:stats": "genx analyze",
48
57
  "circular": "npx -y madge --extensions ts --circular ./src",
@@ -64,25 +73,26 @@
64
73
  }
65
74
  },
66
75
  "devDependencies": {
67
- "@genesislcap/foundation-testing": "14.494.0",
68
- "@genesislcap/genx": "14.494.0",
69
- "@genesislcap/rollup-builder": "14.494.0",
70
- "@genesislcap/ts-builder": "14.494.0",
71
- "@genesislcap/uvu-playwright-builder": "14.494.0",
72
- "@genesislcap/vite-builder": "14.494.0",
73
- "@genesislcap/webpack-builder": "14.494.0",
76
+ "@genesislcap/foundation-testing": "14.496.0",
77
+ "@genesislcap/genx": "14.496.0",
78
+ "@genesislcap/rollup-builder": "14.496.0",
79
+ "@genesislcap/ts-builder": "14.496.0",
80
+ "@genesislcap/uvu-playwright-builder": "14.496.0",
81
+ "@genesislcap/vite-builder": "14.496.0",
82
+ "@genesislcap/webpack-builder": "14.496.0",
74
83
  "@types/dompurify": "^3.0.5",
75
- "@types/marked": "^5.0.2"
84
+ "@types/marked": "^5.0.2",
85
+ "esbuild": "0.25.12"
76
86
  },
77
87
  "dependencies": {
78
- "@genesislcap/foundation-ai": "14.494.0",
79
- "@genesislcap/foundation-logger": "14.494.0",
80
- "@genesislcap/foundation-notifications": "14.494.0",
81
- "@genesislcap/foundation-redux": "14.494.0",
82
- "@genesislcap/foundation-ui": "14.494.0",
83
- "@genesislcap/foundation-utils": "14.494.0",
84
- "@genesislcap/rapid-design-system": "14.494.0",
85
- "@genesislcap/web-core": "14.494.0",
88
+ "@genesislcap/foundation-ai": "14.496.0",
89
+ "@genesislcap/foundation-logger": "14.496.0",
90
+ "@genesislcap/foundation-notifications": "14.496.0",
91
+ "@genesislcap/foundation-redux": "14.496.0",
92
+ "@genesislcap/foundation-ui": "14.496.0",
93
+ "@genesislcap/foundation-utils": "14.496.0",
94
+ "@genesislcap/rapid-design-system": "14.496.0",
95
+ "@genesislcap/web-core": "14.496.0",
86
96
  "dompurify": "^3.3.1",
87
97
  "marked": "^17.0.3"
88
98
  },
@@ -95,5 +105,5 @@
95
105
  "access": "public"
96
106
  },
97
107
  "customElements": "dist/custom-elements.json",
98
- "gitHead": "df845b67dcc681aa3775bdc87b9d91b404791868"
108
+ "gitHead": "2ae3ec60cab8440c6beb1da84e2d47239b11e9b5"
99
109
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Bundles the Node-safe `./chat-driver` subpath into self-contained, Node-loadable dual output
3
+ * (`dist/chat-driver.mjs` + `dist/chat-driver.cjs`).
4
+ *
5
+ * Why a bundler and not just tsc: `genx build -b ts` emits bundler-target ESM (extensionless
6
+ * relative imports + a bare `tslib` import) that Node's own loader can't resolve. This step bundles
7
+ * `src/chat-driver-node.ts` and its closure (including `@genesislcap/foundation-ai`, `tslib`, and
8
+ * the logger) inline, so a plain Node/CommonJS host can `require`/`import` the driver stack with no
9
+ * bundler and no jsdom. The FAST/DOM layer is never in this entry's closure, and `foundation-ai`'s
10
+ * DI module (its only `@microsoft/fast-foundation` importer) tree-shakes out.
11
+ *
12
+ * Runs after `genx build -b ts` (which produces the matching `dist/dts/chat-driver-node.d.ts`).
13
+ */
14
+ import { dirname, resolve } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import * as esbuild from 'esbuild';
17
+
18
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
19
+ const entry = resolve(packageRoot, 'src/chat-driver-node.ts');
20
+
21
+ /** @type {import('esbuild').BuildOptions} */
22
+ const shared = {
23
+ entryPoints: [entry],
24
+ bundle: true,
25
+ platform: 'node',
26
+ target: 'node22',
27
+ // Self-contained: bundle third-party deps (foundation-ai, tslib, logger) inline so the artifact
28
+ // is Node-loadable standalone. Node built-ins are external automatically under platform: 'node'.
29
+ sourcemap: 'linked',
30
+ logLevel: 'info',
31
+ };
32
+
33
+ await esbuild.build({
34
+ ...shared,
35
+ format: 'esm',
36
+ outfile: resolve(packageRoot, 'dist/chat-driver.mjs'),
37
+ });
38
+ await esbuild.build({
39
+ ...shared,
40
+ format: 'cjs',
41
+ outfile: resolve(packageRoot, 'dist/chat-driver.cjs'),
42
+ });
@@ -10,10 +10,37 @@ export interface AgenticActivityBusConfig {
10
10
  crossTabEvents?: (keyof AgenticActivityEvents)[];
11
11
  /** BroadcastChannel name. Only used when crossTabEvents is non-empty. */
12
12
  channelName?: string;
13
+ /**
14
+ * Lazy alternative to {@link AgenticActivityBusConfig.channelName}: called the first
15
+ * time a cross-tab channel is actually needed, in a browser only. Deferring the name
16
+ * computation keeps side effects (e.g. `sessionStorage` reads) out of module-eval and
17
+ * off the non-browser path. Ignored if `channelName` is also set.
18
+ */
19
+ channelNameFactory?: () => string;
13
20
  }
14
21
 
15
22
  type AnyHandler = (detail: any) => void;
16
23
 
24
+ /**
25
+ * Minimal activity-bus surface the drivers depend on. `ChatDriver`/`OrchestratingDriver`
26
+ * only ever *publish* — accepting this narrow interface (rather than importing the module
27
+ * singleton) lets a headless/server or unit-test host omit the bus entirely, so no live
28
+ * `BroadcastChannel` is ever opened on that path. See {@link NOOP_ACTIVITY_BUS}.
29
+ *
30
+ * @beta
31
+ */
32
+ export interface ActivityBus {
33
+ publish<K extends keyof AgenticActivityEvents>(type: K, detail: AgenticActivityEvents[K]): void;
34
+ }
35
+
36
+ /**
37
+ * No-op {@link ActivityBus}. The default when no bus is injected (Node, tests, headless
38
+ * server use) — publishing goes nowhere and nothing is left open to tear down.
39
+ *
40
+ * @beta
41
+ */
42
+ export const NOOP_ACTIVITY_BUS: ActivityBus = { publish: () => {} };
43
+
17
44
  /**
18
45
  * Typed in-process event bus for AI assistant activity events.
19
46
  *
@@ -31,7 +58,9 @@ type AnyHandler = (detail: any) => void;
31
58
  export class AgenticActivityBus {
32
59
  private readonly subscribers = new Map<string, Set<AnyHandler>>();
33
60
  private readonly crossTabEvents: Set<string>;
61
+ private readonly channelNameFactory?: () => string;
34
62
  private channel?: BroadcastChannel;
63
+ private channelInitialised = false;
35
64
 
36
65
  /**
37
66
  * @param config - Optional configuration. Omit to create an in-memory-only bus.
@@ -39,13 +68,28 @@ export class AgenticActivityBus {
39
68
  */
40
69
  constructor(config?: AgenticActivityBusConfig) {
41
70
  this.crossTabEvents = new Set(config?.crossTabEvents ?? []);
42
- if (this.crossTabEvents.size > 0 && config?.channelName) {
43
- this.channel = new BroadcastChannel(config.channelName);
44
- this.channel.onmessage = (ev) => {
45
- const { type, detail } = (ev.data ?? {}) as { type?: string; detail?: unknown };
46
- if (type) this.notifyLocal(type, detail);
47
- };
48
- }
71
+ this.channelNameFactory =
72
+ config?.channelName != null ? () => config.channelName! : config?.channelNameFactory;
73
+ }
74
+
75
+ /**
76
+ * Lazily open the cross-tab `BroadcastChannel` on first use. Deferred (rather than
77
+ * created in the constructor) so that importing this module, or constructing the bus
78
+ * off-browser (Node, tests, headless server), never opens a live channel or reads
79
+ * `sessionStorage` — both would keep a Node event loop alive and hang the process.
80
+ * Cross-tab forwarding is inherently browser-only, so channel creation is gated on a
81
+ * browser environment; elsewhere the bus stays purely in-process.
82
+ */
83
+ private ensureChannel(): void {
84
+ if (this.channelInitialised) return;
85
+ this.channelInitialised = true;
86
+ const inBrowser = typeof window !== 'undefined' && typeof BroadcastChannel !== 'undefined';
87
+ if (this.crossTabEvents.size === 0 || !this.channelNameFactory || !inBrowser) return;
88
+ this.channel = new BroadcastChannel(this.channelNameFactory());
89
+ this.channel.onmessage = (ev) => {
90
+ const { type, detail } = (ev.data ?? {}) as { type?: string; detail?: unknown };
91
+ if (type) this.notifyLocal(type, detail);
92
+ };
49
93
  }
50
94
 
51
95
  /**
@@ -58,6 +102,9 @@ export class AgenticActivityBus {
58
102
  type: K,
59
103
  handler: (detail: AgenticActivityEvents[K]) => void,
60
104
  ): () => void {
105
+ // Open the cross-tab channel (browser only) so this subscriber receives events
106
+ // forwarded from other tabs; no-op off-browser.
107
+ this.ensureChannel();
61
108
  let handlers = this.subscribers.get(type as string);
62
109
  if (!handlers) {
63
110
  handlers = new Set();
@@ -76,8 +123,9 @@ export class AgenticActivityBus {
76
123
  */
77
124
  publish<K extends keyof AgenticActivityEvents>(type: K, detail: AgenticActivityEvents[K]): void {
78
125
  this.notifyLocal(type as string, detail);
79
- if (this.crossTabEvents.has(type as string) && this.channel) {
80
- this.channel.postMessage({ type, detail });
126
+ if (this.crossTabEvents.has(type as string)) {
127
+ this.ensureChannel();
128
+ this.channel?.postMessage({ type, detail });
81
129
  }
82
130
  }
83
131
 
@@ -124,6 +172,9 @@ export class AgenticActivityBus {
124
172
  * instances on the same origin.
125
173
  */
126
174
  function getTabScopedChannelName(base: string): string {
175
+ // Belt-and-braces: this only runs in a browser (see `ensureChannel`), but guard anyway
176
+ // so a direct call off-browser never throws a `sessionStorage is not defined` ReferenceError.
177
+ if (typeof sessionStorage === 'undefined') return `${base}-nostorage`;
127
178
  const key = `${base}-tab-id`;
128
179
  let id = sessionStorage.getItem(key);
129
180
  if (!id) {
@@ -151,5 +202,8 @@ function getTabScopedChannelName(base: string): string {
151
202
  */
152
203
  export const agenticActivityBus = new AgenticActivityBus({
153
204
  crossTabEvents: ['halo-start', 'halo-stop', 'tool-loop-start', 'tool-loop-end'],
154
- channelName: getTabScopedChannelName('foundation-ai-activity'),
205
+ // Lazy: the tab-scoped name (and its `sessionStorage` read) is computed on first
206
+ // cross-tab use in a browser, not at module-eval — so importing this module is
207
+ // side-effect-free and Node/test-safe.
208
+ channelNameFactory: () => getTabScopedChannelName('foundation-ai-activity'),
155
209
  });
@@ -1,3 +1,5 @@
1
+ import type { TurnFailureReason } from '@genesislcap/foundation-ai';
2
+
1
3
  /**
2
4
  * Event map for the AI activity bus.
3
5
  *
@@ -59,8 +61,14 @@ export interface AgenticActivityEvents {
59
61
  * spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas
60
62
  * this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending
61
63
  * (telemetry flushes, post-turn workspace transitions, deferred state recomputation).
64
+ *
65
+ * The detail carries the turn's outcome (PTC-0): on a failure it is `{ failureReason }`
66
+ * with the typed `TurnFailureReason`; on a clean completion, a user cancel, a
67
+ * driver dispose, and an agent handoff the detail is `undefined` — the historical shape,
68
+ * kept byte-identical so subscribers that only care about the boundary can keep ignoring
69
+ * it. Structured-cloneable so it survives the cross-tab BroadcastChannel hop.
62
70
  */
63
- 'tool-loop-end': undefined;
71
+ 'tool-loop-end': { failureReason?: TurnFailureReason } | undefined;
64
72
  /**
65
73
  * Fired when a tool handler hands a widget to the user mid-loop and parks awaiting it
66
74
  * (`requestInteraction`) — the turn is suspended between provider calls, with no request
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Node-safe headless entry point for the AI assistant driver stack (`@genesislcap/ai-assistant/chat-driver`).
3
+ *
4
+ * Re-exports ONLY the framework-agnostic pieces — {@link ChatDriver}, `OrchestratingDriver`, the
5
+ * agent-config surface (`defineAgent`, `defineStatefulAgent`, and the config/input types), and the
6
+ * activity-bus types/`NOOP_ACTIVITY_BUS` — WITHOUT the DOM/FAST component layer that the package's
7
+ * main entry (`.`) pulls in via `main/main`. This lets a Node / CommonJS host (server, unit tests,
8
+ * benches) construct and drive `ChatDriver` over `foundation-ai` providers with no jsdom and no
9
+ * custom-element registration.
10
+ *
11
+ * Every module re-exported here is DOM-clean at import time (no `window`/`document`/`sessionStorage`
12
+ * eval-time access, no `@genesislcap/web-core`). The activity bus is imported for its types and the
13
+ * no-op default; its cross-tab `BroadcastChannel` is created lazily and only in a browser, so
14
+ * pulling this entry into Node opens nothing.
15
+ *
16
+ * @packageDocumentation
17
+ * @beta
18
+ */
19
+ export * from './components/chat-driver';
20
+ export * from './components/orchestrating-driver';
21
+ export * from './channel/ai-activity-channel';
22
+ export * from './channel/ai-activity-bus';
23
+ export * from './config/config';
24
+ export * from './config/define-stateful-agent';
25
+ export * from './config/fallback-agents';
26
+
27
+ // Provider-construction surface, re-exported from `@genesislcap/foundation-ai` so a headless
28
+ // consumer builds its registry from the SAME bundled foundation-ai instance the driver uses. That
29
+ // shared identity matters: `ChatDriver`'s malformed/truncated handling does `instanceof` on the
30
+ // transports' error classes, so a second foundation-ai copy (a separate dep) would silently break
31
+ // those checks. NAMED re-exports only — `export *` would drag in the FAST-DI module
32
+ // (`@microsoft/fast-foundation` → `fast-element` → `document` at eval); by naming just these,
33
+ // esbuild tree-shakes the DI module out and the bundle stays Node-loadable.
34
+ export {
35
+ AnthropicProvider,
36
+ AnthropicTransport,
37
+ GeminiProvider,
38
+ GeminiTransport,
39
+ MutableAIProviderRegistry,
40
+ isObservableAIProviderRegistry,
41
+ } from '@genesislcap/foundation-ai';
42
+ export type {
43
+ AIProvider,
44
+ AIProviderRegistry,
45
+ AnthropicModelId,
46
+ CachePolicy,
47
+ ChatFallback,
48
+ ChatMessage,
49
+ ChatRequestOptions,
50
+ ChatToolChoice,
51
+ ChatToolDefinition,
52
+ ChatToolHandlers,
53
+ GeminiModelId,
54
+ } from '@genesislcap/foundation-ai';