@genesislcap/ai-assistant 14.420.0 → 14.421.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-assistant.api.json +4061 -1416
- package/dist/ai-assistant.d.ts +594 -81
- package/dist/dts/channel/ai-activity-channel.d.ts +4 -22
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/ai-driver.d.ts +52 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -0
- package/dist/dts/components/ai-driver/index.d.ts +2 -0
- package/dist/dts/components/ai-driver/index.d.ts.map +1 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts +63 -8
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +3 -3
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts +1 -1
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts.map +1 -1
- package/dist/dts/components/halo-overlay.d.ts +13 -1
- package/dist/dts/components/halo-overlay.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/index.d.ts +2 -0
- package/dist/dts/components/orchestrating-driver/index.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +39 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -0
- package/dist/dts/components/popout-manager/index.d.ts +2 -0
- package/dist/dts/components/popout-manager/index.d.ts.map +1 -0
- package/dist/dts/components/popout-manager/popout-manager.d.ts +72 -0
- package/dist/dts/components/popout-manager/popout-manager.d.ts.map +1 -0
- package/dist/dts/config/config.d.ts +43 -15
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/config/fallback-agents.d.ts +20 -0
- package/dist/dts/config/fallback-agents.d.ts.map +1 -0
- package/dist/dts/config/index.d.ts +1 -0
- package/dist/dts/config/index.d.ts.map +1 -1
- package/dist/dts/index.d.ts +6 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +122 -21
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/main/main.types.d.ts +16 -0
- package/dist/dts/main/main.types.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +38 -0
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -0
- package/dist/dts/state/driver-registry.d.ts +22 -0
- package/dist/dts/state/driver-registry.d.ts.map +1 -0
- package/dist/dts/state/session-store.d.ts +37 -0
- package/dist/dts/state/session-store.d.ts.map +1 -0
- package/dist/dts/suggestions/chat-suggestions.d.ts +7 -0
- package/dist/dts/suggestions/chat-suggestions.d.ts.map +1 -0
- package/dist/dts/types/ai-chat-widget.d.ts +3 -2
- package/dist/dts/types/ai-chat-widget.d.ts.map +1 -1
- package/dist/dts/utils/index.d.ts +1 -0
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/dts/utils/tool-fold.d.ts +133 -0
- package/dist/dts/utils/tool-fold.d.ts.map +1 -0
- package/dist/esm/components/ai-driver/ai-driver.js +1 -0
- package/dist/esm/components/ai-driver/index.js +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +499 -67
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +2 -2
- package/dist/esm/components/chat-markdown/chat-markdown.js +1 -1
- package/dist/esm/components/halo-overlay.js +53 -7
- package/dist/esm/components/orchestrating-driver/index.js +1 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +247 -0
- package/dist/esm/components/popout-manager/index.js +1 -0
- package/dist/esm/components/popout-manager/popout-manager.js +126 -0
- package/dist/esm/config/fallback-agents.js +26 -0
- package/dist/esm/config/index.js +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/main/main.js +546 -112
- package/dist/esm/main/main.styles.js +200 -4
- package/dist/esm/main/main.template.js +163 -63
- package/dist/esm/state/ai-assistant-slice.js +54 -0
- package/dist/esm/state/driver-registry.js +46 -0
- package/dist/esm/state/session-store.js +39 -0
- package/dist/esm/suggestions/chat-suggestions.js +147 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/tool-fold.js +92 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/migration-FUI-2495.md +339 -0
- package/docs/sub_agent.md +310 -0
- package/package.json +16 -15
- package/src/channel/ai-activity-channel.ts +4 -20
- package/src/components/ai-driver/ai-driver.ts +69 -0
- package/src/components/ai-driver/index.ts +1 -0
- package/src/components/chat-driver/chat-driver.ts +600 -73
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +3 -3
- package/src/components/chat-markdown/chat-markdown.ts +1 -1
- package/src/components/halo-overlay.ts +45 -7
- package/src/components/orchestrating-driver/index.ts +1 -0
- package/src/components/orchestrating-driver/orchestrating-driver.ts +328 -0
- package/src/components/popout-manager/index.ts +1 -0
- package/src/components/popout-manager/popout-manager.ts +147 -0
- package/src/config/config.ts +45 -15
- package/src/config/fallback-agents.ts +29 -0
- package/src/config/index.ts +1 -0
- package/src/index.ts +6 -0
- package/src/main/main.styles.ts +200 -4
- package/src/main/main.template.ts +200 -80
- package/src/main/main.ts +567 -94
- package/src/main/main.types.ts +11 -0
- package/src/state/ai-assistant-slice.ts +80 -0
- package/src/state/driver-registry.ts +51 -0
- package/src/state/session-store.ts +56 -0
- package/src/suggestions/chat-suggestions.ts +158 -0
- package/src/types/ai-chat-widget.ts +4 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/tool-fold.ts +181 -0
- package/docs/multi-agent-architecture.md +0 -198
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Sub-Agent Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Sub-agents are specialist agents embedded inside a parent agent. Tool handlers on the parent invoke them explicitly via `requestSubAgent` — a sibling to the existing `requestInteraction` API. Each sub-agent runs its own full tool loop to completion and returns its final response to the calling handler as a resolved promise. From the LLM's perspective, it called a normal tool and got a result back — it has no visibility into the sub-agent that produced it.
|
|
6
|
+
|
|
7
|
+
The goal is to improve quality and reduce hallucinations by routing bounded, well-defined tasks (data extraction, validation, enrichment, judge passes) to purpose-built agents with focused prompts and tight tool sets, without burdening the parent's context window.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Motivating Example
|
|
12
|
+
|
|
13
|
+
**Trade File Extraction sub-agent**
|
|
14
|
+
|
|
15
|
+
A user attaches a CSV of trades they want booked. The parent (Trades Agent) is good at booking and querying trades but is not the right place to parse arbitrary file formats, normalise field names, and resolve ambiguous values. Instead, the parent's `process_trade_file` tool handler delegates to a sub-agent:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const processTradeFile: ChatToolHandler = async (args, { requestSubAgent }) => {
|
|
19
|
+
const result = await requestSubAgent('trade_file_extractor', {
|
|
20
|
+
task: `Extract and normalise all trades from this file content: ${args.file_content}`,
|
|
21
|
+
});
|
|
22
|
+
// result is the sub-agent's final assistant message — a JSON string of normalised trades
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The sub-agent runs its own tool loop — calling `parse_csv`, `resolve_instrument`, `validate_trade_fields` etc. — and when it finishes, its final message is handed back to the parent handler. The parent then proceeds to book each trade with its own tools.
|
|
28
|
+
|
|
29
|
+
Other natural candidates in the trades domain:
|
|
30
|
+
- **Risk Judge sub-agent** — called inside a `confirm_trade` handler to validate against risk limits before confirming. The same sub-agent can be reused across multiple handlers with different `task` descriptions and `context` payloads.
|
|
31
|
+
- **Counterparty Resolver sub-agent** — called inside a search handler to resolve raw counterparty names to canonical IDs. Multiple handlers can call it in parallel if they are invoked in the same LLM turn.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Config
|
|
36
|
+
|
|
37
|
+
Sub-agents are declared on the parent `AgentConfig` using a new optional `subAgents` field. They use the identical `AgentConfig` shape — no new interface is needed. The `name` field is used to look up the sub-agent at call time.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// config.ts (BaseAgentConfig addition)
|
|
41
|
+
interface BaseAgentConfig {
|
|
42
|
+
// ... existing fields ...
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sub-agents available to this agent's tool handlers via `requestSubAgent`.
|
|
46
|
+
* Sub-agents are leaf nodes — their own `subAgents` field is ignored.
|
|
47
|
+
*/
|
|
48
|
+
subAgents?: AgentConfig[];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* When this config is used as a sub-agent, caps the number of parent history
|
|
52
|
+
* messages passed as context. If unset, full history is passed. Messages within
|
|
53
|
+
* the cap are passed verbatim; messages older than the cap have tool args and
|
|
54
|
+
* results masked (same transform used by OrchestratingDriver for cross-agent history).
|
|
55
|
+
*/
|
|
56
|
+
historyCap?: number;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Example declaration in the host app:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const tradesAgent: SpecialistAgentConfig = {
|
|
64
|
+
name: 'Trades Agent',
|
|
65
|
+
description: 'Books, searches, and manages trades',
|
|
66
|
+
systemPrompt: '...',
|
|
67
|
+
toolDefinitions: [...tradeTools],
|
|
68
|
+
toolHandlers: { ...tradeHandlers },
|
|
69
|
+
subAgents: [
|
|
70
|
+
{
|
|
71
|
+
name: 'trade_file_extractor',
|
|
72
|
+
description: 'Extracts and normalises trade data from uploaded files',
|
|
73
|
+
historyCap: 4,
|
|
74
|
+
systemPrompt: `You are a data extraction specialist. Your only job is to parse the
|
|
75
|
+
provided file content and return a JSON array of normalised trade objects.
|
|
76
|
+
Each trade must have: instrument, quantity, side, price, counterparty.
|
|
77
|
+
Do not book trades. Do not ask the user questions. Just extract.`,
|
|
78
|
+
toolDefinitions: [parseCsvDefinition, resolveInstrumentDefinition],
|
|
79
|
+
toolHandlers: { parse_csv: parseCsvHandler, resolve_instrument: resolveInstrumentHandler },
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'risk_judge',
|
|
83
|
+
description: 'Validates a proposed action against risk limits',
|
|
84
|
+
systemPrompt: `You are a risk validation specialist. Assess the provided action
|
|
85
|
+
against the supplied risk limits and return a JSON object with approved (boolean)
|
|
86
|
+
and reason (string).`,
|
|
87
|
+
toolDefinitions: [getRiskLimitsDefinition],
|
|
88
|
+
toolHandlers: { get_risk_limits: getRiskLimitsHandler },
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Invocation API
|
|
97
|
+
|
|
98
|
+
Sub-agents are invoked from within tool handlers via `requestSubAgent`, available on the handler context alongside `requestInteraction`:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// ChatToolHandlers context (extended)
|
|
102
|
+
context: {
|
|
103
|
+
requestInteraction: <T>(componentName: string, data: any) => Promise<T>,
|
|
104
|
+
requestSubAgent: <T = never>(name: string, options?: SubAgentRequestOptions) => Promise<T | string>,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface SubAgentRequestOptions {
|
|
108
|
+
/** Plain-language description of what this specific invocation should do. */
|
|
109
|
+
task?: string;
|
|
110
|
+
/** Per-call override of the config-level historyCap. */
|
|
111
|
+
historyCap?: number;
|
|
112
|
+
/**
|
|
113
|
+
* Extra dynamic data for this invocation. Injected as a primer message before
|
|
114
|
+
* the task so the sub-agent sees it as part of its context.
|
|
115
|
+
*/
|
|
116
|
+
context?: Record<string, unknown>;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The return type is `T | string`:
|
|
121
|
+
- `T` — when the sub-agent explicitly calls `completeSubAgent(result)` from within one of its tool handlers. The result is the structured value passed to `completeSubAgent`, fully typed.
|
|
122
|
+
- `string` — when the sub-agent finishes its tool loop naturally with a plain text response, without calling `completeSubAgent`. This is always valid and requires no special tooling.
|
|
123
|
+
|
|
124
|
+
With `T` defaulting to `never`, `T | string` collapses to `string` when no type parameter is provided — so untyped callers get a plain string with no extra ceremony.
|
|
125
|
+
|
|
126
|
+
The `name` must match a declared sub-agent on the parent config — validated at call time with a clear error if not found.
|
|
127
|
+
|
|
128
|
+
### `completeSubAgent`
|
|
129
|
+
|
|
130
|
+
Sub-agent tool handlers receive `completeSubAgent` on their context. Calling it signals explicit completion with a structured result:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// Sub-agent tool handler context
|
|
134
|
+
context: {
|
|
135
|
+
requestInteraction: <T>(componentName: string, data: any) => Promise<T>,
|
|
136
|
+
completeSubAgent: (result: unknown) => void,
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Example — a judge sub-agent with an explicit completion tool:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
toolHandlers: {
|
|
144
|
+
submit_judgment: async (args, { completeSubAgent }) => {
|
|
145
|
+
completeSubAgent({ approved: args.approved, feedback: args.feedback });
|
|
146
|
+
return 'Judgment submitted.'; // still appended to the trace for debug visibility
|
|
147
|
+
},
|
|
148
|
+
get_risk_limits: async (args) => {
|
|
149
|
+
// normal tool — no completeSubAgent call needed
|
|
150
|
+
return fetchRiskLimits();
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
When `completeSubAgent` is called, `SubAgentRunner` stores the result, and after that tool iteration completes the loop exits and resolves `requestSubAgent` with the stored value. If no tool ever calls `completeSubAgent`, the runner falls back to the final assistant message text.
|
|
156
|
+
|
|
157
|
+
The parent handler then gets fully typed structured data with no `JSON.parse`:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const result = await requestSubAgent<{ approved: boolean; feedback: string }>('risk_judge', {
|
|
161
|
+
task: `Validate this trade: ${JSON.stringify(args.trade)}`,
|
|
162
|
+
context: { limits: getCurrentRiskLimits() },
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (typeof result === 'string') {
|
|
166
|
+
// sub-agent returned plain text — handle gracefully
|
|
167
|
+
} else {
|
|
168
|
+
const { approved, feedback } = result;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Runtime Mechanics
|
|
175
|
+
|
|
176
|
+
### SubAgentRunner
|
|
177
|
+
|
|
178
|
+
A lightweight class that owns a sub-agent's execution. Creates its own private `ChatDriver` instance, completely isolated from the parent's history.
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
SubAgentRunner
|
|
182
|
+
├── Creates its own ChatDriver instance (sub-agent config)
|
|
183
|
+
├── Builds the history snapshot from the parent (applying historyCap + masking if set)
|
|
184
|
+
├── Prepends any call-time context as a primer message
|
|
185
|
+
├── Sends the task string as the initial user message
|
|
186
|
+
└── Runs the tool loop to completion → returns the final assistant message text + trace
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The parent's `ChatDriver` never sees the sub-agent's intermediate messages. It only receives the final string result when the handler's promise resolves — exactly as for any other tool.
|
|
190
|
+
|
|
191
|
+
### Parallel execution
|
|
192
|
+
|
|
193
|
+
No special work needed. The parent's tool loop already runs all tool calls from a single LLM turn in parallel via `Promise.all`. If two tool handlers both call `requestSubAgent`, their `SubAgentRunner` instances start concurrently and the parent resumes after the last one resolves.
|
|
194
|
+
|
|
195
|
+
### History passed to the sub-agent
|
|
196
|
+
|
|
197
|
+
By default the sub-agent receives a read-only snapshot of the full parent history as primer context. With `historyCap: N`:
|
|
198
|
+
- The last N messages are passed verbatim with full fidelity.
|
|
199
|
+
- Messages older than N have tool call args and results masked — using the same `transformHistoryForAgent` function already in `OrchestratingDriver`. No new logic needed.
|
|
200
|
+
|
|
201
|
+
Any `context` object passed at call time is injected as an additional primer message immediately before the task, so the sub-agent sees it naturally as part of the conversation.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## History & Debug
|
|
206
|
+
|
|
207
|
+
All sub-agent activity is hidden behind the existing `showToolCalls` toggle — no new UI settings needed. Sub-agents are an implementation detail of a tool call and are treated as such.
|
|
208
|
+
|
|
209
|
+
### What the parent sees (always)
|
|
210
|
+
|
|
211
|
+
A single `tool` role message containing the sub-agent's result. Compact, no noise.
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
[tool] process_trade_file → '[{"instrument":"AAPL","qty":100,...}, ...]'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### What the debug view sees (`showToolCalls: true`)
|
|
218
|
+
|
|
219
|
+
The `tool` message carries an additional `subAgentTrace` field (UI-only, stripped from provider calls — same pattern as `foldPath`/`foldEvent`):
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
// Extension to ChatToolResult
|
|
223
|
+
interface ChatToolResult {
|
|
224
|
+
toolCallId: string;
|
|
225
|
+
content: string;
|
|
226
|
+
/** Full message history from the sub-agent run, for debug display only. */
|
|
227
|
+
subAgentTrace?: ChatMessage[];
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The trace is rendered as a nested, collapsible conversation inside the parent tool call. It is breadcrumbed with the sub-agent name — consistent with how `foldPath` labels tool calls inside folds:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
▼ process_trade_file
|
|
235
|
+
Trades Agent › trade_file_extractor
|
|
236
|
+
├── [assistant] I'll parse the file and extract the trade data.
|
|
237
|
+
├── [tool call] parse_csv(...)
|
|
238
|
+
├── [tool result] ...
|
|
239
|
+
└── [complete] { trades: [...], warnings: [...] }
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The `agentName` field is already set on every message by `ChatDriver.appendToHistory` — the UI uses it as the breadcrumb label without any additional tagging.
|
|
243
|
+
|
|
244
|
+
### Live activity indicator
|
|
245
|
+
|
|
246
|
+
While a sub-agent is running, `SubAgentRunner` fires `sub-agent-start` / `sub-agent-stop` events (with `detail: { name: string }`) via an `onProgress` callback provided by the parent `ChatDriver`. The parent dispatches these on itself so `FoundationAiAssistant` can show a contextual activity label (e.g. "Consulting risk judge...") in the same overlay used for the orchestrating classifier. Hidden behind `showToolCalls: false` by default — visible only when tool calls are shown.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## State Sharing
|
|
251
|
+
|
|
252
|
+
**Structured result via `completeSubAgent` (preferred)**
|
|
253
|
+
|
|
254
|
+
Define an explicit completion tool on the sub-agent. When called, `completeSubAgent` resolves the `requestSubAgent` promise with a typed object — no `JSON.parse`, no brittle string parsing.
|
|
255
|
+
|
|
256
|
+
**Plain text fallback**
|
|
257
|
+
|
|
258
|
+
If no tool calls `completeSubAgent`, the sub-agent's final assistant message text is returned as a `string`. Sufficient for simple cases where the sub-agent's response is human-readable prose the parent just forwards on.
|
|
259
|
+
|
|
260
|
+
**Shared store via tools (for side-effects)**
|
|
261
|
+
|
|
262
|
+
A sub-agent that needs to write to shared application state can be given tools that do so directly. The parent reads that state via its own tools. No built-in bridge needed — this is normal tool use, wired up by the host app.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Implementation Plan
|
|
267
|
+
|
|
268
|
+
### 1. Config (`config.ts`)
|
|
269
|
+
- Add `subAgents?: AgentConfig[]` and `historyCap?: number` to `BaseAgentConfig`.
|
|
270
|
+
|
|
271
|
+
### 2. Types (`chat.types.ts`)
|
|
272
|
+
- Add `SubAgentRequestOptions` interface.
|
|
273
|
+
- Extend `ChatToolHandlers` handler context with `requestSubAgent` and `completeSubAgent`.
|
|
274
|
+
|
|
275
|
+
### 3. SubAgentRunner (`sub-agent-runner.ts`)
|
|
276
|
+
New class, lives alongside `ChatDriver`:
|
|
277
|
+
- Constructor: `(aiProvider, subAgentConfig, parentHistory: readonly ChatMessage[], onProgress: (event: 'start' | 'stop', name: string) => void)`
|
|
278
|
+
- Method: `run(task: string, options?: SubAgentRequestOptions): Promise<{ result: unknown | string; trace: ChatMessage[] }>`
|
|
279
|
+
- Applies `historyCap` masking, prepends `context` as a primer message, creates a `ChatDriver`.
|
|
280
|
+
- Fires `onProgress('start', name)` before `sendMessage` and `onProgress('stop', name)` when the loop completes.
|
|
281
|
+
- Provides `completeSubAgent` in the handler context. When called, stores the result and sets a completion flag.
|
|
282
|
+
- After each tool iteration, checks the flag — if set, exits the loop and resolves with the stored value.
|
|
283
|
+
- If the loop ends without `completeSubAgent` being called, resolves with the final assistant message text.
|
|
284
|
+
- Ignores `subAgents` on the sub-agent config and logs a warning if present (leaf-only, v1).
|
|
285
|
+
|
|
286
|
+
### 4. ChatDriver — `requestSubAgent` in handler context
|
|
287
|
+
- In `applyAgent`, store `config.subAgents` as a map keyed by name.
|
|
288
|
+
- When building the handler context object (currently `{ requestInteraction }`), add `requestSubAgent`: looks up the sub-agent config by name, constructs a `SubAgentRunner` with an `onProgress` callback that dispatches `sub-agent-start` / `sub-agent-stop` `CustomEvent`s on the `ChatDriver` itself, calls `runner.run(task, options)`, and returns the result.
|
|
289
|
+
- If the sub-agent name is not found in the declared sub-agents, throws a descriptive error listing available names. This is caught by the existing tool error handler in `runToolLoop` and returned to the LLM as a tool result string — no special error path needed.
|
|
290
|
+
|
|
291
|
+
### 5. `ChatToolResult` — trace field (`chat.types.ts`)
|
|
292
|
+
- Add `subAgentTrace?: ChatMessage[]` to `ChatToolResult`.
|
|
293
|
+
- In `ChatDriver.runToolLoop`, after a handler resolves, attach the trace if present.
|
|
294
|
+
- Strip `subAgentTrace` in `providerHistoryTransform` so it is never sent to the AI provider.
|
|
295
|
+
|
|
296
|
+
### 6. Debug UI
|
|
297
|
+
- In the tool-calls panel, detect `toolResult.subAgentTrace` and render a collapsible nested conversation.
|
|
298
|
+
- Style it distinctly (indented, labelled with the sub-agent name).
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Resolved Questions
|
|
303
|
+
|
|
304
|
+
1. **Sub-agent of sub-agent?** Not supported in v1. Sub-agents are leaf nodes — `SubAgentRunner` ignores `subAgents` on the sub-agent config and logs a warning. Revisit if a concrete use case emerges.
|
|
305
|
+
|
|
306
|
+
2. **Abort / cancellation**: Not needed. The driver registry PR (FUI-2495-ai-redux-store) decouples driver lifetime from DOM lifetime — a sub-agent that outlives a component disconnect simply completes against an unobserved driver. No abort mechanism required for v1.
|
|
307
|
+
|
|
308
|
+
3. **Token budget / concurrency cap**: Deferred. A concurrency cap can be added later as a semaphore in `SubAgentRunner` without any interface changes.
|
|
309
|
+
|
|
310
|
+
4. **History truncation**: Handled via `historyCap` on the sub-agent config (or overridden per-call). Messages within the cap are passed verbatim; older messages are masked using the existing `transformHistoryForAgent` function from `OrchestratingDriver`.
|
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.421.1",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -64,23 +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.421.1",
|
|
68
|
+
"@genesislcap/genx": "14.421.1",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.421.1",
|
|
70
|
+
"@genesislcap/ts-builder": "14.421.1",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.421.1",
|
|
72
|
+
"@genesislcap/vite-builder": "14.421.1",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.421.1",
|
|
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-
|
|
81
|
-
"@genesislcap/foundation-
|
|
82
|
-
"@genesislcap/
|
|
83
|
-
"@genesislcap/
|
|
78
|
+
"@genesislcap/foundation-ai": "14.421.1",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.421.1",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.421.1",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.421.1",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.421.1",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.421.1",
|
|
84
|
+
"@genesislcap/web-core": "14.421.1",
|
|
84
85
|
"dompurify": "^3.3.1",
|
|
85
86
|
"marked": "^17.0.3"
|
|
86
87
|
},
|
|
@@ -95,5 +96,5 @@
|
|
|
95
96
|
"peerDependencies": {
|
|
96
97
|
"@microsoft/fast-react-wrapper": ">=0.3.0"
|
|
97
98
|
},
|
|
98
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "ef3efc54221d1c0af5c842f0b98225ca813b4042"
|
|
99
100
|
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
2
|
-
import type { AiAssistantAnimation } from '../main/main.types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Serialized state of a `FoundationAiAssistant` instance.
|
|
6
|
-
* All fields are JSON-serializable.
|
|
7
|
-
*
|
|
8
|
-
* @beta
|
|
9
|
-
*/
|
|
10
|
-
export interface AiAssistantSerializedState {
|
|
11
|
-
messages: ChatMessage[];
|
|
12
|
-
showToolCalls: boolean;
|
|
13
|
-
showThinkingSteps: boolean;
|
|
14
|
-
enabledAnimations: AiAssistantAnimation[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
1
|
/**
|
|
18
2
|
* Event map for the AI activity bus.
|
|
19
3
|
*
|
|
@@ -33,8 +17,8 @@ export interface AgenticActivityEvents {
|
|
|
33
17
|
'halo-start': { toolNames: string[] };
|
|
34
18
|
/** Fired when the assistant finishes all tool processing and returns to idle. */
|
|
35
19
|
'halo-stop': undefined;
|
|
36
|
-
/** Fired when the user clicks the expand button — moves assistant from bubble into layout. */
|
|
37
|
-
'chat-popout':
|
|
38
|
-
/** Fired when the user clicks the collapse button — moves assistant from layout back to bubble. */
|
|
39
|
-
'chat-popin':
|
|
20
|
+
/** Fired when the user clicks the expand button — moves assistant from bubble into layout. State lives in the session store, not in this event. */
|
|
21
|
+
'chat-popout': undefined;
|
|
22
|
+
/** Fired when the user clicks the collapse button — moves assistant from layout back to bubble. State lives in the session store, not in this event. */
|
|
23
|
+
'chat-popin': undefined;
|
|
40
24
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChatAttachment,
|
|
3
|
+
ChatDriverResult,
|
|
4
|
+
ChatMessage,
|
|
5
|
+
ChatToolDefinition,
|
|
6
|
+
} from '@genesislcap/foundation-ai';
|
|
7
|
+
|
|
8
|
+
/** @internal */
|
|
9
|
+
export interface AllAgentSummary {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
tools: ChatToolDefinition[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Common interface implemented by both `ChatDriver` (single-agent) and
|
|
17
|
+
* `OrchestratingDriver` (multi-agent). `FoundationAiAssistant` depends only
|
|
18
|
+
* on this interface — the concrete class is chosen at startup based on how
|
|
19
|
+
* many agents are configured.
|
|
20
|
+
*
|
|
21
|
+
* Extends `EventTarget` so consumers can subscribe to `history-updated` events
|
|
22
|
+
* without knowing the concrete implementation.
|
|
23
|
+
*
|
|
24
|
+
* @beta
|
|
25
|
+
*/
|
|
26
|
+
export interface AiDriver extends EventTarget {
|
|
27
|
+
/**
|
|
28
|
+
* Send a user message and run the tool loop to completion.
|
|
29
|
+
*/
|
|
30
|
+
sendMessage(input: string, attachments?: ChatAttachment[]): Promise<ChatDriverResult>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Continue the tool loop from the current history without appending a new
|
|
34
|
+
* user message. Used by `OrchestratingDriver` on agent handoffs — the
|
|
35
|
+
* handoff context is already in history; `transientPrimer` is injected as
|
|
36
|
+
* an invisible one-shot message for this call only.
|
|
37
|
+
*/
|
|
38
|
+
continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a pending blocking interaction with the given result.
|
|
42
|
+
*/
|
|
43
|
+
resolveInteraction(interactionId: string, result: unknown): void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Seed the driver's message history (called on state restore / pop-in).
|
|
47
|
+
*/
|
|
48
|
+
loadHistory(messages: ChatMessage[]): void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return the full, unredacted conversation history.
|
|
52
|
+
*/
|
|
53
|
+
getRawHistory?(): readonly ChatMessage[];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns true if the driver is currently processing a request.
|
|
57
|
+
*/
|
|
58
|
+
isBusy(): boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get query suggestions from the AI.
|
|
62
|
+
*/
|
|
63
|
+
getSuggestions(
|
|
64
|
+
history: ChatMessage[],
|
|
65
|
+
prompt: string,
|
|
66
|
+
count: number,
|
|
67
|
+
allAgentInfo?: AllAgentSummary[],
|
|
68
|
+
): Promise<string[]>;
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { AiDriver } from './ai-driver';
|