@genesislcap/ai-assistant 14.444.1 → 14.445.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 (47) hide show
  1. package/dist/ai-assistant.api.json +312 -41
  2. package/dist/ai-assistant.d.ts +105 -8
  3. package/dist/dts/components/ai-driver/ai-driver.d.ts +7 -0
  4. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  5. package/dist/dts/components/chat-driver/chat-driver.d.ts +37 -3
  6. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  7. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +5 -3
  8. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  9. package/dist/dts/config/config.d.ts +31 -0
  10. package/dist/dts/config/config.d.ts.map +1 -1
  11. package/dist/dts/config/define-stateful-agent.d.ts +9 -0
  12. package/dist/dts/config/define-stateful-agent.d.ts.map +1 -1
  13. package/dist/dts/config/validate-providers.d.ts +25 -0
  14. package/dist/dts/config/validate-providers.d.ts.map +1 -0
  15. package/dist/dts/config/validate-providers.test.d.ts +2 -0
  16. package/dist/dts/config/validate-providers.test.d.ts.map +1 -0
  17. package/dist/dts/main/main.d.ts +17 -5
  18. package/dist/dts/main/main.d.ts.map +1 -1
  19. package/dist/dts/main/main.styles.d.ts.map +1 -1
  20. package/dist/dts/main/main.template.d.ts.map +1 -1
  21. package/dist/dts/state/ai-assistant-slice.d.ts +14 -1
  22. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  23. package/dist/dts/state/session-store.d.ts +2 -0
  24. package/dist/dts/state/session-store.d.ts.map +1 -1
  25. package/dist/esm/components/chat-driver/chat-driver.js +84 -15
  26. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +23 -4
  27. package/dist/esm/config/define-stateful-agent.js +12 -0
  28. package/dist/esm/config/validate-providers.js +47 -0
  29. package/dist/esm/config/validate-providers.test.js +100 -0
  30. package/dist/esm/main/main.js +57 -10
  31. package/dist/esm/main/main.styles.js +52 -0
  32. package/dist/esm/main/main.template.js +36 -1
  33. package/dist/esm/state/ai-assistant-slice.js +8 -0
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/docs/migration-GENC-1262.md +219 -0
  36. package/package.json +16 -16
  37. package/src/components/ai-driver/ai-driver.ts +8 -0
  38. package/src/components/chat-driver/chat-driver.ts +96 -14
  39. package/src/components/orchestrating-driver/orchestrating-driver.ts +29 -4
  40. package/src/config/config.ts +32 -0
  41. package/src/config/define-stateful-agent.ts +28 -0
  42. package/src/config/validate-providers.test.ts +148 -0
  43. package/src/config/validate-providers.ts +58 -0
  44. package/src/main/main.styles.ts +52 -0
  45. package/src/main/main.template.ts +50 -2
  46. package/src/main/main.ts +51 -7
  47. package/src/state/ai-assistant-slice.ts +24 -1
@@ -1,4 +1,4 @@
1
- import type { ChatInputDuringExecutionMode, ChatMessage } from '@genesislcap/foundation-ai';
1
+ import type { AIProviderRegistryStatusEntry, ChatInputDuringExecutionMode, ChatMessage } from '@genesislcap/foundation-ai';
2
2
  import type { PayloadAction } from '@genesislcap/foundation-redux';
3
3
  import type { AgentConfig } from '../config/config';
4
4
  import type { AiAssistantAnimation, AiAssistantState, SuggestionsState } from '../main/main.types';
