@genesislcap/ai-assistant 14.421.0 → 14.421.1-FUI-2511.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/ai-assistant.api.json +191 -1
  2. package/dist/ai-assistant.d.ts +60 -0
  3. package/dist/dts/components/chat-driver/chat-driver.d.ts +33 -0
  4. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  5. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  6. package/dist/dts/config/config.d.ts +20 -0
  7. package/dist/dts/config/config.d.ts.map +1 -1
  8. package/dist/dts/main/main.d.ts +6 -0
  9. package/dist/dts/main/main.d.ts.map +1 -1
  10. package/dist/dts/main/main.styles.d.ts.map +1 -1
  11. package/dist/dts/main/main.template.d.ts +16 -0
  12. package/dist/dts/main/main.template.d.ts.map +1 -1
  13. package/dist/dts/state/ai-assistant-slice.d.ts +6 -0
  14. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  15. package/dist/dts/state/session-store.d.ts +2 -0
  16. package/dist/dts/state/session-store.d.ts.map +1 -1
  17. package/dist/dts/utils/history-transform.d.ts +13 -0
  18. package/dist/dts/utils/history-transform.d.ts.map +1 -0
  19. package/dist/esm/components/chat-driver/chat-driver.js +119 -12
  20. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +8 -20
  21. package/dist/esm/config/config.js +18 -1
  22. package/dist/esm/main/main.js +43 -11
  23. package/dist/esm/main/main.styles.js +62 -0
  24. package/dist/esm/main/main.template.js +122 -71
  25. package/dist/esm/state/ai-assistant-slice.js +8 -0
  26. package/dist/esm/utils/history-transform.js +35 -0
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/docs/sub_agent.md +149 -211
  29. package/package.json +16 -16
  30. package/src/components/chat-driver/chat-driver.ts +161 -11
  31. package/src/components/orchestrating-driver/orchestrating-driver.ts +10 -22
  32. package/src/config/config.ts +24 -0
  33. package/src/main/main.styles.ts +62 -0
  34. package/src/main/main.template.ts +189 -117
  35. package/src/main/main.ts +43 -9
  36. package/src/state/ai-assistant-slice.ts +12 -0
  37. package/src/utils/history-transform.ts +40 -0
@@ -1,5 +1,6 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
3
+ import { applyHistoryCap } from '../../utils/history-transform';
3
4
  import { logger } from '../../utils/logger';
4
5
  import { TOOL_FOLD_SYMBOL } from '../../utils/tool-fold';
5
6
  const DEFAULT_MAX_TOOL_ITERATIONS = 50;
