@genesislcap/ai-assistant 14.434.0 → 14.436.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 +1513 -70
- package/dist/ai-assistant.d.ts +367 -7
- package/dist/dts/components/ai-driver/ai-driver.d.ts +8 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +79 -3
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +23 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +106 -2
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/config/define-stateful-agent.d.ts +115 -0
- package/dist/dts/config/define-stateful-agent.d.ts.map +1 -0
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +36 -4
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +126 -11
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +192 -33
- package/dist/esm/config/define-stateful-agent.js +174 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/main/main.js +164 -21
- package/dist/esm/main/main.template.js +2 -11
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/components/ai-driver/ai-driver.ts +9 -0
- package/src/components/chat-driver/chat-driver.ts +178 -8
- package/src/components/orchestrating-driver/orchestrating-driver.ts +191 -17
- package/src/config/config.ts +112 -2
- package/src/config/define-stateful-agent.ts +293 -0
- package/src/index.ts +1 -0
- package/src/main/main.template.ts +2 -9
- package/src/main/main.ts +167 -14
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { __awaiter, __rest } from "tslib";
|
|
2
|
+
import { TOOL_FOLD_SYMBOL } from '../utils/tool-fold';
|
|
3
|
+
/**
|
|
4
|
+
* Walk a state object and snapshot any machine-shaped values found on it.
|
|
5
|
+
* Recognises foundation-state-machine instances by structural shape rather
|
|
6
|
+
* than `instanceof`, so the helper stays free of a runtime dep on
|
|
7
|
+
* foundation-state-machine.
|
|
8
|
+
*
|
|
9
|
+
* Strips the `errors`/`error` framework noise from each machine's context —
|
|
10
|
+
* these are foundation-state-machine internals (an ErrorMap instance and its
|
|
11
|
+
* last-error pointer) that bloat the debug payload without aiding debugging.
|
|
12
|
+
*/
|
|
13
|
+
function defaultStatefulDebugSnapshot(state) {
|
|
14
|
+
var _a;
|
|
15
|
+
if (!state || typeof state !== 'object')
|
|
16
|
+
return state;
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const [key, val] of Object.entries(state)) {
|
|
19
|
+
if (val && typeof val === 'object' && 'state' in val && 'context' in val && 'complete' in val) {
|
|
20
|
+
const m = val;
|
|
21
|
+
const ctx = (_a = m.context) !== null && _a !== void 0 ? _a : {};
|
|
22
|
+
const { errors: _e, error: _err } = ctx, userContext = __rest(ctx, ["errors", "error"]);
|
|
23
|
+
result[key] = {
|
|
24
|
+
state: m.state,
|
|
25
|
+
context: userContext,
|
|
26
|
+
complete: m.complete,
|
|
27
|
+
output: m.output,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return Object.keys(result).length
|
|
32
|
+
? result
|
|
33
|
+
: '<no auto-snapshot — state has no machine-shaped properties; provide getDebugSnapshot>';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build an `AgentConfig` whose `systemPrompt`, `toolDefinitions`, and tool
|
|
37
|
+
* handlers all close over a long-lived state object created on activation.
|
|
38
|
+
*
|
|
39
|
+
* The framework wires the lifecycle: `init` on `onActivate`, `dispose` on
|
|
40
|
+
* `onDeactivate`. State is held inside the helper's closure — never exposed on
|
|
41
|
+
* the resulting `AgentConfig` — so the redux serializer doesn't see it.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const guidedBooking = defineStatefulAgent<{ machine: GuidedBookingMachine }>({
|
|
46
|
+
* name: 'Guided Booking',
|
|
47
|
+
* description: 'Books a trade via a guided wizard.',
|
|
48
|
+
* excludeFromClassifier: true,
|
|
49
|
+
* manualSelection: { enabled: true, hint: 'Step-by-step trade booking' },
|
|
50
|
+
* init: () => ({ machine: new GuidedBookingMachine() }),
|
|
51
|
+
* dispose: ({ state }) => state.machine.stop(),
|
|
52
|
+
* systemPrompt: ({ state }) => composeFromMachine(state.machine),
|
|
53
|
+
* toolDefinitions: ({ state }) => toolsForState(state.machine.state),
|
|
54
|
+
* toolHandlers: ({ machine }) => ({ ... }),
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @beta
|
|
59
|
+
*/
|
|
60
|
+
export function defineStatefulAgent(opts) {
|
|
61
|
+
let state;
|
|
62
|
+
let cachedHandlers;
|
|
63
|
+
const assertNoFolds = (handlers) => {
|
|
64
|
+
for (const [name, handler] of Object.entries(handlers)) {
|
|
65
|
+
if (handler[TOOL_FOLD_SYMBOL]) {
|
|
66
|
+
throw new Error(`Stateful agent "${opts.name}" tool "${name}" carries fold metadata. ` +
|
|
67
|
+
`Folds and state-machine-driven tool filtering both try to control the ` +
|
|
68
|
+
`LLM's tool view — pick one. Remove the fold or migrate this agent to ` +
|
|
69
|
+
`a non-stateful AgentConfig.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
// Wrap the optional dynamic-tools factory so it threads `state` in and asserts
|
|
74
|
+
// we never accidentally resolved a fold-tagged tool. Static arrays are passed
|
|
75
|
+
// through unchanged.
|
|
76
|
+
const wrappedTools = (() => {
|
|
77
|
+
const td = opts.toolDefinitions;
|
|
78
|
+
if (typeof td === 'function') {
|
|
79
|
+
return (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
if (!state) {
|
|
81
|
+
throw new Error(`Stateful agent "${opts.name}" tools called before init`);
|
|
82
|
+
}
|
|
83
|
+
return td(Object.assign(Object.assign({}, ctx), { state }));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return td;
|
|
87
|
+
})();
|
|
88
|
+
// Each proxy entry calls into `cachedHandlers`, which is populated eagerly
|
|
89
|
+
// inside `onActivate` (see below). Eager population means the no-folds
|
|
90
|
+
// assertion fires at activation time — not on first tool call — so a
|
|
91
|
+
// misconfigured agent fails loud and immediately instead of silently
|
|
92
|
+
// appearing to work until the LLM happens to invoke a tool.
|
|
93
|
+
const buildHandlerProxy = (names) => {
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
out[name] = (args, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
if (!state || !cachedHandlers) {
|
|
98
|
+
throw new Error(`Stateful agent "${opts.name}" handler called before init`);
|
|
99
|
+
}
|
|
100
|
+
const handler = cachedHandlers[name];
|
|
101
|
+
if (!handler) {
|
|
102
|
+
throw new Error(`Tool "${name}" has no handler on stateful agent "${opts.name}"`);
|
|
103
|
+
}
|
|
104
|
+
return handler(args, ctx);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
};
|
|
109
|
+
// Static-tools case: handler names are knowable from the definitions array.
|
|
110
|
+
// Dynamic-tools case: we discover names from a sample of `toolHandlers(state)`
|
|
111
|
+
// taken inside onActivate. Until then, the handler dict is empty — fine
|
|
112
|
+
// because no tool call can happen before activation completes.
|
|
113
|
+
const staticHandlerProxy = Array.isArray(opts.toolDefinitions) && opts.toolHandlers
|
|
114
|
+
? buildHandlerProxy(opts.toolDefinitions.map((d) => d.name))
|
|
115
|
+
: undefined;
|
|
116
|
+
// For the dynamic case we patch this object in place on activation. The
|
|
117
|
+
// driver reads `toolHandlers` by reference, so mutating in place is enough.
|
|
118
|
+
const resolvedHandlers = staticHandlerProxy !== null && staticHandlerProxy !== void 0 ? staticHandlerProxy : {};
|
|
119
|
+
const base = {
|
|
120
|
+
name: opts.name,
|
|
121
|
+
primerHistory: opts.primerHistory,
|
|
122
|
+
subAgents: opts.subAgents,
|
|
123
|
+
manualSelection: opts.manualSelection,
|
|
124
|
+
chatInputDuringExecution: opts.chatInputDuringExecution,
|
|
125
|
+
toolDefinitions: wrappedTools,
|
|
126
|
+
toolHandlers: opts.toolHandlers ? resolvedHandlers : undefined,
|
|
127
|
+
onActivate: (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
var _a, _b;
|
|
129
|
+
state = yield opts.init(ctx);
|
|
130
|
+
// Sample handlers eagerly and validate up-front. Throws here are visible
|
|
131
|
+
// at the agent-switch boundary instead of buried inside a future tool
|
|
132
|
+
// dispatch.
|
|
133
|
+
cachedHandlers = (_b = (_a = opts.toolHandlers) === null || _a === void 0 ? void 0 : _a.call(opts, state)) !== null && _b !== void 0 ? _b : {};
|
|
134
|
+
assertNoFolds(cachedHandlers);
|
|
135
|
+
// Dynamic-tools path: handler names are discovered from the sample.
|
|
136
|
+
if (!staticHandlerProxy && opts.toolHandlers) {
|
|
137
|
+
const dynamicProxy = buildHandlerProxy(Object.keys(cachedHandlers));
|
|
138
|
+
for (const key of Object.keys(resolvedHandlers))
|
|
139
|
+
delete resolvedHandlers[key];
|
|
140
|
+
Object.assign(resolvedHandlers, dynamicProxy);
|
|
141
|
+
}
|
|
142
|
+
}),
|
|
143
|
+
onDeactivate: (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
144
|
+
if (state !== undefined && opts.dispose) {
|
|
145
|
+
yield opts.dispose(Object.assign(Object.assign({}, ctx), { state }));
|
|
146
|
+
}
|
|
147
|
+
// Orchestrator serializes onActivate/onDeactivate; concurrent calls
|
|
148
|
+
// cannot interleave the read-then-write of `state`.
|
|
149
|
+
// eslint-disable-next-line require-atomic-updates
|
|
150
|
+
state = undefined;
|
|
151
|
+
cachedHandlers = undefined;
|
|
152
|
+
}),
|
|
153
|
+
systemPrompt: opts.systemPrompt
|
|
154
|
+
? (ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
155
|
+
if (!state) {
|
|
156
|
+
throw new Error(`Stateful agent "${opts.name}" systemPrompt called before init`);
|
|
157
|
+
}
|
|
158
|
+
return opts.systemPrompt(Object.assign(Object.assign({}, ctx), { state }));
|
|
159
|
+
})
|
|
160
|
+
: undefined,
|
|
161
|
+
// Called per LLM call by ChatDriver, and once at debug-log export time.
|
|
162
|
+
// Returns `undefined` before activation (state hasn't been built yet) and
|
|
163
|
+
// after deactivation (state was disposed) — the snapshotter is best-effort
|
|
164
|
+
// and the driver tolerates undefined.
|
|
165
|
+
getDebugSnapshot: () => {
|
|
166
|
+
if (!state)
|
|
167
|
+
return undefined;
|
|
168
|
+
return opts.getDebugSnapshot
|
|
169
|
+
? opts.getDebugSnapshot(state)
|
|
170
|
+
: defaultStatefulDebugSnapshot(state);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
return Object.assign(Object.assign({}, base), { description: opts.description, excludeFromClassifier: opts.excludeFromClassifier });
|
|
174
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './components/popout-manager';
|
|
|
9
9
|
export * from './channel/ai-activity-channel';
|
|
10
10
|
export * from './channel/ai-activity-bus';
|
|
11
11
|
export * from './config/config';
|
|
12
|
+
export * from './config/define-stateful-agent';
|
|
12
13
|
export * from './config/fallback-agents';
|
|
13
14
|
export * from './utils/tool-fold';
|
|
14
15
|
export { AiChatMarkdown } from './components/chat-markdown/chat-markdown';
|
package/dist/esm/main/main.js
CHANGED
|
@@ -69,11 +69,19 @@ const MODEL_CONTEXT_LIMITS = {
|
|
|
69
69
|
};
|
|
70
70
|
// Register supporting components when the main component module is imported.
|
|
71
71
|
avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiChatBubble, AiActivityHalo, ChatSuggestions, AgentPicker);
|
|
72
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Recursively strips non-serializable fields from an agent before storing in Redux:
|
|
74
|
+
* - `toolHandlers` (functions),
|
|
75
|
+
* - `onActivate` / `onDeactivate` (lifecycle hooks, functions),
|
|
76
|
+
* - function-form `systemPrompt` / `toolDefinitions` (downgraded to `undefined`
|
|
77
|
+
* in the snapshot — the live config on the driver is still the source of
|
|
78
|
+
* truth; the slice only stores a serializable projection).
|
|
79
|
+
*/
|
|
73
80
|
function stripHandlers(agent) {
|
|
74
|
-
const { toolHandlers:
|
|
81
|
+
const { toolHandlers: _h, onActivate: _on, onDeactivate: _off, getDebugSnapshot: _g, subAgents, systemPrompt, toolDefinitions } = agent, rest = __rest(agent, ["toolHandlers", "onActivate", "onDeactivate", "getDebugSnapshot", "subAgents", "systemPrompt", "toolDefinitions"]);
|
|
82
|
+
const stripped = Object.assign(Object.assign({}, rest), { systemPrompt: typeof systemPrompt === 'function' ? undefined : systemPrompt, toolDefinitions: typeof toolDefinitions === 'function' ? undefined : toolDefinitions });
|
|
75
83
|
return (subAgents === null || subAgents === void 0 ? void 0 : subAgents.length)
|
|
76
|
-
? Object.assign(Object.assign({},
|
|
84
|
+
? Object.assign(Object.assign({}, stripped), { subAgents: subAgents.map(stripHandlers) }) : stripped;
|
|
77
85
|
}
|
|
78
86
|
/**
|
|
79
87
|
* Foundation AI Assistant component.
|
|
@@ -443,8 +451,15 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
443
451
|
if (this.driver.isBusy())
|
|
444
452
|
return;
|
|
445
453
|
const history = (_c = (_b = (_a = this.driver).getRawHistory) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : [];
|
|
454
|
+
const oldDriver = this.driver;
|
|
446
455
|
this.unwireDriver();
|
|
447
456
|
deleteDriver(key);
|
|
457
|
+
// Fire the active agent's `onDeactivate` so machines / per-agent state get
|
|
458
|
+
// torn down before the replacement driver instantiates them again. Fire
|
|
459
|
+
// and forget — the old driver is about to be GC'd, errors are non-fatal.
|
|
460
|
+
if (oldDriver && 'dispose' in oldDriver && typeof oldDriver.dispose === 'function') {
|
|
461
|
+
void oldDriver.dispose();
|
|
462
|
+
}
|
|
448
463
|
this.driver = getOrCreateDriver(key, () => this.createDriver());
|
|
449
464
|
this.wireDriver();
|
|
450
465
|
if (history.length)
|
|
@@ -469,22 +484,64 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
469
484
|
* current agent configuration. Does not wire event listeners or register in
|
|
470
485
|
* the driver registry.
|
|
471
486
|
*/
|
|
487
|
+
/**
|
|
488
|
+
* Warn at config time if any stateful agent (one with lifecycle hooks) is
|
|
489
|
+
* configured in a way that makes it unreachable: `excludeFromClassifier`
|
|
490
|
+
* removes the classifier path, so the only entry left is manual pinning —
|
|
491
|
+
* which requires both `manualSelection.enabled` on the agent and the picker
|
|
492
|
+
* itself being enabled at the assistant level.
|
|
493
|
+
*/
|
|
494
|
+
warnUnreachableStatefulAgents() {
|
|
495
|
+
var _a, _b;
|
|
496
|
+
const pickerDisabled = this.agentPicker === 'disabled';
|
|
497
|
+
for (const a of (_a = this.agents) !== null && _a !== void 0 ? _a : []) {
|
|
498
|
+
const isStateful = !!(a.onActivate || a.onDeactivate);
|
|
499
|
+
if (!isStateful)
|
|
500
|
+
continue;
|
|
501
|
+
// Specialists only — defineStatefulAgent rejects fallback.
|
|
502
|
+
const excluded = 'excludeFromClassifier' in a && a.excludeFromClassifier === true;
|
|
503
|
+
if (!excluded)
|
|
504
|
+
continue;
|
|
505
|
+
const manual = ((_b = a.manualSelection) === null || _b === void 0 ? void 0 : _b.enabled) === true;
|
|
506
|
+
if (manual && !pickerDisabled)
|
|
507
|
+
continue;
|
|
508
|
+
const reason = pickerDisabled
|
|
509
|
+
? 'the picker is disabled at chatConfig.picker.mode'
|
|
510
|
+
: 'manualSelection.enabled is not set on the agent';
|
|
511
|
+
logger.warn(`Stateful agent "${a.name}" is unreachable: excludeFromClassifier is true and ${reason}. ` +
|
|
512
|
+
'Either remove excludeFromClassifier, enable manualSelection on the agent, or enable the picker.');
|
|
513
|
+
}
|
|
514
|
+
}
|
|
472
515
|
createDriver() {
|
|
473
|
-
var _a, _b
|
|
516
|
+
var _a, _b;
|
|
517
|
+
this.warnUnreachableStatefulAgents();
|
|
474
518
|
const agent = (_a = this.chatConfig.agent) !== null && _a !== void 0 ? _a : {};
|
|
475
519
|
const { agents } = this;
|
|
476
|
-
|
|
520
|
+
// Always route through OrchestratingDriver when agents are configured —
|
|
521
|
+
// even with a single agent — so lifecycle hooks (`onActivate`/
|
|
522
|
+
// `onDeactivate`), auto-pin, and release semantics fire uniformly. The
|
|
523
|
+
// classifier short-circuits when there's only one viable candidate, so
|
|
524
|
+
// single-agent setups pay no LLM cost for routing. With no agents
|
|
525
|
+
// configured at all, fall back to a bare ChatDriver — there's nothing to
|
|
526
|
+
// orchestrate and no hooks to fire.
|
|
527
|
+
if (agents && agents.length > 0) {
|
|
528
|
+
if (agents.length === 1 && !agents[0].fallback) {
|
|
529
|
+
// Preset for getters that read activeAgent before the first turn
|
|
530
|
+
// (e.g. chatInputDuringExecution). The orchestrator will overwrite
|
|
531
|
+
// this via `agent-changed` once applyAgent fires on first sendMessage.
|
|
532
|
+
this.activeAgent = agents[0];
|
|
533
|
+
}
|
|
477
534
|
return new OrchestratingDriver(this.aiProvider, agents, {
|
|
535
|
+
sessionKey: (_b = this.getStateKey()) !== null && _b !== void 0 ? _b : '',
|
|
478
536
|
maxHandoffs: agent.maxHandoffs,
|
|
479
537
|
classifierHistoryLength: agent.classifierHistoryLength,
|
|
480
538
|
classifierRetries: agent.classifierRetries,
|
|
481
539
|
maxToolIterations: agent.maxToolIterations,
|
|
482
540
|
maxFoldOperations: agent.maxFoldOperations,
|
|
541
|
+
maxTurnSnapshots: agent.maxTurnSnapshots,
|
|
483
542
|
});
|
|
484
543
|
}
|
|
485
|
-
|
|
486
|
-
this.activeAgent = singleAgent;
|
|
487
|
-
return new ChatDriver(this.aiProvider, (_b = singleAgent === null || singleAgent === void 0 ? void 0 : singleAgent.toolHandlers) !== null && _b !== void 0 ? _b : {}, (_c = singleAgent === null || singleAgent === void 0 ? void 0 : singleAgent.toolDefinitions) !== null && _c !== void 0 ? _c : [], singleAgent === null || singleAgent === void 0 ? void 0 : singleAgent.systemPrompt, singleAgent === null || singleAgent === void 0 ? void 0 : singleAgent.primerHistory, agent.maxToolIterations, agent.maxFoldOperations);
|
|
544
|
+
return new ChatDriver(this.aiProvider, {}, [], undefined, undefined, agent.maxToolIterations, agent.maxFoldOperations, agent.maxTurnSnapshots);
|
|
488
545
|
}
|
|
489
546
|
/**
|
|
490
547
|
* Attaches event listeners to the current driver. Stores a cleanup function
|
|
@@ -550,10 +607,22 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
550
607
|
const onAgentChanged = (e) => {
|
|
551
608
|
this.activeAgent = e.detail;
|
|
552
609
|
};
|
|
610
|
+
// Orchestrator-initiated pin changes (auto-pin on stateful activation,
|
|
611
|
+
// auto-clear on release). User-initiated pins flow the other direction
|
|
612
|
+
// via the `pinnedAgentName` setter, which already calls setPinnedAgent
|
|
613
|
+
// back into the driver — guard against the redundant round-trip.
|
|
614
|
+
const onPinnedChanged = (e) => {
|
|
615
|
+
var _a, _b;
|
|
616
|
+
const name = e.detail;
|
|
617
|
+
if (((_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.pinnedAgentName) !== name) {
|
|
618
|
+
(_b = this._sessionRef) === null || _b === void 0 ? void 0 : _b.actions.aiAssistant.setPinnedAgentName(name);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
553
621
|
driver.addEventListener('orchestrating-start', onOrchStart);
|
|
554
622
|
driver.addEventListener('orchestrating-stop', onOrchStop);
|
|
555
623
|
driver.addEventListener('agent-changed', onAgentChanged);
|
|
556
|
-
|
|
624
|
+
driver.addEventListener('pinned-changed', onPinnedChanged);
|
|
625
|
+
cleanups.push(() => driver.removeEventListener('orchestrating-start', onOrchStart), () => driver.removeEventListener('orchestrating-stop', onOrchStop), () => driver.removeEventListener('agent-changed', onAgentChanged), () => driver.removeEventListener('pinned-changed', onPinnedChanged));
|
|
557
626
|
}
|
|
558
627
|
this.driverCleanup = () => {
|
|
559
628
|
for (const fn of cleanups)
|
|
@@ -570,7 +639,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
570
639
|
this.driverCleanup = undefined;
|
|
571
640
|
}
|
|
572
641
|
connectedCallback() {
|
|
573
|
-
var _a, _b, _c, _d, _e, _f,
|
|
642
|
+
var _a, _b, _c, _d, _e, _f, _j;
|
|
574
643
|
// Initialise the store reference BEFORE super.connectedCallback() so that
|
|
575
644
|
// the first FAST render has access to the store. The store Proxy calls
|
|
576
645
|
// Observable.track(observableStore, sliceName) whenever a slice is read,
|
|
@@ -621,7 +690,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
621
690
|
// Subscribe once so that when the originating send() completes (possibly on a
|
|
622
691
|
// different element instance that has since disconnected), this element cleans up
|
|
623
692
|
// its own timer, syncs the halo, and triggers the post-response suggestion fetch.
|
|
624
|
-
this._executionCompletionUnsub = (
|
|
693
|
+
this._executionCompletionUnsub = (_j = this._sessionRef) === null || _j === void 0 ? void 0 : _j.subscribeKey((s) => s.aiAssistant.state, () => {
|
|
625
694
|
var _a, _b;
|
|
626
695
|
if (((_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.state) === 'idle') {
|
|
627
696
|
this.stopLoadingTimer();
|
|
@@ -856,6 +925,44 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
856
925
|
return undefined;
|
|
857
926
|
return (_c = (_b = (_a = this.agents) === null || _a === void 0 ? void 0 : _a.find((a) => a.name === this.pinnedAgentName)) === null || _b === void 0 ? void 0 : _b.manualSelection) === null || _c === void 0 ? void 0 : _c.hint;
|
|
858
927
|
}
|
|
928
|
+
/**
|
|
929
|
+
* The pin is locked when a stateful agent (one with lifecycle hooks) is
|
|
930
|
+
* *actively running* — i.e. its `onActivate` has fired and it owns live
|
|
931
|
+
* state. Until the user sends their first message, a freshly pinned stateful
|
|
932
|
+
* agent is not yet active and the picker should remain free; the user might
|
|
933
|
+
* change their mind and unpin without anything to clean up.
|
|
934
|
+
*
|
|
935
|
+
* We derive from `activeAgent` (set by the orchestrator after `onActivate`
|
|
936
|
+
* completes) rather than `pinnedAgentName` (set immediately on picker
|
|
937
|
+
* click). The serialized `activeAgent` strips lifecycle hooks, so we look
|
|
938
|
+
* up the live config from `this.agents` to check for them.
|
|
939
|
+
*/
|
|
940
|
+
get pinLocked() {
|
|
941
|
+
var _a;
|
|
942
|
+
if (!this.activeAgent)
|
|
943
|
+
return false;
|
|
944
|
+
const live = (_a = this.agents) === null || _a === void 0 ? void 0 : _a.find((a) => a.name === this.activeAgent.name);
|
|
945
|
+
if (!live)
|
|
946
|
+
return false;
|
|
947
|
+
return !!(live.onActivate || live.onDeactivate);
|
|
948
|
+
}
|
|
949
|
+
/** Tooltip shown on the picker toggle button. */
|
|
950
|
+
get agentToggleTitle() {
|
|
951
|
+
var _a;
|
|
952
|
+
if (this.pinLocked) {
|
|
953
|
+
const name = (_a = this.pinnedAgentName) !== null && _a !== void 0 ? _a : 'This agent';
|
|
954
|
+
return `${name} is in the middle of a guided flow. Agent switching is disabled until the flow completes.`;
|
|
955
|
+
}
|
|
956
|
+
if (this.agentPickerOpen) {
|
|
957
|
+
return 'Close agent picker';
|
|
958
|
+
}
|
|
959
|
+
if (this.pinnedAgentName !== null) {
|
|
960
|
+
return this.pinnedAgentHint
|
|
961
|
+
? `${this.pinnedAgentName} — ${this.pinnedAgentHint}`
|
|
962
|
+
: this.pinnedAgentName;
|
|
963
|
+
}
|
|
964
|
+
return 'Attempts to route messages to the correct available agent. Click to manually pin an agent.';
|
|
965
|
+
}
|
|
859
966
|
/**
|
|
860
967
|
* Tint applied to the pin icon when an agent is pinned. Picked from the
|
|
861
968
|
* brand palette by agent position (modulo the palette length), so each agent
|
|
@@ -897,21 +1004,46 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
897
1004
|
this.enabledAnimations = animations;
|
|
898
1005
|
}
|
|
899
1006
|
getDebugLog() {
|
|
900
|
-
var _a, _b, _c, _d, _e, _f,
|
|
1007
|
+
var _a, _b, _c, _d, _e, _f, _j, _k, _l, _m, _o, _p, _q;
|
|
901
1008
|
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
1009
|
+
// Live agent reference — `this.activeAgent` is the redux-stripped projection,
|
|
1010
|
+
// so getDebugSnapshot is not on it. Look up the live config from the agents
|
|
1011
|
+
// array to get the function back.
|
|
1012
|
+
const liveActiveAgent = this.activeAgent
|
|
1013
|
+
? (_a = this.agents) === null || _a === void 0 ? void 0 : _a.find((a) => a.name === this.activeAgent.name)
|
|
1014
|
+
: undefined;
|
|
1015
|
+
let activeDebugSnapshot;
|
|
1016
|
+
try {
|
|
1017
|
+
activeDebugSnapshot = (_b = liveActiveAgent === null || liveActiveAgent === void 0 ? void 0 : liveActiveAgent.getDebugSnapshot) === null || _b === void 0 ? void 0 : _b.call(liveActiveAgent);
|
|
1018
|
+
}
|
|
1019
|
+
catch (e) {
|
|
1020
|
+
activeDebugSnapshot = `<getDebugSnapshot threw: ${e instanceof Error ? e.message : String(e)}>`;
|
|
1021
|
+
}
|
|
902
1022
|
return {
|
|
903
|
-
messages: (
|
|
1023
|
+
messages: (_e = (_d = (_c = this.driver) === null || _c === void 0 ? void 0 : _c.getRawHistory) === null || _d === void 0 ? void 0 : _d.call(_c)) !== null && _e !== void 0 ? _e : this.messages,
|
|
904
1024
|
meta: {
|
|
905
1025
|
timestamp,
|
|
906
1026
|
host: window.location.host,
|
|
907
|
-
agentSummary: (
|
|
908
|
-
var _a
|
|
909
|
-
return (Object.assign(Object.assign({}, a), { toolDefinitions:
|
|
1027
|
+
agentSummary: (_f = this.agents) === null || _f === void 0 ? void 0 : _f.map((a) => {
|
|
1028
|
+
var _a;
|
|
1029
|
+
return (Object.assign(Object.assign({}, a), { toolDefinitions: Array.isArray(a.toolDefinitions)
|
|
1030
|
+
? expandToolTree(a.toolDefinitions, (_a = a.toolHandlers) !== null && _a !== void 0 ? _a : {})
|
|
1031
|
+
: typeof a.toolDefinitions === 'function'
|
|
1032
|
+
? '<dynamic — resolved per turn>'
|
|
1033
|
+
: [], toolHandlers: undefined, onActivate: undefined, onDeactivate: undefined, getDebugSnapshot: undefined }));
|
|
910
1034
|
}),
|
|
911
|
-
activeSystemPrompt: (
|
|
912
|
-
|
|
1035
|
+
activeSystemPrompt: typeof ((_j = this.activeAgent) === null || _j === void 0 ? void 0 : _j.systemPrompt) === 'function'
|
|
1036
|
+
? '<dynamic — resolved per turn>'
|
|
1037
|
+
: (_k = this.activeAgent) === null || _k === void 0 ? void 0 : _k.systemPrompt,
|
|
1038
|
+
activePrimerHistory: (_l = this.activeAgent) === null || _l === void 0 ? void 0 : _l.primerHistory,
|
|
913
1039
|
activeFoldStack: this.driver instanceof ChatDriver ? this.driver.getActiveFoldNames() : undefined,
|
|
914
|
-
|
|
1040
|
+
// Snapshot captured fresh at log-export time — reflects state NOW, which
|
|
1041
|
+
// may have transitioned since the last LLM call.
|
|
1042
|
+
activeDebugSnapshot,
|
|
1043
|
+
// Per-LLM-call timeline — pairs each turn with the prompt + tool surface
|
|
1044
|
+
// + agent state that drove it. Capped to the most recent N entries.
|
|
1045
|
+
turnSnapshots: (_p = (_o = (_m = this.driver) === null || _m === void 0 ? void 0 : _m.getTurnSnapshots) === null || _o === void 0 ? void 0 : _o.call(_m)) !== null && _p !== void 0 ? _p : [],
|
|
1046
|
+
debug: (_q = this.debugStateFactory) === null || _q === void 0 ? void 0 : _q.call(this),
|
|
915
1047
|
},
|
|
916
1048
|
};
|
|
917
1049
|
}
|
|
@@ -1011,7 +1143,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1011
1143
|
}
|
|
1012
1144
|
fetchSuggestions() {
|
|
1013
1145
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1014
|
-
var _a
|
|
1146
|
+
var _a;
|
|
1015
1147
|
const suggestionsConfig = this.chatConfig.suggestions;
|
|
1016
1148
|
if (!this.driver || !suggestionsConfig || suggestionsConfig.behavior === 'never') {
|
|
1017
1149
|
return;
|
|
@@ -1041,7 +1173,12 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1041
1173
|
{
|
|
1042
1174
|
name: this.activeAgent.name,
|
|
1043
1175
|
description: 'description' in this.activeAgent ? this.activeAgent.description : '',
|
|
1044
|
-
tools
|
|
1176
|
+
// Dynamic-tools agents have no resolvable tool list outside a
|
|
1177
|
+
// per-turn context — suggestions just get an empty hint, matching
|
|
1178
|
+
// what `OrchestratingDriver.getSuggestions` does for them.
|
|
1179
|
+
tools: Array.isArray(this.activeAgent.toolDefinitions)
|
|
1180
|
+
? this.activeAgent.toolDefinitions
|
|
1181
|
+
: [],
|
|
1045
1182
|
},
|
|
1046
1183
|
]
|
|
1047
1184
|
: undefined;
|
|
@@ -1192,6 +1329,12 @@ __decorate([
|
|
|
1192
1329
|
__decorate([
|
|
1193
1330
|
volatile
|
|
1194
1331
|
], FoundationAiAssistant.prototype, "pinnedAgentHint", null);
|
|
1332
|
+
__decorate([
|
|
1333
|
+
volatile
|
|
1334
|
+
], FoundationAiAssistant.prototype, "pinLocked", null);
|
|
1335
|
+
__decorate([
|
|
1336
|
+
volatile
|
|
1337
|
+
], FoundationAiAssistant.prototype, "agentToggleTitle", null);
|
|
1195
1338
|
__decorate([
|
|
1196
1339
|
volatile
|
|
1197
1340
|
], FoundationAiAssistant.prototype, "pinnedAgentColour", null);
|
|
@@ -196,17 +196,8 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
196
196
|
class="agent-toggle-button"
|
|
197
197
|
part="agent-toggle-button"
|
|
198
198
|
appearance="stealth"
|
|
199
|
-
title=${(x) =>
|
|
200
|
-
|
|
201
|
-
return x.agentPickerOpen
|
|
202
|
-
? 'Close agent picker'
|
|
203
|
-
: x.pinnedAgentName !== null
|
|
204
|
-
? x.pinnedAgentHint
|
|
205
|
-
? `${x.pinnedAgentName} — ${x.pinnedAgentHint}`
|
|
206
|
-
: ((_a = x.pinnedAgentName) !== null && _a !== void 0 ? _a : '')
|
|
207
|
-
: 'Attempts to route messages to the correct available agent. Click to manually pin an agent.';
|
|
208
|
-
}}
|
|
209
|
-
?disabled=${(x) => x.state === 'loading'}
|
|
199
|
+
title=${(x) => x.agentToggleTitle}
|
|
200
|
+
?disabled=${(x) => x.state === 'loading' || x.pinLocked}
|
|
210
201
|
@click=${(x) => x.toggleAgentPicker()}
|
|
211
202
|
>
|
|
212
203
|
${when((x) => x.agentPickerOpen, html `<${iconTag} name="chevron-down"></${iconTag}>`)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/halo-overlay.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/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.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.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/fallback-agents.ts","../src/config/index.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/state/ai-assistant-slice.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
|
|
1
|
+
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/halo-overlay.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/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.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.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.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/state/ai-assistant-slice.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/tool-fold.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.
|
|
4
|
+
"version": "14.436.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -64,24 +64,24 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@genesislcap/foundation-testing": "14.
|
|
68
|
-
"@genesislcap/genx": "14.
|
|
69
|
-
"@genesislcap/rollup-builder": "14.
|
|
70
|
-
"@genesislcap/ts-builder": "14.
|
|
71
|
-
"@genesislcap/uvu-playwright-builder": "14.
|
|
72
|
-
"@genesislcap/vite-builder": "14.
|
|
73
|
-
"@genesislcap/webpack-builder": "14.
|
|
67
|
+
"@genesislcap/foundation-testing": "14.436.0",
|
|
68
|
+
"@genesislcap/genx": "14.436.0",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.436.0",
|
|
70
|
+
"@genesislcap/ts-builder": "14.436.0",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.436.0",
|
|
72
|
+
"@genesislcap/vite-builder": "14.436.0",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.436.0",
|
|
74
74
|
"@types/dompurify": "^3.0.5",
|
|
75
75
|
"@types/marked": "^5.0.2"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@genesislcap/foundation-ai": "14.
|
|
79
|
-
"@genesislcap/foundation-logger": "14.
|
|
80
|
-
"@genesislcap/foundation-redux": "14.
|
|
81
|
-
"@genesislcap/foundation-ui": "14.
|
|
82
|
-
"@genesislcap/foundation-utils": "14.
|
|
83
|
-
"@genesislcap/rapid-design-system": "14.
|
|
84
|
-
"@genesislcap/web-core": "14.
|
|
78
|
+
"@genesislcap/foundation-ai": "14.436.0",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.436.0",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.436.0",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.436.0",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.436.0",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.436.0",
|
|
84
|
+
"@genesislcap/web-core": "14.436.0",
|
|
85
85
|
"dompurify": "^3.3.1",
|
|
86
86
|
"marked": "^17.0.3"
|
|
87
87
|
},
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "87ef9b53cc5f3030651cbc3126bd26f07ffd6819"
|
|
97
97
|
}
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ChatMessage,
|
|
5
5
|
ChatToolDefinition,
|
|
6
6
|
} from '@genesislcap/foundation-ai';
|
|
7
|
+
import type { TurnSnapshot } from '../chat-driver/chat-driver';
|
|
7
8
|
|
|
8
9
|
/** @internal */
|
|
9
10
|
export interface AllAgentSummary {
|
|
@@ -66,4 +67,12 @@ export interface AiDriver extends EventTarget {
|
|
|
66
67
|
count: number,
|
|
67
68
|
allAgentInfo?: AllAgentSummary[],
|
|
68
69
|
): Promise<string[]>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Per-LLM-call snapshots — what the model saw each turn (prompt, tools,
|
|
73
|
+
* agent state). Used by the host's debug-log exporter. Optional because the
|
|
74
|
+
* interface is implemented by both leaf and orchestrating drivers; the
|
|
75
|
+
* orchestrator just delegates to its inner `ChatDriver`.
|
|
76
|
+
*/
|
|
77
|
+
getTurnSnapshots?(): ReadonlyArray<TurnSnapshot>;
|
|
69
78
|
}
|