@@ -36,6 +36,17 @@ export interface AiAssistantSessionState {
36
36
  sessionCostUsd: number;
37
37
  /** Active model id (e.g. `claude-sonnet-4-6`), resolved on connect. */
38
38
  activeModel: string | undefined;
39
+ /**
40
+ * Name of the AI provider used on the most recent turn — drives "current"
41
+ * marker in the settings panel. `undefined` until the first turn runs.
42
+ */
43
+ activeProviderName: string | undefined;
44
+ /**
45
+ * Snapshot of every registered provider's status, in registration order.
46
+ * Populated on connect (or on settings-open if not yet loaded) and reused
47
+ * for the lifetime of the session — provider registration is static.
48
+ */
49
+ providerStatuses: AIProviderRegistryStatusEntry[];
39
50
  activeAgent: Omit<AgentConfig, 'toolHandlers'> | undefined;
40
51
  /**
41
52
  * Name of the agent the user has pinned via the picker. `null` means the
@@ -87,6 +98,8 @@ export declare const aiAssistantSlice: import("@reduxjs/toolkit").Slice<AiAssist
87
98
  setContextLimit(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<number | undefined>): void;
88
99
  setSessionCostUsd(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<number>): void;
89
100
  setActiveModel(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | undefined>): void;
101
+ setActiveProviderName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | undefined>): void;
102
+ setProviderStatuses(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<AIProviderRegistryStatusEntry[]>): void;
90
103
  setActiveAgent(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<Omit<AgentConfig, "toolHandlers"> | undefined>): void;
91
104
  setPinnedAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | null>): void;
92
105
  setFlowOwnerAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | null>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-assistant-slice.d.ts","sourceRoot":"","sources":["../../../src/state/ai-assistant-slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEnG;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,4BAA4B,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kEAAkE;IAClE,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;IAC3D;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC;AAED,eAAO,MAAM,mBAAmB,EAAE,uBAoBjC,CAAC;AAEF,eAAO,MAAM,gBAAgB;uFAIE,aAAa,CAAC,WAAW,EAAE,CAAC;oFAG/B,aAAa,CAAC,gBAAgB,CAAC;4FAGvB,aAAa,CAAC,OAAO,CAAC;gGAGlB,aAAa,CAAC,OAAO,CAAC;uGAGf,aAAa,CAAC,OAAO,CAAC;gGAG7B,aAAa,CAAC,oBAAoB,EAAE,CAAC;+FAGtC,aAAa,CAAC,gBAAgB,CAAC;4FAGlC,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;2FAGlC,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;6FAG/B,aAAa,CAAC,MAAM,CAAC;0FAGxB,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;0FAGjC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;8FAGxD,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;iGAGzB,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;8FAG/B,aAAa,CAAC,OAAO,CAAC;yFAG3B,aAAa,CAAC,MAAM,CAAC;gGAGd,aAAa,CAAC,WAAW,EAAE,CAAC;+FAG7B,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;4FAG/B,aAAa,CAAC,aAAa,CAAC;+FAGzB,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;oCAKlE,CAAC"}
1
+ {"version":3,"file":"ai-assistant-slice.d.ts","sourceRoot":"","sources":["../../../src/state/ai-assistant-slice.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ declare function makeStore(devTools: boolean | undefined): import("@genesislcap/
12
12
  setContextLimit(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<number | undefined>): void;
13
13
  setSessionCostUsd(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<number>): void;
14
14
  setActiveModel(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | undefined>): void;
15
+ setActiveProviderName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | undefined>): void;
16
+ setProviderStatuses(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<import("@genesislcap/foundation-ai").AIProviderRegistryStatusEntry[]>): void;
15
17
  setActiveAgent(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<Omit<import("..").AgentConfig, "toolHandlers"> | undefined>): void;
16
18
  setPinnedAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | null>): void;
17
19
  setFlowOwnerAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | null>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/state/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAIvD,iBAAS,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;wCAM/C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAMnF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC"}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/state/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAIvD,iBAAS,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;wCAM/C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAMnF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
3
3
  import { agenticActivityBus } from '../../channel/ai-activity-bus';
4
+ import { resolveChatProvider } from '../../config/validate-providers';
4
5
  import { applyHistoryCap } from '../../utils/history-transform';
5
6
  import { logger } from '../../utils/logger';
6
7
  import { TOOL_FOLD_SYMBOL } from '../../utils/tool-fold';
@@ -26,9 +27,9 @@ const HANDOFF_TOOL_RESULT_PLACEHOLDER = 'Handoff to another specialist — routi
26
27
  * @beta
27
28
  */
28
29
  export class ChatDriver extends EventTarget {
29
- constructor(aiProvider, toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS, maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS) {
30
+ constructor(providerRegistry, toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS, maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS) {
30
31
  super();
31
- this.aiProvider = aiProvider;
32
+ this.providerRegistry = providerRegistry;
32
33
  this.maxToolIterations = maxToolIterations;
33
34
  this.history = [];
34
35
  this.busy = false;
@@ -59,6 +60,12 @@ export class ChatDriver extends EventTarget {
59
60
  this.turnSnapshots = [];
60
61
  /** Monotonic counter that survives agent swaps — useful for cross-referencing with history. */
61
62
  this.globalTurnIndex = 0;
63
+ /**
64
+ * Caches validated provider lookups per name within the current agent. Cleared
65
+ * by `applyAgent` so each new agent's static/function-resolved names are
66
+ * validated fresh.
67
+ */
68
+ this.resolvedProviderCache = new Map();
62
69
  if (typeof toolHandlers === 'function') {
63
70
  this.toolHandlersFactory = toolHandlers;
64
71
  this.toolHandlers = {};
@@ -117,10 +124,71 @@ export class ChatDriver extends EventTarget {
117
124
  typeof config.displayName === 'string' ? config.displayName : config.name;
118
125
  this.debugSnapshotter = config.getDebugSnapshot;
119
126
  this.subAgentsMap = new Map(((_c = config.subAgents) !== null && _c !== void 0 ? _c : []).map((s) => [s.name, s]));
127
+ this.activeProviderInput = config.provider;
128
+ this.resolvedProviderCache.clear();
129
+ this.lastResolvedProviderName = undefined;
130
+ // Static validation: resolve the name now so unknown-provider and missing-
131
+ // capability errors fire at agent swap rather than on the first LLM call.
132
+ // Function-form `provider` is validated lazily inside `resolveProviderForTurn`.
133
+ if (typeof config.provider === 'string') {
134
+ this.resolveProviderByName(config.provider, config.name);
135
+ }
120
136
  // Reset fold state when agent changes — each specialist starts fresh
121
137
  this.foldStack = [];
122
138
  this.consecutiveFoldOps = 0;
123
139
  }
140
+ /**
141
+ * Returns the most recently resolved provider name. Falls back to the
142
+ * registry's default when no per-turn resolution has happened yet.
143
+ */
144
+ getActiveProviderName() {
145
+ var _a;
146
+ return (_a = this.lastResolvedProviderName) !== null && _a !== void 0 ? _a : this.providerRegistry.defaultName();
147
+ }
148
+ /**
149
+ * Resolve a named provider against the registry. Cached per-agent so
150
+ * repeated lookups during one agent's lifetime don't re-validate.
151
+ * Validation lives in `resolveChatProvider`; this wrapper just adds the
152
+ * cache.
153
+ */
154
+ resolveProviderByName(name, agentName) {
155
+ const cached = this.resolvedProviderCache.get(name);
156
+ if (cached)
157
+ return cached;
158
+ const provider = resolveChatProvider(this.providerRegistry, name, agentName);
159
+ this.resolvedProviderCache.set(name, provider);
160
+ return provider;
161
+ }
162
+ /**
163
+ * Resolve the provider to use for the current turn. Walks the agent's
164
+ * `provider` selector (static or function form) or falls back to the
165
+ * registry default. Dispatches `provider-changed` when the resolved name
166
+ * differs from the last dispatched value.
167
+ */
168
+ resolveProviderForTurn(ctx) {
169
+ return __awaiter(this, void 0, void 0, function* () {
170
+ var _a;
171
+ let provider;
172
+ let resolvedName;
173
+ if (this.activeProviderInput === undefined) {
174
+ provider = this.providerRegistry.default();
175
+ resolvedName = this.providerRegistry.defaultName();
176
+ }
177
+ else {
178
+ const name = typeof this.activeProviderInput === 'function'
179
+ ? yield this.activeProviderInput(ctx)
180
+ : this.activeProviderInput;
181
+ provider = this.resolveProviderByName(name, (_a = this.activeAgentName) !== null && _a !== void 0 ? _a : '<unknown>');
182
+ resolvedName = name;
183
+ }
184
+ this.lastResolvedProviderName = resolvedName;
185
+ if (resolvedName !== this.lastDispatchedProviderName) {
186
+ this.lastDispatchedProviderName = resolvedName;
187
+ this.dispatchEvent(new CustomEvent('provider-changed', { detail: { name: resolvedName } }));
188
+ }
189
+ return provider;
190
+ });
191
+ }
124
192
  /**
125
193
  * Returns the early-stop result set by `completeSubAgent`, if any.
126
194
  * Called by a parent `ChatDriver` after running this instance as a sub-agent.
@@ -197,8 +265,12 @@ export class ChatDriver extends EventTarget {
197
265
  }
198
266
  getSuggestions(history, prompt, count, allAgentInfo) {
199
267
  return __awaiter(this, void 0, void 0, function* () {
200
- if (!this.aiProvider.prompt) {
201
- logger.warn('ChatDriver: AIProvider does not implement prompt()');
268
+ // Suggestions are an out-of-turn UI helper, not bound to any single agent —
269
+ // always run against the registry default. Best-effort: a default with no
270
+ // `prompt()` just means no suggestions, not a hard error.
271
+ const defaultProvider = this.providerRegistry.default();
272
+ if (!defaultProvider.prompt) {
273
+ logger.warn('ChatDriver: default AI provider does not implement prompt()');
202
274
  return [];
203
275
  }
204
276
  let agentContext = '';
@@ -275,7 +347,7 @@ export class ChatDriver extends EventTarget {
275
347
  `- No numbering, bullets, markdown, emojis, code blocks, or quotes around the suggestion.\n` +
276
348
  `- No preamble, headings, summary, or commentary before or after the list.`;
277
349
  }
278
- const text = yield this.aiProvider.prompt(userMessage, { systemPrompt });
350
+ const text = yield defaultProvider.prompt(userMessage, { systemPrompt });
279
351
  // Lenient parsing as a defensive backstop: even with the strict prompt,
280
352
  // models occasionally slip in numbering, bullets, or surrounding markdown.
281
353
  return (text
@@ -391,10 +463,6 @@ export class ChatDriver extends EventTarget {
391
463
  return __awaiter(this, void 0, void 0, function* () {
392
464
  if (this.busy || (!userInput.trim() && !(attachments === null || attachments === void 0 ? void 0 : attachments.length)))
393
465
  return { reason: 'done' };
394
- if (!this.aiProvider.chat) {
395
- logger.warn('ChatDriver: AIProvider does not implement chat()');
396
- return { reason: 'done' };
397
- }
398
466
  this.busy = true;
399
467
  this.subAgentCompletion = undefined;
400
468
  this.agentReleaseRequested = false;
@@ -477,7 +545,7 @@ export class ChatDriver extends EventTarget {
477
545
  ...contextMessages,
478
546
  ...((_b = subConfig.primerHistory) !== null && _b !== void 0 ? _b : []),
479
547
  ];
480
- const child = new ChatDriver(this.aiProvider);
548
+ const child = new ChatDriver(this.providerRegistry);
481
549
  child.applyAgent(Object.assign(Object.assign({}, subConfig), { primerHistory: effectivePrimer }));
482
550
  // Route interactions back through this driver so widgets render in the
483
551
  // parent's (ultimately the root's) history and resolve via the same
@@ -522,10 +590,6 @@ export class ChatDriver extends EventTarget {
522
590
  return __awaiter(this, void 0, void 0, function* () {
523
591
  if (this.busy)
524
592
  return { reason: 'done' };
525
- if (!this.aiProvider.chat) {
526
- logger.warn('ChatDriver: AIProvider does not implement chat()');
527
- return { reason: 'done' };
528
- }
529
593
  this.busy = true;
530
594
  this.subAgentCompletion = undefined;
531
595
  agenticActivityBus.publish('tool-loop-start', undefined);
@@ -762,10 +826,15 @@ export class ChatDriver extends EventTarget {
762
826
  tools: this.toolDefinitions.length ? this.toolDefinitions : undefined,
763
827
  attachments: attachmentsForCall,
764
828
  };
829
+ // Resolve the active provider for this turn. Static names were validated
830
+ // in `applyAgent`; function-form names are validated on first resolution
831
+ // here and cached for the agent's lifetime.
832
+ // oxlint-disable-next-line no-await-in-loop
833
+ const activeProvider = yield this.resolveProviderForTurn(promptCtx);
765
834
  let response;
766
835
  try {
767
836
  // oxlint-disable-next-line no-await-in-loop
768
- response = yield this.aiProvider.chat(historyForCall, userInputForCall, options);
837
+ response = yield activeProvider.chat(historyForCall, userInputForCall, options);
769
838
  }
770
839
  catch (e) {
771
840
  if (e instanceof MalformedFunctionCallError) {
@@ -1,4 +1,5 @@
1
1
  import { __awaiter } from "tslib";
2
+ import { validateStaticAgentProviders } from '../../config/validate-providers';
2
3
  import { transformHistoryForAgent } from '../../utils/history-transform';
3
4
  import { logger } from '../../utils/logger';
4
5
  import { ChatDriver, REQUEST_CONTINUATION_TOOL, } from '../chat-driver/chat-driver';
@@ -50,10 +51,10 @@ function buildFallbackSystemPrompt(fallback, specialists) {
50
51
  * @beta
51
52
  */
52
53
  export class OrchestratingDriver extends EventTarget {
53
- constructor(aiProvider, agents, options = {}) {
54
+ constructor(providerRegistry, agents, options = {}) {
54
55
  var _a, _b, _c, _d;
55
56
  super();
56
- this.aiProvider = aiProvider;
57
+ this.providerRegistry = providerRegistry;
57
58
  this.agents = agents;
58
59
  /**
59
60
  * Aborted on driver disposal. Threaded into `AgentLifecycleContext.signal`
@@ -80,6 +81,10 @@ export class OrchestratingDriver extends EventTarget {
80
81
  this.classifierHistoryLength =
81
82
  (_c = options.classifierHistoryLength) !== null && _c !== void 0 ? _c : DEFAULT_CLASSIFIER_HISTORY_LENGTH;
82
83
  this.classifierRetries = (_d = options.classifierRetries) !== null && _d !== void 0 ? _d : DEFAULT_CLASSIFIER_RETRIES;
84
+ // Static-name validation: walk every agent (and nested sub-agents). Any
85
+ // `provider: '<name>'` that isn't registered, or resolves to a provider
86
+ // without `chat()`, throws now rather than at first turn.
87
+ validateStaticAgentProviders(agents, providerRegistry);
83
88
  // Specialists drive the classifier. `excludeFromClassifier` agents are still
84
89
  // resolvable by name (so manual pinning works) but never auto-routed.
85
90
  this.specialists = agents.filter(isSpecialist).filter((a) => !a.excludeFromClassifier);
@@ -90,7 +95,7 @@ export class OrchestratingDriver extends EventTarget {
90
95
  const rawFallback = fallbacks[0];
91
96
  this.fallback = rawFallback
92
97
  ? Object.assign(Object.assign({}, rawFallback), { systemPrompt: buildFallbackSystemPrompt(rawFallback, this.specialists) }) : undefined;
93
- this.chatDriver = new ChatDriver(aiProvider, {}, [], undefined, undefined, options.maxToolIterations, options.maxFoldOperations, options.maxTurnSnapshots);
98
+ this.chatDriver = new ChatDriver(providerRegistry, {}, [], undefined, undefined, options.maxToolIterations, options.maxFoldOperations, options.maxTurnSnapshots);
94
99
  // Proxy events from the shared driver
95
100
  this.chatDriver.addEventListener('history-updated', (e) => {
96
101
  this.dispatchEvent(new CustomEvent('history-updated', { detail: e.detail }));
@@ -110,6 +115,9 @@ export class OrchestratingDriver extends EventTarget {
110
115
  this.chatDriver.addEventListener('interaction-stop', (e) => {
111
116
  this.dispatchEvent(new CustomEvent('interaction-stop', { detail: e.detail }));
112
117
  });
118
+ this.chatDriver.addEventListener('provider-changed', (e) => {
119
+ this.dispatchEvent(new CustomEvent('provider-changed', { detail: e.detail }));
120
+ });
113
121
  }
114
122
  resolveInteraction(interactionId, result) {
115
123
  this.chatDriver.resolveInteraction(interactionId, result);
@@ -117,6 +125,10 @@ export class OrchestratingDriver extends EventTarget {
117
125
  isBusy() {
118
126
  return this.chatDriver.isBusy();
119
127
  }
128
+ /** Currently active provider name from the underlying ChatDriver. */
129
+ getActiveProviderName() {
130
+ return this.chatDriver.getActiveProviderName();
131
+ }
120
132
  /**
121
133
  * Pins routing to a specific agent by name. While pinned, the classifier is
122
134
  * skipped and the continuation tool is hidden from the agent's tool list, so
@@ -427,8 +439,15 @@ export class OrchestratingDriver extends EventTarget {
427
439
  systemPrompt: classifierPrompt,
428
440
  tools: [routingTool],
429
441
  };
442
+ // Classification is orchestrator-level, not bound to any single agent —
443
+ // always run against the registry default.
444
+ const classifierProvider = this.providerRegistry.default();
445
+ if (!classifierProvider.chat) {
446
+ logger.warn('OrchestratingDriver: default AI provider does not implement chat() — cannot classify.');
447
+ break;
448
+ }
430
449
  // oxlint-disable-next-line no-await-in-loop
431
- const response = yield this.aiProvider.chat([], input, options);
450
+ const response = yield classifierProvider.chat([], input, options);
432
451
  const tc = (_b = response.toolCalls) === null || _b === void 0 ? void 0 : _b[0];
433
452
  const index = (tc === null || tc === void 0 ? void 0 : tc.name) === 'select_agent' ? tc.args.agent_index : -1;
434
453
  if (index >= 0 && index < this.specialists.length) {
@@ -107,6 +107,17 @@ export function defineStatefulAgent(opts) {
107
107
  return opts.displayName(Object.assign(Object.assign({}, ctx), { state }));
108
108
  })
109
109
  : undefined;
110
+ // Function-form `provider` needs `state` injected the same way the other
111
+ // per-turn resolvers do; the static-string form is passed through unchanged
112
+ // and validated up-front by OrchestratingDriver.
113
+ const wrappedProvider = typeof opts.provider === 'function'
114
+ ? (ctx) => __awaiter(this, void 0, void 0, function* () {
115
+ if (!state) {
116
+ throw new Error(`Stateful agent "${opts.name}" provider called before init`);
117
+ }
118
+ return opts.provider(Object.assign(Object.assign({}, ctx), { state }));
119
+ })
120
+ : opts.provider;
110
121
  const base = {
111
122
  name: opts.name,
112
123
  displayName: wrappedDisplayName,
@@ -116,6 +127,7 @@ export function defineStatefulAgent(opts) {
116
127
  chatInputDuringExecution: opts.chatInputDuringExecution,
117
128
  toolDefinitions: wrappedTools,
118
129
  toolHandlers: wrappedHandlers,
130
+ provider: wrappedProvider,
119
131
  onActivate: (ctx) => __awaiter(this, void 0, void 0, function* () {
120
132
  state = yield opts.init(ctx);
121
133
  // Sample once with the init state to fail loud on fold misuse at the
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Resolve a provider name against the registry and assert it implements
3
+ * `chat()` — the capability the chat tool-loop depends on. Throws with a
4
+ * message naming the agent so misconfigured agents are easy to track down.
5
+ *
6
+ * Used by both the up-front static walk in `OrchestratingDriver` and by
7
+ * `ChatDriver`'s per-turn resolver for function-form `provider` values.
8
+ *
9
+ * @internal
10
+ */
11
+ export function resolveChatProvider(registry, providerName, agentName) {
12
+ const provider = registry.get(providerName);
13
+ if (!provider) {
14
+ const available = registry.names().join(', ') || '(none)';
15
+ throw new Error(`Agent "${agentName}" references unknown provider "${providerName}". Registered: ${available}`);
16
+ }
17
+ if (!provider.chat) {
18
+ throw new Error(`Agent "${agentName}" resolved to provider "${providerName}" which does not implement chat().`);
19
+ }
20
+ return provider;
21
+ }
22
+ /**
23
+ * Walk an agent tree (including nested `subAgents`) and validate every static
24
+ * `provider: string` against the registry. Function-form `provider` values are
25
+ * deferred — they validate on first resolution inside the driver.
26
+ *
27
+ * Throws on the first failure, with the same message shape produced by
28
+ * {@link resolveChatProvider}.
29
+ *
30
+ * @internal
31
+ */
32
+ export function validateStaticAgentProviders(agents, registry) {
33
+ const visited = new Set();
34
+ const walk = (agent) => {
35
+ var _a;
36
+ if (visited.has(agent))
37
+ return;
38
+ visited.add(agent);
39
+ if (typeof agent.provider === 'string') {
40
+ resolveChatProvider(registry, agent.provider, agent.name);
41
+ }
42
+ for (const sub of (_a = agent.subAgents) !== null && _a !== void 0 ? _a : [])
43
+ walk(sub);
44
+ };
45
+ for (const root of agents)
46
+ walk(root);
47
+ }
@@ -0,0 +1,100 @@
1
+ import { __awaiter } from "tslib";
2
+ import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
3
+ import { resolveChatProvider, validateStaticAgentProviders } from './validate-providers';
4
+ const chatProvider = () => ({
5
+ chat: () => __awaiter(void 0, void 0, void 0, function* () { return ({ role: 'assistant', content: '' }); }),
6
+ });
7
+ const promptOnlyProvider = () => ({
8
+ prompt: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
9
+ });
10
+ /**
11
+ * Minimal in-test registry — implements only what the validation helpers
12
+ * touch (`get`, `names`). Avoids depending on foundation-ai's concrete
13
+ * registry impl, and keeps these tests Node-runnable.
14
+ */
15
+ const makeRegistry = (providers) => {
16
+ const map = new Map(Object.entries(providers));
17
+ return {
18
+ get: (name) => map.get(name),
19
+ default: () => map.values().next().value,
20
+ defaultName: () => map.keys().next().value,
21
+ names: () => [...map.keys()],
22
+ getStatus: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
23
+ listStatuses: () => __awaiter(void 0, void 0, void 0, function* () { return []; }),
24
+ };
25
+ };
26
+ const specialist = (overrides) => (Object.assign({ name: overrides.name, description: 'test specialist' }, overrides));
27
+ // ---------------------------------------------------------------------------
28
+ // resolveChatProvider
29
+ // ---------------------------------------------------------------------------
30
+ const resolve = createLogicSuite('resolveChatProvider');
31
+ resolve('returns the provider when the name is registered and has chat()', () => {
32
+ const fast = chatProvider();
33
+ const registry = makeRegistry({ fast });
34
+ assert.is(resolveChatProvider(registry, 'fast', 'A'), fast);
35
+ });
36
+ resolve('throws on an unknown provider name with the agent name in the message', () => {
37
+ const registry = makeRegistry({ fast: chatProvider() });
38
+ assert.throws(() => resolveChatProvider(registry, 'deep', 'A'), /Agent "A" references unknown provider "deep"/);
39
+ });
40
+ resolve('throws including the registered names so the misconfiguration is debuggable', () => {
41
+ const registry = makeRegistry({
42
+ fast: chatProvider(),
43
+ deep: chatProvider(),
44
+ cheap: chatProvider(),
45
+ });
46
+ assert.throws(() => resolveChatProvider(registry, 'typo', 'A'), /Registered: fast, deep, cheap/);
47
+ });
48
+ resolve('throws when the resolved provider does not implement chat()', () => {
49
+ const registry = makeRegistry({ chrome: promptOnlyProvider() });
50
+ assert.throws(() => resolveChatProvider(registry, 'chrome', 'A'), /Agent "A" resolved to provider "chrome" which does not implement chat\(\)/);
51
+ });
52
+ resolve.run();
53
+ // ---------------------------------------------------------------------------
54
+ // validateStaticAgentProviders
55
+ // ---------------------------------------------------------------------------
56
+ const validate = createLogicSuite('validateStaticAgentProviders');
57
+ validate('accepts agents with no `provider` field (back-compat path)', () => {
58
+ const registry = makeRegistry({ default: chatProvider() });
59
+ assert.not.throws(() => validateStaticAgentProviders([specialist({ name: 'A' })], registry));
60
+ });
61
+ validate('accepts agents that name a registered chat-capable provider', () => {
62
+ const registry = makeRegistry({ fast: chatProvider(), deep: chatProvider() });
63
+ assert.not.throws(() => validateStaticAgentProviders([specialist({ name: 'A', provider: 'deep' })], registry));
64
+ });
65
+ validate('throws when a top-level agent references an unknown provider', () => {
66
+ const registry = makeRegistry({ fast: chatProvider() });
67
+ assert.throws(() => validateStaticAgentProviders([specialist({ name: 'A', provider: 'deep' })], registry), /Agent "A" references unknown provider "deep"/);
68
+ });
69
+ validate('recurses into subAgents — bad provider on a sub-agent throws', () => {
70
+ const registry = makeRegistry({ fast: chatProvider() });
71
+ const sub = specialist({ name: 'Sub', provider: 'missing' });
72
+ assert.throws(() => validateStaticAgentProviders([specialist({ name: 'A', subAgents: [sub] })], registry), /Agent "Sub" references unknown provider "missing"/);
73
+ });
74
+ validate('recurses through multiple levels — bad provider on a deeply-nested sub-agent throws', () => {
75
+ const registry = makeRegistry({ fast: chatProvider() });
76
+ const deepest = specialist({ name: 'Deepest', provider: 'missing' });
77
+ const middle = specialist({ name: 'Middle', subAgents: [deepest] });
78
+ assert.throws(() => validateStaticAgentProviders([specialist({ name: 'Top', subAgents: [middle] })], registry), /Agent "Deepest" references unknown provider "missing"/);
79
+ });
80
+ validate('defers function-form `provider` — it is not invoked at validation time', () => {
81
+ const registry = makeRegistry({ fast: chatProvider() });
82
+ const agent = specialist({
83
+ name: 'A',
84
+ // If validateStaticAgentProviders eagerly resolved this, it would throw
85
+ // here. The contract is that function-form is deferred to the driver's
86
+ // per-turn resolver.
87
+ provider: () => {
88
+ throw new Error('function-form provider should not be invoked at validation time');
89
+ },
90
+ });
91
+ assert.not.throws(() => validateStaticAgentProviders([agent], registry));
92
+ });
93
+ validate('shared sub-agent across two parents validates cleanly', () => {
94
+ const registry = makeRegistry({ fast: chatProvider() });
95
+ const shared = specialist({ name: 'Shared', provider: 'fast' });
96
+ const parentA = specialist({ name: 'ParentA', subAgents: [shared] });
97
+ const parentB = specialist({ name: 'ParentB', subAgents: [shared] });
98
+ assert.not.throws(() => validateStaticAgentProviders([parentA, parentB], registry));
99
+ });
100
+ validate.run();
@@ -22,7 +22,7 @@
22
22
  // =============================================================================
23
23
  var FoundationAiAssistant_1;
24
24
  import { __awaiter, __decorate, __rest } from "tslib";
25
- import { AIProvider } from '@genesislcap/foundation-ai';
25
+ import { AIProviderRegistry } from '@genesislcap/foundation-ai';
26
26
  import { avoidTreeShaking } from '@genesislcap/foundation-utils';
27
27
  import { customElement, html, GenesisElement, observable, volatile, attr, } from '@genesislcap/web-core';
28
28
  import { agenticActivityBus } from '../channel/ai-activity-bus';
@@ -81,8 +81,11 @@ function stripHandlers(agent) {
81
81
  * Foundation AI Assistant component.
82
82
  *
83
83
  * @remarks
84
- * Inject an `AIProvider` through the DI container. Pass agent configuration via the `agents`
85
- * property. The component creates a `ChatDriver` (single agent) or `OrchestratingDriver`
84
+ * Register one or more AI providers via `registerAIProviders` from
85
+ * `@genesislcap/foundation-ai`; this element resolves the `AIProviderRegistry`
86
+ * through the DI container. Pass agent configuration via the `agents` property —
87
+ * each agent can override which registered provider it uses via `provider:`.
88
+ * The component creates a `ChatDriver` (single agent) or `OrchestratingDriver`
86
89
  * (multiple agents) to manage the conversation loop.
87
90
  *
88
91
  * Popout/collapse coordination uses `agenticActivityBus` topics `chat-popout` and `chat-popin` — not DOM `CustomEvent`s on this element.
@@ -422,6 +425,24 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
422
425
  var _a;
423
426
  (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveModel(value);
424
427
  }
428
+ /** Name of the AI provider used on the most recent turn. */
429
+ get activeProviderName() {
430
+ var _a;
431
+ return (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.activeProviderName;
432
+ }
433
+ set activeProviderName(value) {
434
+ var _a;
435
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveProviderName(value);
436
+ }
437
+ /** Per-provider status snapshot used by the settings panel. */
438
+ get providerStatuses() {
439
+ var _a, _b;
440
+ return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.providerStatuses) !== null && _b !== void 0 ? _b : [];
441
+ }
442
+ set providerStatuses(value) {
443
+ var _a;
444
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setProviderStatuses(value);
445
+ }
425
446
  get inputValue() {
426
447
  var _a, _b;
427
448
  return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.inputValue) !== null && _b !== void 0 ? _b : '';
@@ -627,7 +648,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
627
648
  // this via `agent-changed` once applyAgent fires on first sendMessage.
628
649
  this.activeAgent = agents[0];
629
650
  }
630
- return new OrchestratingDriver(this.aiProvider, agents, {
651
+ return new OrchestratingDriver(this.providerRegistry, agents, {
631
652
  sessionKey: (_b = this.getStateKey()) !== null && _b !== void 0 ? _b : '',
632
653
  maxHandoffs: agent.maxHandoffs,
633
654
  classifierHistoryLength: agent.classifierHistoryLength,
@@ -637,7 +658,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
637
658
  maxTurnSnapshots: agent.maxTurnSnapshots,
638
659
  });
639
660
  }
640
- return new ChatDriver(this.aiProvider, {}, [], undefined, undefined, agent.maxToolIterations, agent.maxFoldOperations, agent.maxTurnSnapshots);
661
+ return new ChatDriver(this.providerRegistry, {}, [], undefined, undefined, agent.maxToolIterations, agent.maxFoldOperations, agent.maxTurnSnapshots);
641
662
  }
642
663
  /**
643
664
  * Attaches event listeners to the current driver. Stores a cleanup function
@@ -697,11 +718,19 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
697
718
  (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeInputOverride({ id: interactionId });
698
719
  }
699
720
  };
721
+ const onProviderChanged = (e) => {
722
+ const { name } = e.detail;
723
+ this.activeProviderName = name;
724
+ // Status (model id / context limit) belongs to the active provider — refresh
725
+ // the displayed values so the header reflects the provider that just took over.
726
+ void this.resolveContextLimit();
727
+ };
700
728
  driver.addEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated);
701
729
  driver.addEventListener('sub-agent-start', onSubAgentStart);
702
730
  driver.addEventListener('sub-agent-stop', onSubAgentStop);
703
731
  driver.addEventListener('interaction-start', onInteractionStart);
704
732
  driver.addEventListener('interaction-stop', onInteractionStop);
733
+ driver.addEventListener('provider-changed', onProviderChanged);
705
734
  const cleanups = [
706
735
  () => driver.removeEventListener('history-updated', onHistoryUpdated),
707
736
  () => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
@@ -709,6 +738,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
709
738
  () => driver.removeEventListener('sub-agent-stop', onSubAgentStop),
710
739
  () => driver.removeEventListener('interaction-start', onInteractionStart),
711
740
  () => driver.removeEventListener('interaction-stop', onInteractionStop),
741
+ () => driver.removeEventListener('provider-changed', onProviderChanged),
712
742
  ];
713
743
  if (driver instanceof OrchestratingDriver) {
714
744
  // Restore the user pin and flow-owner lock from the session store onto
@@ -837,6 +867,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
837
867
  }
838
868
  this.fetchSuggestions();
839
869
  void this.resolveContextLimit();
870
+ void this.loadProviderStatuses();
840
871
  if (this.messagesEl) {
841
872
  this._scrollListener = () => {
842
873
  this._userScrolledAway =
@@ -885,17 +916,33 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
885
916
  }
886
917
  resolveContextLimit() {
887
918
  return __awaiter(this, void 0, void 0, function* () {
888
- var _a, _b;
919
+ var _a;
889
920
  try {
890
- const status = yield ((_b = (_a = this.aiProvider).getStatus) === null || _b === void 0 ? void 0 : _b.call(_a));
921
+ // Resolve status for the currently active provider when a driver is up
922
+ // (so per-agent provider swaps update the displayed model/limit); fall
923
+ // back to the registry default at startup before any turn has run.
924
+ const activeName = (_a = this.driver) === null || _a === void 0 ? void 0 : _a.getActiveProviderName();
925
+ const status = activeName
926
+ ? yield this.providerRegistry.getStatus(activeName)
927
+ : yield this.providerRegistry.getStatus();
891
928
  this.contextLimit = status === null || status === void 0 ? void 0 : status.contextLimit;
892
929
  this.activeModel = status === null || status === void 0 ? void 0 : status.model;
893
930
  }
894
- catch (_c) {
931
+ catch (_b) {
895
932
  // Non-fatal — context limit / model display simply won't show
896
933
  }
897
934
  });
898
935
  }
936
+ loadProviderStatuses() {
937
+ return __awaiter(this, void 0, void 0, function* () {
938
+ try {
939
+ this.providerStatuses = yield this.providerRegistry.listStatuses();
940
+ }
941
+ catch (_a) {
942
+ // Non-fatal — settings panel just won't list providers
943
+ }
944
+ });
945
+ }
899
946
  chatConfigChanged() {
900
947
  this.syncShowingSplash();
901
948
  }
@@ -1493,8 +1540,8 @@ FoundationAiAssistant.DEFAULT_LOADING_DELAY_S = 5;
1493
1540
  FoundationAiAssistant.DEFAULT_SUGGESTION_COUNT = 3;
1494
1541
  FoundationAiAssistant.MS_PER_SECOND = 1000;
1495
1542
  __decorate([
1496
- AIProvider
1497
- ], FoundationAiAssistant.prototype, "aiProvider", void 0);
1543
+ AIProviderRegistry
1544
+ ], FoundationAiAssistant.prototype, "providerRegistry", void 0);
1498
1545
  __decorate([
1499
1546
  observable
1500
1547
  ], FoundationAiAssistant.prototype, "designSystemPrefix", void 0);