@@ -35,6 +36,8 @@ export class ChatDriver extends EventTarget {
35
36
  this.consecutiveFoldOps = 0;
36
37
  /** Consecutive unknown-tool calls without a real tool call. Reset on real tool execution. */
37
38
  this.consecutiveUnknownToolCalls = 0;
39
+ /** Sub-agents declared on the active agent config, keyed by name. */
40
+ this.subAgentsMap = new Map();
38
41
  this.toolHandlers = toolHandlers;
39
42
  this.toolDefinitions = toolDefinitions;
40
43
  this.systemPrompt = systemPrompt;
@@ -46,16 +49,24 @@ export class ChatDriver extends EventTarget {
46
49
  * each specialist turn so the shared driver runs with the right tools and prompt.
47
50
  */
48
51
  applyAgent(config) {
49
- var _a, _b;
52
+ var _a, _b, _c;
50
53
  this.systemPrompt = config.systemPrompt;
51
54
  this.toolDefinitions = (_a = config.toolDefinitions) !== null && _a !== void 0 ? _a : [];
52
55
  this.toolHandlers = (_b = config.toolHandlers) !== null && _b !== void 0 ? _b : {};
53
56
  this.primerHistory = config.primerHistory;
54
57
  this.activeAgentName = config.name;
58
+ this.subAgentsMap = new Map(((_c = config.subAgents) !== null && _c !== void 0 ? _c : []).map((s) => [s.name, s]));
55
59
  // Reset fold state when agent changes — each specialist starts fresh
56
60
  this.foldStack = [];
57
61
  this.consecutiveFoldOps = 0;
58
62
  }
63
+ /**
64
+ * Returns the early-stop result set by `completeSubAgent`, if any.
65
+ * Called by a parent `ChatDriver` after running this instance as a sub-agent.
66
+ */
67
+ getSubAgentCompletion() {
68
+ return this.subAgentCompletion;
69
+ }
59
70
  /**
60
71
  * Optional transform applied to conversation history immediately before each LLM request.
61
72
  * Cleared when `undefined`. Does not alter stored history.
@@ -212,6 +223,7 @@ export class ChatDriver extends EventTarget {
212
223
  return { reason: 'done' };
213
224
  }
214
225
  this.busy = true;
226
+ this.subAgentCompletion = undefined;
215
227
  this.appendToHistory({ role: 'user', content: userInput, attachments });
216
228
  try {
217
229
  return yield this.runToolLoop(userInput, attachments);
@@ -226,6 +238,89 @@ export class ChatDriver extends EventTarget {
226
238
  }
227
239
  });
228
240
  }
241
+ /**
242
+ * Builds the context object passed to every tool handler call.
243
+ * Centralised here so fold shortcut dispatch and the main tool loop use the
244
+ * same context without duplication.
245
+ *
246
+ * @param traceCapture - Optional per-invocation slot. When provided, the trace
247
+ * from any sub-agent call is written here rather than to shared instance state,
248
+ * so parallel tool calls each capture their own trace independently.
249
+ */
250
+ buildHandlerContext(traceCapture) {
251
+ return Object.assign(Object.assign({ requestInteraction: (componentName, data) => this.requestInteraction(componentName, data) }, (this.subAgentsMap.size > 0 && {
252
+ requestSubAgent: (name, options) => this.invokeSubAgent(name, options).then(({ result, trace }) => {
253
+ if (traceCapture)
254
+ traceCapture.trace = trace;
255
+ return result;
256
+ }),
257
+ })), { completeSubAgent: (result) => {
258
+ var _a;
259
+ if (this.subAgentCompletion) {
260
+ logger.warn(`ChatDriver(${(_a = this.activeAgentName) !== null && _a !== void 0 ? _a : 'unknown'}): completeSubAgent called more than once — ignoring`);
261
+ return;
262
+ }
263
+ this.subAgentCompletion = { result };
264
+ } });
265
+ }
266
+ /**
267
+ * Creates a child `ChatDriver` for the named sub-agent, runs it to completion,
268
+ * and returns its structured result (or final text fallback) together with the
269
+ * full child conversation trace. Callers receive both values so each parallel
270
+ * invocation can capture its own trace without touching shared instance state.
271
+ */
272
+ invokeSubAgent(name, options) {
273
+ return __awaiter(this, void 0, void 0, function* () {
274
+ var _a, _b, _c;
275
+ const subConfig = this.subAgentsMap.get(name);
276
+ if (!subConfig) {
277
+ const available = [...this.subAgentsMap.keys()].join(', ') || '(none)';
278
+ throw new Error(`Sub-agent "${name}" not found on agent "${this.activeAgentName}". Available: ${available}`);
279
+ }
280
+ const { task, historyCap, context } = options !== null && options !== void 0 ? options : {};
281
+ // Exclude the current in-flight assistant message (the one with tool calls that
282
+ // triggered this invocation) — it has unresolved tool calls that would confuse
283
+ // the sub-agent into thinking it needs to handle tools it doesn't own.
284
+ const lastMsg = this.history[this.history.length - 1];
285
+ const baseHistory = (lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.role) === 'assistant' && ((_a = lastMsg.toolCalls) === null || _a === void 0 ? void 0 : _a.length)
286
+ ? this.history.slice(0, -1)
287
+ : this.history;
288
+ const snapshotHistory = historyCap != null ? applyHistoryCap(baseHistory, historyCap) : [...baseHistory];
289
+ const contextMessages = context
290
+ ? [{ role: 'user', content: `[Sub-agent context]: ${JSON.stringify(context)}` }]
291
+ : [];
292
+ const effectivePrimer = [
293
+ ...snapshotHistory,
294
+ ...contextMessages,
295
+ ...((_b = subConfig.primerHistory) !== null && _b !== void 0 ? _b : []),
296
+ ];
297
+ const child = new ChatDriver(this.aiProvider);
298
+ child.applyAgent(Object.assign(Object.assign({}, subConfig), { primerHistory: effectivePrimer }));
299
+ const forwardTrace = (e) => {
300
+ this.dispatchEvent(new CustomEvent('sub-agent-history-updated', {
301
+ detail: { agentName: subConfig.name, history: e.detail },
302
+ }));
303
+ };
304
+ child.addEventListener('history-updated', forwardTrace);
305
+ this.dispatchEvent(new CustomEvent('sub-agent-start', { detail: { name } }));
306
+ try {
307
+ yield child.sendMessage(task !== null && task !== void 0 ? task : '');
308
+ }
309
+ finally {
310
+ child.removeEventListener('history-updated', forwardTrace);
311
+ this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: { name } }));
312
+ }
313
+ const trace = child.getHistory();
314
+ const completion = child.getSubAgentCompletion();
315
+ if (completion) {
316
+ return { result: completion.result, trace };
317
+ }
318
+ const finalMsg = [...trace]
319
+ .reverse()
320
+ .find((m) => { var _a, _b; return m.role === 'assistant' && !((_a = m.toolCalls) === null || _a === void 0 ? void 0 : _a.length) && ((_b = m.content) === null || _b === void 0 ? void 0 : _b.trim()); });
321
+ return { result: ((_c = finalMsg === null || finalMsg === void 0 ? void 0 : finalMsg.content) !== null && _c !== void 0 ? _c : ''), trace };
322
+ });
323
+ }
229
324
  /**
230
325
  * Continue the tool loop from current history without appending a new user message.
231
326
  * Used by OrchestratingDriver after an agent handoff.
@@ -239,6 +334,7 @@ export class ChatDriver extends EventTarget {
239
334
  return { reason: 'done' };
240
335
  }
241
336
  this.busy = true;
337
+ this.subAgentCompletion = undefined;
242
338
  try {
243
339
  return yield this.runToolLoop('', undefined, transientPrimer);
244
340
  }
@@ -327,9 +423,7 @@ export class ChatDriver extends EventTarget {
327
423
  const innerArgs = typeof args[key] === 'object' && args[key] !== null
328
424
  ? args[key]
329
425
  : {};
330
- return innerHandler(innerArgs, {
331
- requestInteraction: (c, d) => this.requestInteraction(c, d),
332
- }).then((r) => (typeof r === 'string' ? r : JSON.stringify(r)));
426
+ return innerHandler(innerArgs, this.buildHandlerContext()).then((r) => typeof r === 'string' ? r : JSON.stringify(r));
333
427
  }
334
428
  }
335
429
  // Normal two-step open
@@ -395,7 +489,11 @@ export class ChatDriver extends EventTarget {
395
489
  let currentAttachments = attachments;
396
490
  let iterations = 0;
397
491
  let malformedAttempts = 0;
398
- const startIteration = currentInput ? 1 : 0;
492
+ // True only for the very first LLM call. Used to exclude the pending user message
493
+ // from history (it is passed separately as currentInput). Must not be derived from
494
+ // `iterations` because fold operations decrement iterations, which would incorrectly
495
+ // re-trigger the slice on subsequent calls after a fold open/close.
496
+ let firstLlmCall = !!currentInput;
399
497
  while (iterations < this.maxToolIterations) {
400
498
  iterations += 1;
401
499
  const foldSuffix = this.buildFoldSystemPromptSuffix();
@@ -403,7 +501,8 @@ export class ChatDriver extends EventTarget {
403
501
  ? `${this.systemPrompt}${foldSuffix}`
404
502
  : foldSuffix || undefined;
405
503
  const primer = [...((_a = this.primerHistory) !== null && _a !== void 0 ? _a : []), ...(transientPrimer !== null && transientPrimer !== void 0 ? transientPrimer : [])];
406
- const baseHistory = iterations === startIteration ? this.history.slice(0, -1) : this.history;
504
+ const baseHistory = firstLlmCall ? this.history.slice(0, -1) : this.history;
505
+ firstLlmCall = false;
407
506
  const historyForProvider = this.providerHistoryTransform
408
507
  ? this.providerHistoryTransform([...baseHistory])
409
508
  : baseHistory;
@@ -542,11 +641,14 @@ export class ChatDriver extends EventTarget {
542
641
  }
543
642
  // Real tool execution
544
643
  try {
545
- const result = yield handler(tc.args, {
546
- requestInteraction: (componentName, data) => this.requestInteraction(componentName, data),
547
- });
644
+ const traceCapture = {};
645
+ const result = yield handler(tc.args, this.buildHandlerContext(traceCapture));
548
646
  const content = typeof result === 'string' ? result : JSON.stringify(result);
549
- executedById.set(tc.id, { toolCallId: tc.id, content });
647
+ executedById.set(tc.id, {
648
+ toolCallId: tc.id,
649
+ content,
650
+ subAgentTrace: traceCapture.trace,
651
+ });
550
652
  anyRealToolExecuted = true;
551
653
  }
552
654
  catch (e) {
@@ -594,7 +696,7 @@ export class ChatDriver extends EventTarget {
594
696
  const tcMsg = this.history[tcMsgIdx];
595
697
  const availableToolNames = Object.keys(this.toolHandlers);
596
698
  const annotatedCalls = tcMsg.toolCalls.map((tc) => {
597
- var _a, _b, _c, _d;
699
+ var _a, _b, _c, _d, _e;
598
700
  const isFoldOpen = !!this.getFold(tc.name) ||
599
701
  (
600
702
  // Was a fold facade at time of the call (now the tool set has changed)
@@ -608,7 +710,7 @@ export class ChatDriver extends EventTarget {
608
710
  ? 'close'
609
711
  : undefined,
610
712
  // Use the fold path that was active at the START of this iteration (before any opens/closes)
611
- foldPath: !isFoldOpen && !isFoldClose && foldPath.length > 0 ? foldPath : undefined, unknown: isUnknown || undefined, availableTools: isUnknown ? availableToolNames : undefined });
713
+ foldPath: !isFoldOpen && !isFoldClose && foldPath.length > 0 ? foldPath : undefined, unknown: isUnknown || undefined, availableTools: isUnknown ? availableToolNames : undefined, subAgentTrace: (_e = executedById.get(tc.id)) === null || _e === void 0 ? void 0 : _e.subAgentTrace });
612
714
  });
613
715
  this.history[tcMsgIdx] = Object.assign(Object.assign({}, tcMsg), { toolCalls: annotatedCalls });
614
716
  this.dispatchEvent(new CustomEvent('history-updated', {
@@ -628,6 +730,11 @@ export class ChatDriver extends EventTarget {
628
730
  const { summary, remaining_task: remainingTask } = firstContinuation.args;
629
731
  return { reason: 'agent-handoff', summary, remainingTask };
630
732
  }
733
+ // Sub-agent early exit — checked here so the exit point mirrors the
734
+ // system-call pattern above. Set by completeSubAgent() in a tool handler.
735
+ if (this.subAgentCompletion) {
736
+ return { reason: 'done' };
737
+ }
631
738
  currentInput = '';
632
739
  }
633
740
  if (iterations >= this.maxToolIterations) {
@@ -1,4 +1,5 @@
1
1
  import { __awaiter } from "tslib";
2
+ import { transformHistoryForAgent } from '../../utils/history-transform';
2
3
  import { logger } from '../../utils/logger';
3
4
  import { ChatDriver, REQUEST_CONTINUATION_TOOL } from '../chat-driver/chat-driver';
4
5
  const DEFAULT_MAX_HANDOFFS = 3;
@@ -35,25 +36,6 @@ function buildFallbackSystemPrompt(fallback, specialists) {
35
36
  }
36
37
  return `You are a helpful assistant. You cannot directly help with the user's request, but the following specialists are available:\n\n${agentList}\n\nPolitely let the user know what you can help with and invite them to rephrase their request.`;
37
38
  }
38
- /**
39
- * Prepares history for the LLM only: masks tool call args and results from other
40
- * agents so the active specialist is not confused by tools it does not have.
41
- * Canonical history in `ChatDriver` stays unmasked for UI and logging.
42
- */
43
- function transformHistoryForAgent(history, agentName) {
44
- return history.map((msg) => {
45
- var _a;
46
- if (!msg.agentName || msg.agentName === agentName)
47
- return msg;
48
- if ((_a = msg.toolCalls) === null || _a === void 0 ? void 0 : _a.length) {
49
- return Object.assign(Object.assign({}, msg), { toolCalls: msg.toolCalls.map((tc) => (Object.assign(Object.assign({}, tc), { args: {} }))) });
50
- }
51
- if (msg.toolResult) {
52
- return Object.assign(Object.assign({}, msg), { toolResult: Object.assign(Object.assign({}, msg.toolResult), { content: "[other agent's tool result omitted]" }) });
53
- }
54
- return msg;
55
- });
56
- }
57
39
  /**
58
40
  * Orchestrates multiple specialist agents. Sits between `FoundationAiAssistant`
59
41
  * and `ChatDriver`, classifying each user message and routing it to the right
@@ -80,10 +62,16 @@ export class OrchestratingDriver extends EventTarget {
80
62
  this.fallback = rawFallback
81
63
  ? Object.assign(Object.assign({}, rawFallback), { systemPrompt: buildFallbackSystemPrompt(rawFallback, this.specialists) }) : undefined;
82
64
  this.chatDriver = new ChatDriver(aiProvider, {}, [], undefined, undefined, options.maxToolIterations, options.maxFoldOperations);
83
- // Proxy history-updated events from the shared driver
65
+ // Proxy events from the shared driver
84
66
  this.chatDriver.addEventListener('history-updated', (e) => {
85
67
  this.dispatchEvent(new CustomEvent('history-updated', { detail: e.detail }));
86
68
  });
69
+ this.chatDriver.addEventListener('sub-agent-history-updated', (e) => {
70
+ this.dispatchEvent(new CustomEvent('sub-agent-history-updated', { detail: e.detail }));
71
+ });
72
+ this.chatDriver.addEventListener('sub-agent-stop', (e) => {
73
+ this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: e.detail }));
74
+ });
87
75
  }
88
76
  resolveInteraction(interactionId, result) {
89
77
  this.chatDriver.resolveInteraction(interactionId, result);
@@ -1 +1,18 @@
1
- export {};
1
+ /**
2
+ * Identity helper that infers the narrowest possible type for an agent config,
3
+ * preserving string literal types (including `name`) without requiring `as const`.
4
+ *
5
+ * Use this when you need `typeof myAgent` to carry the literal `name` type —
6
+ * for example, when wiring `ChatToolHandlers<typeof myAgent>`.
7
+ *
8
+ * ```ts
9
+ * const myAgent = defineAgent({ name: 'my_agent', ... });
10
+ * type Handlers = ChatToolHandlers<typeof myAgent>;
11
+ * // requestSubAgent name param is now typed as 'my_agent'
12
+ * ```
13
+ *
14
+ * @beta
15
+ */
16
+ export function defineAgent(config) {
17
+ return config;
18
+ }
@@ -51,6 +51,12 @@ const MODEL_CONTEXT_LIMITS = {
51
51
  };
