@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
|
@@ -7,9 +7,11 @@ import type {
|
|
|
7
7
|
ChatToolCall,
|
|
8
8
|
ChatToolDefinition,
|
|
9
9
|
ChatToolHandlers,
|
|
10
|
+
SubAgentRequestOptions,
|
|
10
11
|
} from '@genesislcap/foundation-ai';
|
|
11
12
|
import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
|
|
12
13
|
import type { AgentConfig } from '../../config/config';
|
|
14
|
+
import { applyHistoryCap } from '../../utils/history-transform';
|
|
13
15
|
import { logger } from '../../utils/logger';
|
|
14
16
|
import { TOOL_FOLD_SYMBOL, type ToolFold } from '../../utils/tool-fold';
|
|
15
17
|
import type { AiDriver, AllAgentSummary } from '../ai-driver/ai-driver';
|
|
@@ -18,6 +20,7 @@ const DEFAULT_MAX_TOOL_ITERATIONS = 50;
|
|
|
18
20
|
const DEFAULT_MAX_FOLD_OPERATIONS = 5;
|
|
19
21
|
const DEFAULT_MAX_UNKNOWN_TOOL_CALLS = 5;
|
|
20
22
|
const MAX_MALFORMED_RETRIES = 2;
|
|
23
|
+
const MAX_EMPTY_RESPONSE_RETRIES = 3;
|
|
21
24
|
const SUGGESTIONS_HISTORY_WINDOW = 8;
|
|
22
25
|
|
|
23
26
|
/** Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver. */
|
|
@@ -77,6 +80,16 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
77
80
|
private consecutiveUnknownToolCalls = 0;
|
|
78
81
|
private readonly maxFoldOperations: number;
|
|
79
82
|
|
|
83
|
+
/** Sub-agents declared on the active agent config, keyed by name. */
|
|
84
|
+
private subAgentsMap: Map<string, AgentConfig> = new Map();
|
|
85
|
+
/**
|
|
86
|
+
* Set by `completeSubAgent` inside a sub-agent tool handler. Checked at the
|
|
87
|
+
* same point in the loop as `REQUEST_CONTINUATION_TOOL` — after tool results
|
|
88
|
+
* are appended — so the exit path mirrors the system-call pattern.
|
|
89
|
+
* `undefined` means the loop has not been stopped early.
|
|
90
|
+
*/
|
|
91
|
+
private subAgentCompletion: { result: unknown } | undefined;
|
|
92
|
+
|
|
80
93
|
constructor(
|
|
81
94
|
private readonly aiProvider: AIProvider,
|
|
82
95
|
toolHandlers: ChatToolHandlers = {},
|
|
@@ -104,11 +117,20 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
104
117
|
this.toolHandlers = config.toolHandlers ?? {};
|
|
105
118
|
this.primerHistory = config.primerHistory;
|
|
106
119
|
this.activeAgentName = config.name;
|
|
120
|
+
this.subAgentsMap = new Map((config.subAgents ?? []).map((s) => [s.name, s]));
|
|
107
121
|
// Reset fold state when agent changes — each specialist starts fresh
|
|
108
122
|
this.foldStack = [];
|
|
109
123
|
this.consecutiveFoldOps = 0;
|
|
110
124
|
}
|
|
111
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Returns the early-stop result set by `completeSubAgent`, if any.
|
|
128
|
+
* Called by a parent `ChatDriver` after running this instance as a sub-agent.
|
|
129
|
+
*/
|
|
130
|
+
getSubAgentCompletion(): { result: unknown } | undefined {
|
|
131
|
+
return this.subAgentCompletion;
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
/**
|
|
113
135
|
* Optional transform applied to conversation history immediately before each LLM request.
|
|
114
136
|
* Cleared when `undefined`. Does not alter stored history.
|
|
@@ -281,6 +303,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
281
303
|
}
|
|
282
304
|
|
|
283
305
|
this.busy = true;
|
|
306
|
+
this.subAgentCompletion = undefined;
|
|
284
307
|
this.appendToHistory({ role: 'user', content: userInput, attachments });
|
|
285
308
|
|
|
286
309
|
try {
|
|
@@ -294,6 +317,116 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
294
317
|
}
|
|
295
318
|
}
|
|
296
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Builds the context object passed to every tool handler call.
|
|
322
|
+
* Centralised here so fold shortcut dispatch and the main tool loop use the
|
|
323
|
+
* same context without duplication.
|
|
324
|
+
*
|
|
325
|
+
* @param traceCapture - Optional per-invocation slot. When provided, the trace
|
|
326
|
+
* from any sub-agent call is written here rather than to shared instance state,
|
|
327
|
+
* so parallel tool calls each capture their own trace independently.
|
|
328
|
+
*/
|
|
329
|
+
private buildHandlerContext(traceCapture?: { trace?: ChatMessage[] }) {
|
|
330
|
+
return {
|
|
331
|
+
requestInteraction: <T>(componentName: string, data: any): Promise<T> =>
|
|
332
|
+
this.requestInteraction(componentName, data),
|
|
333
|
+
...(this.subAgentsMap.size > 0 && {
|
|
334
|
+
requestSubAgent: <T = never>(
|
|
335
|
+
name: string,
|
|
336
|
+
options?: SubAgentRequestOptions,
|
|
337
|
+
): Promise<T | string> =>
|
|
338
|
+
this.invokeSubAgent<T>(name, options).then(({ result, trace }) => {
|
|
339
|
+
if (traceCapture) traceCapture.trace = trace;
|
|
340
|
+
return result;
|
|
341
|
+
}),
|
|
342
|
+
}),
|
|
343
|
+
completeSubAgent: (result: unknown): void => {
|
|
344
|
+
if (this.subAgentCompletion) {
|
|
345
|
+
logger.warn(
|
|
346
|
+
`ChatDriver(${this.activeAgentName ?? 'unknown'}): completeSubAgent called more than once — ignoring`,
|
|
347
|
+
);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
this.subAgentCompletion = { result };
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Creates a child `ChatDriver` for the named sub-agent, runs it to completion,
|
|
357
|
+
* and returns its structured result (or final text fallback) together with the
|
|
358
|
+
* full child conversation trace. Callers receive both values so each parallel
|
|
359
|
+
* invocation can capture its own trace without touching shared instance state.
|
|
360
|
+
*/
|
|
361
|
+
private async invokeSubAgent<T = never>(
|
|
362
|
+
name: string,
|
|
363
|
+
options?: SubAgentRequestOptions,
|
|
364
|
+
): Promise<{ result: T | string; trace: ChatMessage[] }> {
|
|
365
|
+
const subConfig = this.subAgentsMap.get(name);
|
|
366
|
+
if (!subConfig) {
|
|
367
|
+
const available = [...this.subAgentsMap.keys()].join(', ') || '(none)';
|
|
368
|
+
throw new Error(
|
|
369
|
+
`Sub-agent "${name}" not found on agent "${this.activeAgentName}". Available: ${available}`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const { task, historyCap, context } = options ?? {};
|
|
374
|
+
|
|
375
|
+
// Exclude the current in-flight assistant message (the one with tool calls that
|
|
376
|
+
// triggered this invocation) — it has unresolved tool calls that would confuse
|
|
377
|
+
// the sub-agent into thinking it needs to handle tools it doesn't own.
|
|
378
|
+
const lastMsg = this.history[this.history.length - 1];
|
|
379
|
+
const baseHistory =
|
|
380
|
+
lastMsg?.role === 'assistant' && lastMsg.toolCalls?.length
|
|
381
|
+
? this.history.slice(0, -1)
|
|
382
|
+
: this.history;
|
|
383
|
+
|
|
384
|
+
const snapshotHistory =
|
|
385
|
+
historyCap != null ? applyHistoryCap(baseHistory, historyCap) : [...baseHistory];
|
|
386
|
+
|
|
387
|
+
const contextMessages: ChatMessage[] = context
|
|
388
|
+
? [{ role: 'user', content: `[Sub-agent context]: ${JSON.stringify(context)}` }]
|
|
389
|
+
: [];
|
|
390
|
+
|
|
391
|
+
const effectivePrimer: ChatMessage[] = [
|
|
392
|
+
...snapshotHistory,
|
|
393
|
+
...contextMessages,
|
|
394
|
+
...(subConfig.primerHistory ?? []),
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
const child = new ChatDriver(this.aiProvider);
|
|
398
|
+
child.applyAgent({ ...subConfig, primerHistory: effectivePrimer });
|
|
399
|
+
|
|
400
|
+
const forwardTrace = (e: Event) => {
|
|
401
|
+
this.dispatchEvent(
|
|
402
|
+
new CustomEvent('sub-agent-history-updated', {
|
|
403
|
+
detail: { agentName: subConfig.name, history: (e as CustomEvent<ChatMessage[]>).detail },
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
child.addEventListener('history-updated', forwardTrace);
|
|
408
|
+
|
|
409
|
+
this.dispatchEvent(new CustomEvent('sub-agent-start', { detail: { name } }));
|
|
410
|
+
try {
|
|
411
|
+
await child.sendMessage(task ?? '');
|
|
412
|
+
} finally {
|
|
413
|
+
child.removeEventListener('history-updated', forwardTrace);
|
|
414
|
+
this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: { name } }));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const trace = child.getHistory() as ChatMessage[];
|
|
418
|
+
const completion = child.getSubAgentCompletion();
|
|
419
|
+
|
|
420
|
+
if (completion) {
|
|
421
|
+
return { result: completion.result as T, trace };
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const finalMsg = [...trace]
|
|
425
|
+
.reverse()
|
|
426
|
+
.find((m) => m.role === 'assistant' && !m.toolCalls?.length && m.content?.trim());
|
|
427
|
+
return { result: (finalMsg?.content ?? '') as string, trace };
|
|
428
|
+
}
|
|
429
|
+
|
|
297
430
|
/**
|
|
298
431
|
* Continue the tool loop from current history without appending a new user message.
|
|
299
432
|
* Used by OrchestratingDriver after an agent handoff.
|
|
@@ -306,6 +439,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
306
439
|
}
|
|
307
440
|
|
|
308
441
|
this.busy = true;
|
|
442
|
+
this.subAgentCompletion = undefined;
|
|
309
443
|
try {
|
|
310
444
|
return await this.runToolLoop('', undefined, transientPrimer);
|
|
311
445
|
} catch (e) {
|
|
@@ -400,9 +534,9 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
400
534
|
typeof args[key] === 'object' && args[key] !== null
|
|
401
535
|
? (args[key] as Record<string, unknown>)
|
|
402
536
|
: {};
|
|
403
|
-
return innerHandler(innerArgs,
|
|
404
|
-
|
|
405
|
-
|
|
537
|
+
return innerHandler(innerArgs, this.buildHandlerContext()).then((r) =>
|
|
538
|
+
typeof r === 'string' ? r : JSON.stringify(r),
|
|
539
|
+
);
|
|
406
540
|
}
|
|
407
541
|
}
|
|
408
542
|
|
|
@@ -487,7 +621,12 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
487
621
|
let currentAttachments: ChatAttachment[] | undefined = attachments;
|
|
488
622
|
let iterations = 0;
|
|
489
623
|
let malformedAttempts = 0;
|
|
490
|
-
|
|
624
|
+
let emptyResponseAttempts = 0;
|
|
625
|
+
// True only for the very first LLM call. Used to exclude the pending user message
|
|
626
|
+
// from history (it is passed separately as currentInput). Must not be derived from
|
|
627
|
+
// `iterations` because fold operations decrement iterations, which would incorrectly
|
|
628
|
+
// re-trigger the slice on subsequent calls after a fold open/close.
|
|
629
|
+
let firstLlmCall = !!currentInput;
|
|
491
630
|
|
|
492
631
|
while (iterations < this.maxToolIterations) {
|
|
493
632
|
iterations += 1;
|
|
@@ -498,7 +637,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
498
637
|
: foldSuffix || undefined;
|
|
499
638
|
|
|
500
639
|
const primer = [...(this.primerHistory ?? []), ...(transientPrimer ?? [])];
|
|
501
|
-
const baseHistory =
|
|
640
|
+
const baseHistory = firstLlmCall ? this.history.slice(0, -1) : this.history;
|
|
641
|
+
firstLlmCall = false;
|
|
502
642
|
const historyForProvider = this.providerHistoryTransform
|
|
503
643
|
? this.providerHistoryTransform([...baseHistory])
|
|
504
644
|
: baseHistory;
|
|
@@ -507,7 +647,9 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
507
647
|
const systemPrompt =
|
|
508
648
|
malformedAttempts > 0
|
|
509
649
|
? `${baseSystemPrompt ?? ''}\n\nIMPORTANT: Use only the structured function-call API to invoke tools. Do not write Python code or use Python-style syntax to call tools.`
|
|
510
|
-
:
|
|
650
|
+
: emptyResponseAttempts > 0
|
|
651
|
+
? `${baseSystemPrompt ?? ''}\n\nIMPORTANT: You must respond to the user's message. Call the appropriate tool or provide a text response — do not return an empty response.`
|
|
652
|
+
: baseSystemPrompt;
|
|
511
653
|
|
|
512
654
|
const options: ChatRequestOptions = {
|
|
513
655
|
systemPrompt,
|
|
@@ -547,10 +689,10 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
547
689
|
const isEmptyResponse = !response.content?.trim() && !response.toolCalls?.length;
|
|
548
690
|
|
|
549
691
|
if (isEmptyResponse) {
|
|
550
|
-
|
|
551
|
-
if (
|
|
692
|
+
emptyResponseAttempts += 1;
|
|
693
|
+
if (emptyResponseAttempts < MAX_EMPTY_RESPONSE_RETRIES) {
|
|
552
694
|
logger.warn(
|
|
553
|
-
`ChatDriver: empty model response, retrying (${
|
|
695
|
+
`ChatDriver: empty model response, retrying (${emptyResponseAttempts}/${MAX_EMPTY_RESPONSE_RETRIES})`,
|
|
554
696
|
);
|
|
555
697
|
iterations -= 1;
|
|
556
698
|
continue;
|
|
@@ -581,7 +723,10 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
581
723
|
[[], []],
|
|
582
724
|
);
|
|
583
725
|
|
|
584
|
-
const executedById = new Map<
|
|
726
|
+
const executedById = new Map<
|
|
727
|
+
string,
|
|
728
|
+
{ toolCallId: string; content: string; subAgentTrace?: ChatMessage[] }
|
|
729
|
+
>();
|
|
585
730
|
const unknownToolIds = new Set<string>();
|
|
586
731
|
let anyRealToolExecuted = false;
|
|
587
732
|
let hitUnknownToolLimit = false;
|
|
@@ -660,12 +805,14 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
660
805
|
|
|
661
806
|
// Real tool execution
|
|
662
807
|
try {
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
this.requestInteraction(componentName, data),
|
|
666
|
-
});
|
|
808
|
+
const traceCapture: { trace?: ChatMessage[] } = {};
|
|
809
|
+
const result = await handler(tc.args, this.buildHandlerContext(traceCapture));
|
|
667
810
|
const content = typeof result === 'string' ? result : JSON.stringify(result);
|
|
668
|
-
executedById.set(tc.id, {
|
|
811
|
+
executedById.set(tc.id, {
|
|
812
|
+
toolCallId: tc.id,
|
|
813
|
+
content,
|
|
814
|
+
subAgentTrace: traceCapture.trace,
|
|
815
|
+
});
|
|
669
816
|
anyRealToolExecuted = true;
|
|
670
817
|
} catch (e) {
|
|
671
818
|
logger.error(`ChatDriver tool "${tc.name}" failed:`, e);
|
|
@@ -734,6 +881,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
734
881
|
foldPath: !isFoldOpen && !isFoldClose && foldPath.length > 0 ? foldPath : undefined,
|
|
735
882
|
unknown: isUnknown || undefined,
|
|
736
883
|
availableTools: isUnknown ? availableToolNames : undefined,
|
|
884
|
+
subAgentTrace: executedById.get(tc.id)?.subAgentTrace,
|
|
737
885
|
};
|
|
738
886
|
});
|
|
739
887
|
this.history[tcMsgIdx] = { ...tcMsg, toolCalls: annotatedCalls };
|
|
@@ -765,6 +913,12 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
765
913
|
return { reason: 'agent-handoff', summary, remainingTask };
|
|
766
914
|
}
|
|
767
915
|
|
|
916
|
+
// Sub-agent early exit — checked here so the exit point mirrors the
|
|
917
|
+
// system-call pattern above. Set by completeSubAgent() in a tool handler.
|
|
918
|
+
if (this.subAgentCompletion) {
|
|
919
|
+
return { reason: 'done' };
|
|
920
|
+
}
|
|
921
|
+
|
|
768
922
|
currentInput = '';
|
|
769
923
|
}
|
|
770
924
|
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ChatRequestOptions,
|
|
7
7
|
} from '@genesislcap/foundation-ai';
|
|
8
8
|
import type { AgentConfig, FallbackAgentConfig, SpecialistAgentConfig } from '../../config/config';
|
|
9
|
+
import { transformHistoryForAgent } from '../../utils/history-transform';
|
|
9
10
|
import { logger } from '../../utils/logger';
|
|
10
11
|
import type { AiDriver, AllAgentSummary } from '../ai-driver/ai-driver';
|
|
11
12
|
import { ChatDriver, REQUEST_CONTINUATION_TOOL } from '../chat-driver/chat-driver';
|
|
@@ -54,27 +55,6 @@ function buildFallbackSystemPrompt(
|
|
|
54
55
|
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.`;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
/**
|
|
58
|
-
* Prepares history for the LLM only: masks tool call args and results from other
|
|
59
|
-
* agents so the active specialist is not confused by tools it does not have.
|
|
60
|
-
* Canonical history in `ChatDriver` stays unmasked for UI and logging.
|
|
61
|
-
*/
|
|
62
|
-
function transformHistoryForAgent(history: ChatMessage[], agentName: string): ChatMessage[] {
|
|
63
|
-
return history.map((msg) => {
|
|
64
|
-
if (!msg.agentName || msg.agentName === agentName) return msg;
|
|
65
|
-
if (msg.toolCalls?.length) {
|
|
66
|
-
return { ...msg, toolCalls: msg.toolCalls.map((tc) => ({ ...tc, args: {} })) };
|
|
67
|
-
}
|
|
68
|
-
if (msg.toolResult) {
|
|
69
|
-
return {
|
|
70
|
-
...msg,
|
|
71
|
-
toolResult: { ...msg.toolResult, content: "[other agent's tool result omitted]" },
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
return msg;
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
58
|
/**
|
|
79
59
|
* Orchestrates multiple specialist agents. Sits between `FoundationAiAssistant`
|
|
80
60
|
* and `ChatDriver`, classifying each user message and routing it to the right
|
|
@@ -132,10 +112,18 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
132
112
|
options.maxFoldOperations,
|
|
133
113
|
);
|
|
134
114
|
|
|
135
|
-
// Proxy
|
|
115
|
+
// Proxy events from the shared driver
|
|
136
116
|
this.chatDriver.addEventListener('history-updated', (e: Event) => {
|
|
137
117
|
this.dispatchEvent(new CustomEvent('history-updated', { detail: (e as CustomEvent).detail }));
|
|
138
118
|
});
|
|
119
|
+
this.chatDriver.addEventListener('sub-agent-history-updated', (e: Event) => {
|
|
120
|
+
this.dispatchEvent(
|
|
121
|
+
new CustomEvent('sub-agent-history-updated', { detail: (e as CustomEvent).detail }),
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
this.chatDriver.addEventListener('sub-agent-stop', (e: Event) => {
|
|
125
|
+
this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: (e as CustomEvent).detail }));
|
|
126
|
+
});
|
|
139
127
|
}
|
|
140
128
|
|
|
141
129
|
resolveInteraction(interactionId: string, result: unknown): void {
|
package/src/config/config.ts
CHANGED
|
@@ -22,6 +22,10 @@ interface BaseAgentConfig {
|
|
|
22
22
|
* Used to establish agent identity and behavioural rules.
|
|
23
23
|
*/
|
|
24
24
|
primerHistory?: ChatMessage[];
|
|
25
|
+
/**
|
|
26
|
+
* Sub-agents available to this agent's tool handlers via `requestSubAgent`.
|
|
27
|
+
*/
|
|
28
|
+
subAgents?: AgentConfig[];
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
/**
|
|
@@ -67,3 +71,23 @@ export interface FallbackAgentConfig extends BaseAgentConfig {
|
|
|
67
71
|
* @beta
|
|
68
72
|
*/
|
|
69
73
|
export type AgentConfig = SpecialistAgentConfig | FallbackAgentConfig;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Identity helper that infers the narrowest possible type for an agent config,
|
|
77
|
+
* preserving string literal types (including `name`) without requiring `as const`.
|
|
78
|
+
*
|
|
79
|
+
* Use this when you need `typeof myAgent` to carry the literal `name` type —
|
|
80
|
+
* for example, when wiring `ChatToolHandlers<typeof myAgent>`.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* const myAgent = defineAgent({ name: 'my_agent', ... });
|
|
84
|
+
* type Handlers = ChatToolHandlers<typeof myAgent>;
|
|
85
|
+
* // requestSubAgent name param is now typed as 'my_agent'
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @beta
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
export function defineAgent<const T extends AgentConfig>(config: T): T {
|
|
92
|
+
return config;
|
|
93
|
+
}
|
package/src/main/main.styles.ts
CHANGED
|
@@ -451,6 +451,68 @@ export const styles = css`
|
|
|
451
451
|
padding-left: 8px;
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
+
.live-sub-agent-trace {
|
|
455
|
+
animation: slide-in-left 0.25s ease-out;
|
|
456
|
+
border-left: 2px solid var(--neutral-stroke-rest);
|
|
457
|
+
padding: 4px 8px;
|
|
458
|
+
margin: 4px 0;
|
|
459
|
+
opacity: 80%;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.live-sub-agent-name {
|
|
463
|
+
font-family: monospace;
|
|
464
|
+
font-size: 0.8em;
|
|
465
|
+
opacity: 70%;
|
|
466
|
+
font-style: italic;
|
|
467
|
+
display: block;
|
|
468
|
+
margin-bottom: 2px;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.sub-agent-trace {
|
|
472
|
+
margin-top: 6px;
|
|
473
|
+
border-left: 2px solid var(--neutral-stroke-rest);
|
|
474
|
+
padding-left: 8px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.sub-agent-trace-summary {
|
|
478
|
+
font-family: monospace;
|
|
479
|
+
font-size: 0.85em;
|
|
480
|
+
opacity: 70%;
|
|
481
|
+
cursor: pointer;
|
|
482
|
+
user-select: none;
|
|
483
|
+
padding: 2px 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.sub-agent-trace-summary:hover {
|
|
487
|
+
opacity: 100%;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.sub-agent-message {
|
|
491
|
+
font-family: monospace;
|
|
492
|
+
font-size: 0.85em;
|
|
493
|
+
margin-top: 4px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.sub-agent-assistant {
|
|
497
|
+
opacity: 85%;
|
|
498
|
+
white-space: pre-wrap;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.sub-agent-tool-call {
|
|
502
|
+
opacity: 60%;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.sub-agent-tool-name::before {
|
|
506
|
+
content: '⚙ ';
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.sub-agent-tool-result {
|
|
510
|
+
opacity: 50%;
|
|
511
|
+
border-left: 1px solid var(--neutral-stroke-rest);
|
|
512
|
+
padding-left: 6px;
|
|
513
|
+
white-space: pre-wrap;
|
|
514
|
+
}
|
|
515
|
+
|
|
454
516
|
.input-row {
|
|
455
517
|
display: flex;
|
|
456
518
|
gap: calc(var(--design-unit) * 2px);
|