@genesislcap/ai-assistant 14.495.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.
- package/dist/ai-assistant.api.json +576 -211
- package/dist/ai-assistant.d.ts +128 -5
- package/dist/chat-driver.cjs +5608 -0
- package/dist/chat-driver.cjs.map +7 -0
- package/dist/chat-driver.mjs +5566 -0
- package/dist/chat-driver.mjs.map +7 -0
- package/dist/custom-elements.json +306 -2
- package/dist/dts/channel/ai-activity-bus.d.ts +36 -0
- package/dist/dts/channel/ai-activity-bus.d.ts.map +1 -1
- package/dist/dts/chat-driver-node.d.ts +28 -0
- package/dist/dts/chat-driver-node.d.ts.map +1 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts +49 -5
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +3 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +37 -2
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/esm/channel/ai-activity-bus.js +48 -12
- package/dist/esm/chat-driver-node.js +33 -0
- package/dist/esm/components/chat-driver/chat-driver.js +28 -14
- package/dist/esm/components/chat-driver/chat-driver.test.js +32 -40
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +7 -1
- package/dist/esm/main/main.js +8 -1
- package/dist/esm/main/popout-interaction-gate.test.js +6 -10
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -19
- package/scripts/build-chat-driver-node.mjs +42 -0
- package/src/channel/ai-activity-bus.ts +64 -10
- package/src/chat-driver-node.ts +54 -0
- package/src/components/chat-driver/chat-driver.test.ts +36 -62
- package/src/components/chat-driver/chat-driver.ts +96 -28
- package/src/components/orchestrating-driver/orchestrating-driver.ts +10 -11
- package/src/config/config.ts +40 -1
- package/src/main/main.ts +8 -11
- package/src/main/popout-interaction-gate.test.ts +6 -11
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
CachePolicy,
|
|
6
6
|
ChatAttachment,
|
|
7
7
|
ChatDriverResult,
|
|
8
|
+
ChatFallback,
|
|
8
9
|
ChatMessage,
|
|
9
10
|
ChatRequestOptions,
|
|
10
11
|
ChatToolCall,
|
|
@@ -24,11 +25,12 @@ import {
|
|
|
24
25
|
MalformedFunctionCallError,
|
|
25
26
|
ResponseTruncatedError,
|
|
26
27
|
} from '@genesislcap/foundation-ai';
|
|
27
|
-
import {
|
|
28
|
+
import { type ActivityBus, NOOP_ACTIVITY_BUS } from '../../channel/ai-activity-bus';
|
|
28
29
|
import type {
|
|
29
30
|
AgentConfig,
|
|
30
31
|
CachePolicyInput,
|
|
31
32
|
ProviderInput,
|
|
33
|
+
ResponseSchemaInput,
|
|
32
34
|
SystemPromptContext,
|
|
33
35
|
SystemPromptInput,
|
|
34
36
|
TailContextInput,
|
|
@@ -192,6 +194,39 @@ interface FoldStackFrame {
|
|
|
192
194
|
previousHandlers: ChatToolHandlers;
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Construction-time configuration for {@link ChatDriver}. Everything except the provider
|
|
199
|
+
* registry is optional — most fields are also settable per-agent via `applyAgent`, so a
|
|
200
|
+
* bare `new ChatDriver(registry)` is valid. Mirrors the `(registry, options)` shape of
|
|
201
|
+
* `OrchestratingDriver`.
|
|
202
|
+
*
|
|
203
|
+
* @beta
|
|
204
|
+
*/
|
|
205
|
+
export interface ChatDriverConfig {
|
|
206
|
+
/** Initial tool handlers (static map or per-turn factory). Default `{}`. */
|
|
207
|
+
toolHandlers?: ToolHandlersInput;
|
|
208
|
+
/** Initial tool definitions (static array or per-turn factory). Default `[]`. */
|
|
209
|
+
toolDefinitions?: ToolDefinitionsInput;
|
|
210
|
+
/** Initial system prompt (string or per-turn resolver). */
|
|
211
|
+
systemPrompt?: SystemPromptInput;
|
|
212
|
+
/** Primer history prepended to the conversation. */
|
|
213
|
+
primerHistory?: ChatMessage[];
|
|
214
|
+
/** Hard cap on tool-loop iterations. Default `50`. */
|
|
215
|
+
maxToolIterations?: number;
|
|
216
|
+
/** Hard cap on fold operations. Default `5`. */
|
|
217
|
+
maxFoldOperations?: number;
|
|
218
|
+
/** Ring-buffer size for per-turn snapshots. Default `400`. */
|
|
219
|
+
maxTurnSnapshots?: number;
|
|
220
|
+
/** Session identity used to file meta events onto the shared debug-log timeline. */
|
|
221
|
+
sessionKey?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Activity bus for lifecycle/halo/tool-loop events. Injected by the browser host
|
|
224
|
+
* (the shared cross-tab singleton); omitted off-browser (Node, tests, headless), where
|
|
225
|
+
* it defaults to {@link NOOP_ACTIVITY_BUS} so no `BroadcastChannel` is ever opened.
|
|
226
|
+
*/
|
|
227
|
+
activityBus?: ActivityBus;
|
|
228
|
+
}
|
|
229
|
+
|
|
195
230
|
/**
|
|
196
231
|
* Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
|
|
197
232
|
* Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
|
|
@@ -454,6 +489,16 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
454
489
|
* the resolved string in a `<system-reminder>` marker and injects it at the message tail.
|
|
455
490
|
*/
|
|
456
491
|
private activeTailContextInput?: TailContextInput;
|
|
492
|
+
/**
|
|
493
|
+
* Active agent's structured-output schema selector (static value or per-turn resolver).
|
|
494
|
+
* When it resolves to a schema, the model's final answer is constrained to it this turn.
|
|
495
|
+
*/
|
|
496
|
+
private activeResponseSchemaInput?: ResponseSchemaInput;
|
|
497
|
+
/**
|
|
498
|
+
* Active agent's refusal-fallback chain (static). Passed through to the provider so a refused
|
|
499
|
+
* turn (e.g. Fable 5) is re-run on the next model server-side.
|
|
500
|
+
*/
|
|
501
|
+
private activeFallbacks?: ChatFallback[];
|
|
457
502
|
/**
|
|
458
503
|
* Active agent's unresolved-tool hook, captured from `applyAgent`. Consulted
|
|
459
504
|
* only when a tool call cannot be dispatched (a stale or hallucinated name);
|
|
@@ -499,19 +544,32 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
499
544
|
*/
|
|
500
545
|
private unsubscribeRegistry?: () => void;
|
|
501
546
|
|
|
547
|
+
/** Hard cap on tool-loop iterations. */
|
|
548
|
+
private readonly maxToolIterations: number;
|
|
549
|
+
/** Session identity used to file meta events onto the shared debug-log timeline. */
|
|
550
|
+
private readonly sessionKey: string;
|
|
551
|
+
/** Injected activity bus; defaults to a no-op off-browser (Node/tests/headless). */
|
|
552
|
+
private readonly activityBus: ActivityBus;
|
|
553
|
+
|
|
502
554
|
constructor(
|
|
503
555
|
private readonly providerRegistry: AIProviderRegistry,
|
|
504
|
-
|
|
505
|
-
toolDefinitions: ToolDefinitionsInput = [],
|
|
506
|
-
systemPrompt?: SystemPromptInput,
|
|
507
|
-
primerHistory?: ChatMessage[],
|
|
508
|
-
private readonly maxToolIterations: number = DEFAULT_MAX_TOOL_ITERATIONS,
|
|
509
|
-
maxFoldOperations: number = DEFAULT_MAX_FOLD_OPERATIONS,
|
|
510
|
-
maxTurnSnapshots: number = DEFAULT_MAX_TURN_SNAPSHOTS,
|
|
511
|
-
/** Session identity used to file meta events onto the shared debug-log timeline. */
|
|
512
|
-
private readonly sessionKey: string = '',
|
|
556
|
+
config: ChatDriverConfig = {},
|
|
513
557
|
) {
|
|
514
558
|
super();
|
|
559
|
+
const {
|
|
560
|
+
toolHandlers = {},
|
|
561
|
+
toolDefinitions = [],
|
|
562
|
+
systemPrompt,
|
|
563
|
+
primerHistory,
|
|
564
|
+
maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS,
|
|
565
|
+
maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS,
|
|
566
|
+
maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS,
|
|
567
|
+
sessionKey = '',
|
|
568
|
+
activityBus = NOOP_ACTIVITY_BUS,
|
|
569
|
+
} = config;
|
|
570
|
+
this.maxToolIterations = maxToolIterations;
|
|
571
|
+
this.sessionKey = sessionKey;
|
|
572
|
+
this.activityBus = activityBus;
|
|
515
573
|
if (typeof toolHandlers === 'function') {
|
|
516
574
|
this.toolHandlersFactory = toolHandlers;
|
|
517
575
|
this.toolHandlers = {};
|
|
@@ -705,6 +763,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
705
763
|
this.activeToolChoiceInput = config.toolChoice;
|
|
706
764
|
this.activeCachePolicyInput = config.cachePolicy;
|
|
707
765
|
this.activeTailContextInput = config.tailContext;
|
|
766
|
+
this.activeResponseSchemaInput = config.responseSchema;
|
|
767
|
+
this.activeFallbacks = config.fallbacks;
|
|
708
768
|
this.activeOnUnresolvedTool = config.onUnresolvedTool;
|
|
709
769
|
this.resolvedProviderCache.clear();
|
|
710
770
|
this.lastResolvedProviderName = undefined;
|
|
@@ -1297,7 +1357,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1297
1357
|
// "actively computing" from "parked awaiting the user" — the latter is a
|
|
1298
1358
|
// safe window for actions disallowed mid-request (e.g. switching provider
|
|
1299
1359
|
// during a long journey step). Paired with `interaction-resolved`.
|
|
1300
|
-
|
|
1360
|
+
this.activityBus.publish('interaction-requested', undefined);
|
|
1301
1361
|
if (chatInputDuringExecution) {
|
|
1302
1362
|
this.dispatchEvent(
|
|
1303
1363
|
new CustomEvent('interaction-start', {
|
|
@@ -1372,7 +1432,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1372
1432
|
// The park is ending and the loop is about to resume computing — paired
|
|
1373
1433
|
// with `interaction-requested`. Fires for every resolution path (user
|
|
1374
1434
|
// completion, timeout, cancellation), since all route through here.
|
|
1375
|
-
|
|
1435
|
+
this.activityBus.publish('interaction-resolved', undefined);
|
|
1376
1436
|
interaction.resolve(result);
|
|
1377
1437
|
this.pendingInteractions.delete(interactionId);
|
|
1378
1438
|
// Tear down the live context on RESOLVE (not on element unmount) — this closes
|
|
@@ -1440,7 +1500,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1440
1500
|
phase: 'sendMessage',
|
|
1441
1501
|
agent: this.activeAgentName,
|
|
1442
1502
|
});
|
|
1443
|
-
|
|
1503
|
+
this.activityBus.publish('tool-loop-start', undefined);
|
|
1444
1504
|
|
|
1445
1505
|
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1446
1506
|
let result: ChatDriverResult = { reason: 'done' };
|
|
@@ -1470,7 +1530,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1470
1530
|
});
|
|
1471
1531
|
this.busy = false;
|
|
1472
1532
|
this.endTurn();
|
|
1473
|
-
|
|
1533
|
+
this.activityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1474
1534
|
}
|
|
1475
1535
|
}
|
|
1476
1536
|
|
|
@@ -1636,17 +1696,12 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1636
1696
|
// be harvested into THIS session on completion and then discarded.
|
|
1637
1697
|
const invocationId = crypto.randomUUID();
|
|
1638
1698
|
const childSessionKey = `${this.sessionKey}::sub:${invocationId}`;
|
|
1639
|
-
const child = new ChatDriver(
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
undefined,
|
|
1646
|
-
undefined,
|
|
1647
|
-
undefined,
|
|
1648
|
-
childSessionKey,
|
|
1649
|
-
);
|
|
1699
|
+
const child = new ChatDriver(this.providerRegistry, {
|
|
1700
|
+
sessionKey: childSessionKey,
|
|
1701
|
+
// Inherit the parent's bus so the sub-agent's tool-loop events still surface
|
|
1702
|
+
// (off-browser this is the shared no-op).
|
|
1703
|
+
activityBus: this.activityBus,
|
|
1704
|
+
});
|
|
1650
1705
|
// Mark before the first turn so the child forces tool use and reports a
|
|
1651
1706
|
// typed failure (rather than user-facing text) if it never completes.
|
|
1652
1707
|
child.markAsSubAgent();
|
|
@@ -1815,7 +1870,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1815
1870
|
phase: 'continueFromHistory',
|
|
1816
1871
|
agent: this.activeAgentName,
|
|
1817
1872
|
});
|
|
1818
|
-
|
|
1873
|
+
this.activityBus.publish('tool-loop-start', undefined);
|
|
1819
1874
|
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1820
1875
|
let result: ChatDriverResult = { reason: 'done' };
|
|
1821
1876
|
try {
|
|
@@ -1844,7 +1899,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1844
1899
|
});
|
|
1845
1900
|
this.busy = false;
|
|
1846
1901
|
this.endTurn();
|
|
1847
|
-
|
|
1902
|
+
this.activityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1848
1903
|
}
|
|
1849
1904
|
}
|
|
1850
1905
|
|
|
@@ -2178,13 +2233,20 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2178
2233
|
// provider is resolved — static value or a function of the turn context
|
|
2179
2234
|
// (which carries the live state for stateful agents). Resolved before the
|
|
2180
2235
|
// snapshot so the debug log records the exact request config the model saw.
|
|
2181
|
-
const [
|
|
2236
|
+
const [
|
|
2237
|
+
resolvedTemperature,
|
|
2238
|
+
resolvedToolChoice,
|
|
2239
|
+
resolvedCachePolicy,
|
|
2240
|
+
resolvedTailContext,
|
|
2241
|
+
resolvedResponseSchema,
|
|
2242
|
+
] =
|
|
2182
2243
|
// oxlint-disable-next-line no-await-in-loop
|
|
2183
2244
|
await Promise.all([
|
|
2184
2245
|
this.resolveTurnInput<number>(this.activeTemperatureInput, promptCtx),
|
|
2185
2246
|
this.resolveTurnInput<ChatToolChoice>(this.activeToolChoiceInput, promptCtx),
|
|
2186
2247
|
this.resolveTurnInput<CachePolicy>(this.activeCachePolicyInput, promptCtx),
|
|
2187
2248
|
this.resolveTurnInput<string>(this.activeTailContextInput, promptCtx),
|
|
2249
|
+
this.resolveTurnInput<object | undefined>(this.activeResponseSchemaInput, promptCtx),
|
|
2188
2250
|
]);
|
|
2189
2251
|
// The system prompt is always just the agent's resolved prompt — byte-stable, so it can be
|
|
2190
2252
|
// cached. The framework's volatile additions (fold suffix, retry nudge) and the agent's tail
|
|
@@ -2235,6 +2297,12 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2235
2297
|
cachePolicy: resolvedCachePolicy,
|
|
2236
2298
|
// Framed volatile context injected at the message tail (never stored). Undefined → none.
|
|
2237
2299
|
tailContext,
|
|
2300
|
+
// Structured-output schema for this turn (agent/state-resolved). When set, the transport
|
|
2301
|
+
// constrains the final answer to it natively where the model supports it. Undefined → free text.
|
|
2302
|
+
responseSchema: resolvedResponseSchema,
|
|
2303
|
+
// Refusal-fallback chain (e.g. Fable 5 → Opus 4.8). Passed to the provider; applied
|
|
2304
|
+
// server-side where supported. Undefined → no fallback.
|
|
2305
|
+
fallbacks: this.activeFallbacks,
|
|
2238
2306
|
};
|
|
2239
2307
|
|
|
2240
2308
|
// Resolve the active provider for this turn. Static names were validated
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
ChatMessage,
|
|
6
6
|
ChatRequestOptions,
|
|
7
7
|
} from '@genesislcap/foundation-ai';
|
|
8
|
+
import type { ActivityBus } from '../../channel/ai-activity-bus';
|
|
8
9
|
import type {
|
|
9
10
|
AgentConfig,
|
|
10
11
|
FallbackAgentConfig,
|
|
@@ -137,6 +138,8 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
137
138
|
maxToolIterations?: number;
|
|
138
139
|
maxFoldOperations?: number;
|
|
139
140
|
maxTurnSnapshots?: number;
|
|
141
|
+
/** Activity bus passed through to the inner ChatDriver (browser host injects the singleton). */
|
|
142
|
+
activityBus?: ActivityBus;
|
|
140
143
|
} = {},
|
|
141
144
|
) {
|
|
142
145
|
super();
|
|
@@ -166,17 +169,13 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
166
169
|
? { ...rawFallback, systemPrompt: buildFallbackSystemPrompt(rawFallback, this.specialists) }
|
|
167
170
|
: undefined;
|
|
168
171
|
|
|
169
|
-
this.chatDriver = new ChatDriver(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
options.maxFoldOperations,
|
|
177
|
-
options.maxTurnSnapshots,
|
|
178
|
-
this.sessionKey,
|
|
179
|
-
);
|
|
172
|
+
this.chatDriver = new ChatDriver(providerRegistry, {
|
|
173
|
+
maxToolIterations: options.maxToolIterations,
|
|
174
|
+
maxFoldOperations: options.maxFoldOperations,
|
|
175
|
+
maxTurnSnapshots: options.maxTurnSnapshots,
|
|
176
|
+
sessionKey: this.sessionKey,
|
|
177
|
+
activityBus: options.activityBus,
|
|
178
|
+
});
|
|
180
179
|
|
|
181
180
|
// Proxy events from the shared driver
|
|
182
181
|
this.chatDriver.addEventListener('history-updated', (e: Event) => {
|
package/src/config/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
CachePolicy,
|
|
3
|
+
ChatFallback,
|
|
3
4
|
ChatInputDuringExecutionMode,
|
|
4
5
|
ChatMessage,
|
|
5
6
|
ChatToolChoice,
|
|
@@ -7,7 +8,7 @@ import type {
|
|
|
7
8
|
ChatToolHandlers,
|
|
8
9
|
} from '@genesislcap/foundation-ai';
|
|
9
10
|
|
|
10
|
-
export type { CachePolicy, ChatInputDuringExecutionMode, ChatToolChoice };
|
|
11
|
+
export type { CachePolicy, ChatFallback, ChatInputDuringExecutionMode, ChatToolChoice };
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Context passed to `onActivate` / `onDeactivate` lifecycle hooks on an agent.
|
|
@@ -174,6 +175,25 @@ export type CachePolicyInput =
|
|
|
174
175
|
*/
|
|
175
176
|
export type TailContextInput = string | ((ctx: SystemPromptContext) => string | Promise<string>);
|
|
176
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Structured-output schema for an agent. When set, the model's final (non-tool) answer is
|
|
180
|
+
* constrained to this JSON Schema instead of free text. Either a static schema or a function
|
|
181
|
+
* resolved each tool-loop iteration — return the schema on the turn(s) it should apply and
|
|
182
|
+
* `undefined` otherwise, so "whole-loop" vs "finalize-turn" enforcement is just what the
|
|
183
|
+
* resolver returns (e.g. `({ state }) => state.machine.matches('finalizing') ? schema : undefined`).
|
|
184
|
+
*
|
|
185
|
+
* Orthogonal to {@link ToolChoiceInput}: an agent can carry both `tools` and a `responseSchema`.
|
|
186
|
+
* Each provider applies it natively where possible (Anthropic `output_config.format`, Gemini
|
|
187
|
+
* JSON mode) and degrades gracefully otherwise. The schema is a plain JSON Schema object; keep to
|
|
188
|
+
* the portable subset providers share (`additionalProperties: false`, explicit `required`, enums,
|
|
189
|
+
* `anyOf` for nullables — no numeric/string constraints or recursion).
|
|
190
|
+
*
|
|
191
|
+
* @beta
|
|
192
|
+
*/
|
|
193
|
+
export type ResponseSchemaInput =
|
|
194
|
+
| object
|
|
195
|
+
| ((ctx: SystemPromptContext) => object | undefined | Promise<object | undefined>);
|
|
196
|
+
|
|
177
197
|
/**
|
|
178
198
|
* Context passed to an agent's `onUnresolvedTool` hook when the model calls a
|
|
179
199
|
* tool the driver cannot dispatch.
|
|
@@ -337,6 +357,25 @@ interface BaseAgentConfig {
|
|
|
337
357
|
* @beta
|
|
338
358
|
*/
|
|
339
359
|
tailContext?: TailContextInput;
|
|
360
|
+
/**
|
|
361
|
+
* Structured-output schema for this agent. When resolved to a schema on a turn, the model's
|
|
362
|
+
* final (non-tool) answer is constrained to it instead of free text. Composes with `tools`;
|
|
363
|
+
* resolved per turn like {@link BaseAgentConfig.cachePolicy}, so returning `undefined` on
|
|
364
|
+
* working turns and the schema on the finalize turn gives finalize-turn enforcement for free.
|
|
365
|
+
* See {@link ResponseSchemaInput}.
|
|
366
|
+
*
|
|
367
|
+
* @beta
|
|
368
|
+
*/
|
|
369
|
+
responseSchema?: ResponseSchemaInput;
|
|
370
|
+
/**
|
|
371
|
+
* Refusal-fallback chain for this agent (provider-neutral). If the model declines a turn
|
|
372
|
+
* (`stop_reason: 'refusal'` — e.g. Fable 5 safety classifiers), the provider re-runs it on the
|
|
373
|
+
* next listed model. Typical use: a Fable 5 agent with `[{ model: 'claude-opus-4-8' }]`. Static
|
|
374
|
+
* (not per-turn); providers that support it apply it server-side, others ignore it.
|
|
375
|
+
*
|
|
376
|
+
* @beta
|
|
377
|
+
*/
|
|
378
|
+
fallbacks?: ChatFallback[];
|
|
340
379
|
/**
|
|
341
380
|
* Optional hook consulted when the model calls a tool the driver cannot
|
|
342
381
|
* dispatch — either a *stale* tool (advertised earlier this activation but
|
package/src/main/main.ts
CHANGED
|
@@ -1249,20 +1249,17 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1249
1249
|
maxToolIterations: agent.maxToolIterations,
|
|
1250
1250
|
maxFoldOperations: agent.maxFoldOperations,
|
|
1251
1251
|
maxTurnSnapshots: agent.maxTurnSnapshots,
|
|
1252
|
+
activityBus: agenticActivityBus,
|
|
1252
1253
|
});
|
|
1253
1254
|
}
|
|
1254
1255
|
|
|
1255
|
-
return new ChatDriver(
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
agent.maxFoldOperations,
|
|
1263
|
-
agent.maxTurnSnapshots,
|
|
1264
|
-
this.getStateKey() ?? '',
|
|
1265
|
-
);
|
|
1256
|
+
return new ChatDriver(this.providerRegistry, {
|
|
1257
|
+
maxToolIterations: agent.maxToolIterations,
|
|
1258
|
+
maxFoldOperations: agent.maxFoldOperations,
|
|
1259
|
+
maxTurnSnapshots: agent.maxTurnSnapshots,
|
|
1260
|
+
sessionKey: this.getStateKey() ?? '',
|
|
1261
|
+
activityBus: agenticActivityBus,
|
|
1262
|
+
});
|
|
1266
1263
|
}
|
|
1267
1264
|
|
|
1268
1265
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
2
2
|
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
3
|
-
import { agenticActivityBus } from '../channel/ai-activity-bus';
|
|
4
3
|
import { FoundationAiAssistant } from './main';
|
|
5
4
|
|
|
6
5
|
// Hold a reference so the custom-element registration isn't tree-shaken.
|
|
@@ -14,19 +13,15 @@ FoundationAiAssistant;
|
|
|
14
13
|
// (`main.template.ts`). This suite pins the GATE LOGIC (`hasActivePendingInteraction`)
|
|
15
14
|
// that binding depends on, so a change to when the gate opens/closes is caught.
|
|
16
15
|
//
|
|
17
|
-
// Note: this deliberately does NOT mount the assistant. Mounting runs
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
16
|
+
// Note: this deliberately does NOT mount the assistant. Mounting runs `connectedCallback`,
|
|
17
|
+
// which subscribes to `agenticActivityBus` — opening a cross-tab `BroadcastChannel` in a browser
|
|
18
|
+
// env and leaving the runner's event loop open. A getter-level test needs no mount and stays fast
|
|
19
|
+
// + reliable. `document.createElement` only upgrades the element (constructor) — it does not
|
|
20
|
+
// connect — so nothing subscribes, and the bus opens its channel lazily on first use, so none is
|
|
21
|
+
// ever created here (no teardown needed).
|
|
23
22
|
|
|
24
23
|
const Suite = createLogicSuite('FoundationAiAssistant popout interaction gate');
|
|
25
24
|
|
|
26
|
-
Suite.after(() => {
|
|
27
|
-
agenticActivityBus.close();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
25
|
/** A fresh (unconnected) element with a fake session store wired in. */
|
|
31
26
|
function elementWith(state: string, messages: ChatMessage[]): FoundationAiAssistant {
|
|
32
27
|
const el = document.createElement('foundation-ai-assistant') as FoundationAiAssistant;
|