52
52
  // Register supporting components when the main component module is imported.
53
53
  avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiChatBubble, AiActivityHalo, ChatSuggestions);
54
+ /** Recursively strips `toolHandlers` from an agent and all its sub-agents. */
55
+ function stripHandlers(agent) {
56
+ const { toolHandlers: _, subAgents } = agent, rest = __rest(agent, ["toolHandlers", "subAgents"]);
57
+ return (subAgents === null || subAgents === void 0 ? void 0 : subAgents.length)
58
+ ? Object.assign(Object.assign({}, rest), { subAgents: subAgents.map(stripHandlers) }) : rest;
59
+ }
54
60
  /**
55
61
  * Foundation AI Assistant component.
56
62
  *
@@ -112,17 +118,12 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
112
118
  return (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.activeAgent;
113
119
  }
114
120
  set activeAgent(value) {
115
- var _a, _b;
116
- // Strip toolHandlers before storing — functions are non-serializable and Redux
117
- // serializable-state middleware will warn. toolHandlers are never read back from
118
- // the store; they are always sourced from this.agents when the driver is built.
119
- if (value) {
120
- const { toolHandlers: _ } = value, serializable = __rest(value, ["toolHandlers"]);
121
- (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveAgent(serializable);
122
- }
123
- else {
124
- (_b = this._sessionRef) === null || _b === void 0 ? void 0 : _b.actions.aiAssistant.setActiveAgent(undefined);
125
- }
121
+ var _a;
122
+ // Strip toolHandlers recursively before storing — functions are non-serializable
123
+ // and Redux serializable-state middleware will warn. toolHandlers are never read
124
+ // back from the store; they are always sourced from this.agents when the driver
125
+ // is built.
126
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveAgent(value ? stripHandlers(value) : undefined);
126
127
  }
127
128
  get suggestionsState() {
128
129
  var _a, _b;
@@ -168,6 +169,22 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
168
169
  var _a;
169
170
  (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setEnabledAnimations(value);
170
171
  }
172
+ get liveSubAgentTrace() {
173
+ var _a, _b;
174
+ return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.liveSubAgentTrace) !== null && _b !== void 0 ? _b : [];
175
+ }
176
+ set liveSubAgentTrace(value) {
177
+ var _a;
178
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setLiveSubAgentTrace(value);
179
+ }
180
+ get liveSubAgentName() {
181
+ var _a, _b;
182
+ return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.liveSubAgentName) !== null && _b !== void 0 ? _b : null;
183
+ }
184
+ set liveSubAgentName(value) {
185
+ var _a;
186
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setLiveSubAgentName(value);
187
+ }
171
188
  /** Most recent prompt token count from the AI provider, if available. */
