@genesislcap/ai-assistant 14.421.1 → 14.422.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 +191 -1
- package/dist/ai-assistant.d.ts +60 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts +33 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +20 -0
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +6 -0
- 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 +16 -0
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +6 -0
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +2 -0
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/dts/utils/history-transform.d.ts +13 -0
- package/dist/dts/utils/history-transform.d.ts.map +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +127 -16
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +8 -20
- package/dist/esm/config/config.js +18 -1
- package/dist/esm/main/main.js +43 -11
- package/dist/esm/main/main.styles.js +62 -0
- package/dist/esm/main/main.template.js +122 -71
- package/dist/esm/state/ai-assistant-slice.js +8 -0
- package/dist/esm/utils/history-transform.js +35 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/sub_agent.md +149 -211
- package/package.json +16 -16
- package/src/components/chat-driver/chat-driver.ts +169 -15
- package/src/components/orchestrating-driver/orchestrating-driver.ts +10 -22
- package/src/config/config.ts +24 -0
- package/src/main/main.styles.ts +62 -0
- package/src/main/main.template.ts +189 -117
- package/src/main/main.ts +43 -9
- package/src/state/ai-assistant-slice.ts +12 -0
- package/src/utils/history-transform.ts +40 -0
package/docs/sub_agent.md
CHANGED
|
@@ -1,310 +1,248 @@
|
|
|
1
|
-
# Sub-
|
|
1
|
+
# Sub-Agents
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
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
|
|
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 result to the calling handler as a resolved promise.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
From the LLM's perspective, it called a normal tool and received a result — it has no visibility into the sub-agent that produced it. Sub-agents are an implementation detail of a tool call, not a visible actor in the conversation.
|
|
8
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.
|
|
9
|
+
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 agent's context window.
|
|
32
10
|
|
|
33
11
|
---
|
|
34
12
|
|
|
35
|
-
##
|
|
13
|
+
## Declaring sub-agents
|
|
36
14
|
|
|
37
|
-
Sub-agents are declared on the parent `AgentConfig` using
|
|
15
|
+
Sub-agents are declared on the parent `AgentConfig` using the `subAgents` field, which takes an array of `AgentConfig` objects. The `name` field is used to look up the sub-agent at call time.
|
|
38
16
|
|
|
39
17
|
```ts
|
|
40
|
-
|
|
41
|
-
interface BaseAgentConfig {
|
|
42
|
-
// ... existing fields ...
|
|
18
|
+
import { defineAgent } from '@genesislcap/ai-assistant';
|
|
43
19
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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',
|
|
20
|
+
const tradesAgent = defineAgent({
|
|
21
|
+
name: 'Trades Assistant',
|
|
22
|
+
description: 'Books, searches, and manages trades.',
|
|
66
23
|
systemPrompt: '...',
|
|
67
24
|
toolDefinitions: [...tradeTools],
|
|
68
25
|
toolHandlers: { ...tradeHandlers },
|
|
69
26
|
subAgents: [
|
|
70
27
|
{
|
|
71
28
|
name: 'trade_file_extractor',
|
|
72
|
-
description: 'Extracts and normalises trade data from uploaded files',
|
|
73
|
-
historyCap: 4,
|
|
29
|
+
description: 'Extracts and normalises trade data from uploaded files.',
|
|
74
30
|
systemPrompt: `You are a data extraction specialist. Your only job is to parse the
|
|
75
|
-
provided file
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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 },
|
|
31
|
+
provided file and return structured trade rows.
|
|
32
|
+
Do not book trades. Do not ask questions. Just extract.`,
|
|
33
|
+
toolDefinitions: [returnExtractedTradesDefinition],
|
|
34
|
+
toolHandlers: { return_extracted_trades: returnExtractedTradesHandler },
|
|
89
35
|
},
|
|
90
36
|
],
|
|
91
|
-
};
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `defineAgent` helper
|
|
41
|
+
|
|
42
|
+
Use `defineAgent` when you need the config's `name` type preserved as a string literal — required for typed `requestSubAgent` calls and `ChatToolHandlers` generic wiring (see [TypeScript types](#typescript-types)):
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { defineAgent } from '@genesislcap/ai-assistant';
|
|
46
|
+
|
|
47
|
+
export const tradeFileExtractorAgent = defineAgent({
|
|
48
|
+
name: 'trade_file_extractor',
|
|
49
|
+
// ...
|
|
50
|
+
});
|
|
92
51
|
```
|
|
93
52
|
|
|
53
|
+
Without `defineAgent`, TypeScript widens `name` to `string`, which loses the type safety on `requestSubAgent`'s name parameter.
|
|
54
|
+
|
|
94
55
|
---
|
|
95
56
|
|
|
96
|
-
##
|
|
57
|
+
## Invoking a sub-agent
|
|
97
58
|
|
|
98
|
-
|
|
59
|
+
Tool handlers receive `requestSubAgent` on their context object alongside `requestInteraction`:
|
|
99
60
|
|
|
100
61
|
```ts
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
62
|
+
const processTradeFile = async (args, context) => {
|
|
63
|
+
const { file_name } = args as { file_name: string };
|
|
64
|
+
|
|
65
|
+
const result = await context.requestSubAgent('trade_file_extractor', {
|
|
66
|
+
task: `Extract all trade rows from the attached file named "${file_name}".`,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// result is either the structured value from completeSubAgent, or the
|
|
70
|
+
// sub-agent's final assistant message text as a string fallback.
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`requestSubAgent` is only present on the context when `subAgents` is declared on the parent agent config. Guard for its existence in reusable handlers:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
if (!context.requestSubAgent) {
|
|
79
|
+
return { error: 'Sub-agent support is not available in this context.' };
|
|
105
80
|
}
|
|
81
|
+
```
|
|
106
82
|
|
|
83
|
+
### `SubAgentRequestOptions`
|
|
84
|
+
|
|
85
|
+
```ts
|
|
107
86
|
interface SubAgentRequestOptions {
|
|
108
|
-
/** Plain-language description
|
|
87
|
+
/** Plain-language task description for this specific invocation. */
|
|
109
88
|
task?: string;
|
|
110
|
-
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Caps how many parent history messages the sub-agent receives.
|
|
92
|
+
* Messages within the cap are passed verbatim; older messages have
|
|
93
|
+
* their tool call args and results masked. Overrides any config-level
|
|
94
|
+
* historyCap for this call.
|
|
95
|
+
*/
|
|
111
96
|
historyCap?: number;
|
|
97
|
+
|
|
112
98
|
/**
|
|
113
|
-
* Extra dynamic data for this invocation. Injected as a primer message
|
|
114
|
-
* the task so the sub-agent sees it as
|
|
99
|
+
* Extra dynamic data for this invocation. Injected as a primer message
|
|
100
|
+
* immediately before the task, so the sub-agent sees it naturally as
|
|
101
|
+
* part of its context.
|
|
115
102
|
*/
|
|
116
103
|
context?: Record<string, unknown>;
|
|
117
104
|
}
|
|
118
105
|
```
|
|
119
106
|
|
|
120
|
-
|
|
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:
|
|
107
|
+
---
|
|
131
108
|
|
|
132
|
-
|
|
133
|
-
// Sub-agent tool handler context
|
|
134
|
-
context: {
|
|
135
|
-
requestInteraction: <T>(componentName: string, data: any) => Promise<T>,
|
|
136
|
-
completeSubAgent: (result: unknown) => void,
|
|
137
|
-
}
|
|
138
|
-
```
|
|
109
|
+
## Returning structured results: `completeSubAgent`
|
|
139
110
|
|
|
140
|
-
|
|
111
|
+
By default, `requestSubAgent` returns the sub-agent's final assistant message text as a `string`. For structured data, define a completion tool on the sub-agent and call `completeSubAgent` from its handler:
|
|
141
112
|
|
|
142
113
|
```ts
|
|
143
|
-
toolHandlers: {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
114
|
+
const toolHandlers: ChatToolHandlers = {
|
|
115
|
+
return_extracted_trades: async (args, context) => {
|
|
116
|
+
const { rows } = args as ExtractedTrades;
|
|
117
|
+
context.completeSubAgent?.({ rows });
|
|
118
|
+
return `Extracted ${rows.length} trade row(s).`; // still appended to trace
|
|
147
119
|
},
|
|
148
|
-
|
|
149
|
-
// normal tool — no completeSubAgent call needed
|
|
150
|
-
return fetchRiskLimits();
|
|
151
|
-
},
|
|
152
|
-
}
|
|
120
|
+
};
|
|
153
121
|
```
|
|
154
122
|
|
|
155
|
-
When `completeSubAgent` is called,
|
|
123
|
+
When `completeSubAgent` is called, the sub-agent's tool loop exits and `requestSubAgent` resolves with the value passed to `completeSubAgent`. If `completeSubAgent` is never called, the sub-agent runs to natural completion and returns its final text.
|
|
156
124
|
|
|
157
|
-
The
|
|
125
|
+
The return type of `requestSubAgent<T>` is `T | string`:
|
|
126
|
+
- `T` — when the sub-agent called `completeSubAgent(result)`. Fully typed, no `JSON.parse`.
|
|
127
|
+
- `string` — when the sub-agent finished naturally without calling `completeSubAgent`.
|
|
128
|
+
|
|
129
|
+
With `T` defaulting to `never`, `T | string` collapses to `string` when no type parameter is provided — untyped callers get a plain string with no extra ceremony.
|
|
158
130
|
|
|
159
131
|
```ts
|
|
160
|
-
const result = await requestSubAgent<
|
|
161
|
-
task: `
|
|
162
|
-
context: { limits: getCurrentRiskLimits() },
|
|
132
|
+
const result = await context.requestSubAgent<ExtractedTrades>('trade_file_extractor', {
|
|
133
|
+
task: `Extract all trades from "${file_name}".`,
|
|
163
134
|
});
|
|
164
135
|
|
|
165
136
|
if (typeof result === 'string') {
|
|
166
|
-
// sub-agent returned plain text
|
|
137
|
+
// handle gracefully — sub-agent returned plain text
|
|
167
138
|
} else {
|
|
168
|
-
const {
|
|
139
|
+
const { rows } = result; // fully typed
|
|
169
140
|
}
|
|
170
141
|
```
|
|
171
142
|
|
|
172
143
|
---
|
|
173
144
|
|
|
174
|
-
##
|
|
175
|
-
|
|
176
|
-
### SubAgentRunner
|
|
145
|
+
## TypeScript types
|
|
177
146
|
|
|
178
|
-
|
|
147
|
+
`ChatToolHandlers` accepts an optional type parameter that narrows the `name` argument of `requestSubAgent` to the declared sub-agent names. Pass the agent config type via `typeof`:
|
|
179
148
|
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
|
|
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.
|
|
149
|
+
```ts
|
|
150
|
+
import { defineAgent } from '@genesislcap/ai-assistant';
|
|
151
|
+
import type { ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
190
152
|
|
|
191
|
-
|
|
153
|
+
const tradeFileExtractorAgent = defineAgent({
|
|
154
|
+
name: 'trade_file_extractor',
|
|
155
|
+
// ...
|
|
156
|
+
});
|
|
192
157
|
|
|
193
|
-
|
|
158
|
+
// requestSubAgent name param is now typed as 'trade_file_extractor'
|
|
159
|
+
const handlers: ChatToolHandlers<typeof tradeFileExtractorAgent> = {
|
|
160
|
+
process_trade_file: async (args, context) => {
|
|
161
|
+
// context.requestSubAgent('trade_file_extractor', ...) — type-checked
|
|
162
|
+
// context.requestSubAgent('typo', ...) — TypeScript error
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
```
|
|
194
166
|
|
|
195
|
-
|
|
167
|
+
For multiple sub-agents use a union:
|
|
196
168
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
169
|
+
```ts
|
|
170
|
+
type Handlers = ChatToolHandlers<typeof agentA | typeof agentB>;
|
|
171
|
+
```
|
|
200
172
|
|
|
201
|
-
|
|
173
|
+
The default (`{ name: string }`) leaves the name as `string` — no breaking change for existing handlers.
|
|
202
174
|
|
|
203
175
|
---
|
|
204
176
|
|
|
205
|
-
## History
|
|
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)
|
|
177
|
+
## History passed to the sub-agent
|
|
210
178
|
|
|
211
|
-
|
|
179
|
+
By default the sub-agent receives a snapshot of the full parent conversation history as context. The in-flight assistant message (the one containing the tool call that triggered the sub-agent) is excluded — it has unresolved tool calls that would confuse the sub-agent.
|
|
212
180
|
|
|
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`):
|
|
181
|
+
Use `historyCap` to limit how much history the sub-agent sees:
|
|
220
182
|
|
|
221
183
|
```ts
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
184
|
+
subAgents: [
|
|
185
|
+
{
|
|
186
|
+
name: 'trade_file_extractor',
|
|
187
|
+
historyCap: 4, // pass only the last 4 messages verbatim; mask older ones
|
|
188
|
+
// ...
|
|
189
|
+
},
|
|
190
|
+
],
|
|
229
191
|
```
|
|
230
192
|
|
|
231
|
-
|
|
193
|
+
Or override per call:
|
|
232
194
|
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
├── [tool result] ...
|
|
239
|
-
└── [complete] { trades: [...], warnings: [...] }
|
|
195
|
+
```ts
|
|
196
|
+
await context.requestSubAgent('trade_file_extractor', {
|
|
197
|
+
historyCap: 2,
|
|
198
|
+
task: '...',
|
|
199
|
+
});
|
|
240
200
|
```
|
|
241
201
|
|
|
242
|
-
|
|
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.
|
|
202
|
+
Messages older than the cap have their tool call args and results replaced with a placeholder — the same masking used by the orchestrating driver for cross-agent history.
|
|
247
203
|
|
|
248
204
|
---
|
|
249
205
|
|
|
250
|
-
##
|
|
206
|
+
## Parallel execution
|
|
251
207
|
|
|
252
|
-
|
|
208
|
+
No special work is required. The parent's tool loop runs all tool calls from a single LLM turn in parallel. If two tool handlers both call `requestSubAgent`, their executions start concurrently and the parent resumes after the last one resolves.
|
|
253
209
|
|
|
254
|
-
|
|
210
|
+
---
|
|
255
211
|
|
|
256
|
-
|
|
212
|
+
## Live activity indicator
|
|
257
213
|
|
|
258
|
-
|
|
214
|
+
While a sub-agent is running, `FoundationAiAssistant` exposes two reactive properties you can bind in the host application:
|
|
259
215
|
|
|
260
|
-
|
|
216
|
+
| Property | Type | Description |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `liveSubAgentName` | `string \| null` | Name of the currently-running sub-agent, or `null` when idle. |
|
|
219
|
+
| `liveSubAgentTrace` | `ChatMessage[]` | Growing history from the sub-agent's active tool loop. Cleared on stop. |
|
|
261
220
|
|
|
262
|
-
|
|
221
|
+
These update in real time as the sub-agent produces messages, letting the host app show a live indicator (e.g. "Consulting trade extractor...") or stream the sub-agent's intermediate steps. The built-in template renders a `live-sub-agent-trace` panel while a sub-agent is active and hides it when it stops.
|
|
263
222
|
|
|
264
223
|
---
|
|
265
224
|
|
|
266
|
-
##
|
|
225
|
+
## Debug trace
|
|
267
226
|
|
|
268
|
-
|
|
269
|
-
- Add `subAgents?: AgentConfig[]` and `historyCap?: number` to `BaseAgentConfig`.
|
|
227
|
+
When tool calls are visible (`showToolCalls: true`), each tool call that involved a sub-agent displays a collapsed trace panel. The trace shows the sub-agent's full message history — assistant turns, tool calls, and tool results — nested inside the parent tool call card, labelled with the sub-agent name.
|
|
270
228
|
|
|
271
|
-
|
|
272
|
-
- Add `SubAgentRequestOptions` interface.
|
|
273
|
-
- Extend `ChatToolHandlers` handler context with `requestSubAgent` and `completeSubAgent`.
|
|
229
|
+
The trace is stored on `ChatToolCall.subAgentTrace` (UI-only — never sent to the AI provider, same as `foldPath`).
|
|
274
230
|
|
|
275
|
-
|
|
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.
|
|
231
|
+
---
|
|
290
232
|
|
|
291
|
-
|
|
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.
|
|
233
|
+
## Complete example
|
|
295
234
|
|
|
296
|
-
|
|
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).
|
|
235
|
+
The showcase app contains a working end-to-end example:
|
|
299
236
|
|
|
300
|
-
|
|
237
|
+
- **Sub-agent config:** `packages/showcase/client-app/client/src/routes/ai/trades-assistant/sub-agents/trade-file-extractor.ts`
|
|
238
|
+
- **Parent tool handler:** `process_trade_file` in `packages/showcase/client-app/client/src/routes/ai/trades-assistant/tools/trades-tools.ts`
|
|
239
|
+
- **Parent agent wiring:** `packages/showcase/client-app/client/src/routes/ai/genesis-assistant/genesis-assistant.ts`
|
|
301
240
|
|
|
302
|
-
|
|
241
|
+
The sub-agent (`trade_file_extractor`) defines a single `return_extracted_trades` tool that calls `completeSubAgent` with the parsed rows. The parent's `process_trade_file` handler calls `requestSubAgent<ExtractedTrades>('trade_file_extractor', { task: ... })` and receives the typed `ExtractedTrades` result.
|
|
303
242
|
|
|
304
|
-
|
|
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.
|
|
243
|
+
---
|
|
307
244
|
|
|
308
|
-
|
|
245
|
+
## Constraints (v1)
|
|
309
246
|
|
|
310
|
-
|
|
247
|
+
- Sub-agents are leaf nodes — their own `subAgents` field is ignored. Nesting is not supported.
|
|
248
|
+
- Sub-agents share the same `AIProvider` instance as the parent. They each make independent LLM calls.
|
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.422.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.422.0",
|
|
68
|
+
"@genesislcap/genx": "14.422.0",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.422.0",
|
|
70
|
+
"@genesislcap/ts-builder": "14.422.0",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.422.0",
|
|
72
|
+
"@genesislcap/vite-builder": "14.422.0",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.422.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.422.0",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.422.0",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.422.0",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.422.0",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.422.0",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.422.0",
|
|
84
|
+
"@genesislcap/web-core": "14.422.0",
|
|
85
85
|
"dompurify": "^3.3.1",
|
|
86
86
|
"marked": "^17.0.3"
|
|
87
87
|
},
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
"peerDependencies": {
|
|
97
97
|
"@microsoft/fast-react-wrapper": ">=0.3.0"
|
|
98
98
|
},
|
|
99
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "d596005408a80737f514e2d49b50c9dcc34b6913"
|
|
100
100
|
}
|