172
189
  get contextTokens() {
173
190
  var _a;
@@ -355,8 +372,23 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
355
372
  this.messages = [...e.detail];
356
373
  };
357
374
  driver.addEventListener('history-updated', onHistoryUpdated);
375
+ const onSubAgentHistoryUpdated = (e) => {
376
+ const { agentName, history } = e.detail;
377
+ this.liveSubAgentName = agentName;
378
+ // structuredClone so Immer freezes an independent copy, not the child
379
+ // driver's own history array (which is still being mutated by the tool loop).
380
+ this.liveSubAgentTrace = structuredClone(history);
381
+ };
382
+ const onSubAgentStop = () => {
383
+ this.liveSubAgentTrace = [];
384
+ this.liveSubAgentName = null;
385
+ };
386
+ driver.addEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated);
387
+ driver.addEventListener('sub-agent-stop', onSubAgentStop);
358
388
  const cleanups = [
359
389
  () => driver.removeEventListener('history-updated', onHistoryUpdated),
390
+ () => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
391
+ () => driver.removeEventListener('sub-agent-stop', onSubAgentStop),
360
392
  ];
361
393
  if (driver instanceof OrchestratingDriver) {
362
394
  const onOrchStart = () => {
@@ -445,6 +445,68 @@ export const styles = css `
445
445
  padding-left: 8px;
446
446
  }
447
447
 
448
+ .live-sub-agent-trace {
449
+ animation: slide-in-left 0.25s ease-out;
450
+ border-left: 2px solid var(--neutral-stroke-rest);
451
+ padding: 4px 8px;
452
+ margin: 4px 0;
453
+ opacity: 80%;
454
+ }
455
+
456
+ .live-sub-agent-name {
457
+ font-family: monospace;
458
+ font-size: 0.8em;
459
+ opacity: 70%;
460
+ font-style: italic;
461
+ display: block;
462
+ margin-bottom: 2px;
463
+ }
464
+
465
+ .sub-agent-trace {
466
+ margin-top: 6px;
467
+ border-left: 2px solid var(--neutral-stroke-rest);
468
+ padding-left: 8px;
469
+ }
470
+
471
+ .sub-agent-trace-summary {
472
+ font-family: monospace;
473
+ font-size: 0.85em;
474
+ opacity: 70%;
475
+ cursor: pointer;
476
+ user-select: none;
477
+ padding: 2px 0;
478
+ }
479
+
480
+ .sub-agent-trace-summary:hover {
481
+ opacity: 100%;
482
+ }
483
+
484
+ .sub-agent-message {
485
+ font-family: monospace;
486
+ font-size: 0.85em;
487
+ margin-top: 4px;
488
+ }
489
+
490
+ .sub-agent-assistant {
491
+ opacity: 85%;
492
+ white-space: pre-wrap;
493
+ }
494
+
495
+ .sub-agent-tool-call {
496
+ opacity: 60%;
497
+ }
498
+
499
+ .sub-agent-tool-name::before {
500
+ content: '⚙ ';
501
+ }
502
+
503
+ .sub-agent-tool-result {
504
+ opacity: 50%;
505
+ border-left: 1px solid var(--neutral-stroke-rest);
506
+ padding-left: 6px;
507
+ white-space: pre-wrap;
508
+ }
509
+
448
510
  .input-row {
449
511
  display: flex;
450
512
  gap: calc(var(--design-unit) * 2px);