@tanstack/ai 0.58.0 → 0.59.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/esm/activities/chat/agents/define-agent.d.ts +81 -0
- package/dist/esm/activities/chat/agents/define-agent.js +34 -0
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -0
- package/dist/esm/activities/chat/agents/route.d.ts +53 -0
- package/dist/esm/activities/chat/agents/route.js +59 -0
- package/dist/esm/activities/chat/agents/route.js.map +1 -0
- package/dist/esm/activities/chat/agents/spawn.d.ts +124 -0
- package/dist/esm/activities/chat/agents/spawn.js +490 -0
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -0
- package/dist/esm/activities/chat/agents/turn.d.ts +36 -0
- package/dist/esm/activities/chat/agents/turn.js +78 -0
- package/dist/esm/activities/chat/agents/turn.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +13 -3
- package/dist/esm/activities/chat/index.js +337 -13
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.d.ts +7 -1
- package/dist/esm/activities/chat/messages.js +94 -18
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/run-store.d.ts +43 -7
- package/dist/esm/activities/chat/middleware/run-store.js +8 -1
- package/dist/esm/activities/chat/middleware/run-store.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +47 -1
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +46 -0
- package/dist/esm/activities/chat/stream/processor.js +280 -7
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +17 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +54 -5
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.js +1 -1
- package/dist/esm/activities/generateImage/index.js +1 -1
- package/dist/esm/activities/generateLiveVideo/index.js +1 -1
- package/dist/esm/activities/generateSpeech/index.js +1 -1
- package/dist/esm/activities/generateTranscription/index.js +1 -1
- package/dist/esm/activities/generateVoice/index.js +1 -1
- package/dist/esm/activities/generateWorld/index.js +1 -1
- package/dist/esm/activities/index.d.ts +3 -0
- package/dist/esm/activities/index.js +5 -3
- package/dist/esm/activities/summarize/index.js +1 -1
- package/dist/esm/client.d.ts +5 -36
- package/dist/esm/client.js +4 -37
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +6 -3
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/types.d.ts +110 -88
- package/dist/esm/utilities/adapter-yield-chunk.d.ts +5 -1
- package/dist/esm/utilities/ag-ui-usage.d.ts +9 -9
- package/dist/esm/utilities/ag-ui-usage.js +66 -3
- package/dist/esm/utilities/ag-ui-usage.js.map +1 -1
- package/dist/esm/utilities/ag-ui-wire.js +56 -0
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/normalize-stream-chunk.js +7 -2
- package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -1
- package/dist/esm/utilities/spec-event-keys.js +13 -8
- package/dist/esm/utilities/spec-event-keys.js.map +1 -1
- package/dist/esm/utilities/subagent-wire.d.ts +36 -0
- package/dist/esm/utilities/subagent-wire.js +131 -0
- package/dist/esm/utilities/subagent-wire.js.map +1 -0
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/references/grok-adapter.md +1 -1
- package/skills/ai-core/media-generation/SKILL.md +2 -2
- package/skills/ai-core/middleware/SKILL.md +7 -4
- package/src/activities/chat/agents/define-agent.ts +121 -0
- package/src/activities/chat/agents/route.ts +115 -0
- package/src/activities/chat/agents/spawn.ts +806 -0
- package/src/activities/chat/agents/turn.ts +151 -0
- package/src/activities/chat/index.ts +529 -11
- package/src/activities/chat/messages.ts +125 -15
- package/src/activities/chat/middleware/run-store.ts +56 -7
- package/src/activities/chat/middleware/types.ts +47 -0
- package/src/activities/chat/stream/processor.ts +428 -8
- package/src/activities/chat/tools/tool-calls.ts +77 -11
- package/src/activities/index.ts +15 -0
- package/src/client.ts +22 -35
- package/src/index.ts +23 -0
- package/src/middlewares/content-guard.ts +7 -5
- package/src/types.ts +154 -94
- package/src/utilities/adapter-yield-chunk.ts +10 -2
- package/src/utilities/ag-ui-usage.test.ts +38 -0
- package/src/utilities/ag-ui-usage.ts +98 -11
- package/src/utilities/ag-ui-wire.ts +74 -0
- package/src/utilities/normalize-stream-chunk.ts +10 -2
- package/src/utilities/spec-event-keys.ts +34 -7
- package/src/utilities/subagent-wire.ts +184 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { SubagentInfo as AGUISubagentInfo } from '@ag-ui/core';
|
|
2
|
+
import { InterruptDefinition } from '../../../interrupt-definition.js';
|
|
3
|
+
import { AnyTool, ModelMessage, RunAgentResumeItem, SchemaInput, StreamChunk, UIMessage } from '../../../types.js';
|
|
4
|
+
import { AnyClientTool } from '../tools/tool-definition.js';
|
|
5
|
+
/**
|
|
6
|
+
* Context the library passes into {@link defineAgent} `run`.
|
|
7
|
+
*/
|
|
8
|
+
export interface SubagentRunContext {
|
|
9
|
+
messages: Array<UIMessage | ModelMessage>;
|
|
10
|
+
abortSignal?: AbortSignal;
|
|
11
|
+
threadId: string;
|
|
12
|
+
/** Run id for the child `chat()`. */
|
|
13
|
+
runId: string;
|
|
14
|
+
/**
|
|
15
|
+
* The run this child run continues. It is the parent chat run on the first
|
|
16
|
+
* run, and the interrupted parent run on a resume. Pass it to the child
|
|
17
|
+
* `chat()`.
|
|
18
|
+
*/
|
|
19
|
+
parentRunId: string;
|
|
20
|
+
/** Answers to this child's interrupts. Pass it to the child `chat()`. */
|
|
21
|
+
resume?: Array<RunAgentResumeItem>;
|
|
22
|
+
/**
|
|
23
|
+
* The child's AG-UI run id. Stays the same when an interrupted child
|
|
24
|
+
* continues. Pass it to the child `chat()` so its middleware sees
|
|
25
|
+
* `ctx.subagentRunId`.
|
|
26
|
+
*/
|
|
27
|
+
subagentRunId: string;
|
|
28
|
+
parentSubagentRunId?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A tool a child agent can carry into client part types.
|
|
32
|
+
* Server tools and client tools both qualify.
|
|
33
|
+
*/
|
|
34
|
+
export type SubagentTool = AnyTool | AnyClientTool;
|
|
35
|
+
/**
|
|
36
|
+
* A named child agent. `run` is a `chat()` call (or any stream of AG-UI chunks).
|
|
37
|
+
* `TTools` and `TSchema` stay on the object so `useChat({ subagents })` can
|
|
38
|
+
* type that child's parts.
|
|
39
|
+
*/
|
|
40
|
+
export interface DefinedAgent<TName extends string = string, TTools extends ReadonlyArray<SubagentTool> = ReadonlyArray<SubagentTool>, TSchema extends SchemaInput | undefined = SchemaInput | undefined, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = ReadonlyArray<InterruptDefinition<any, any, any, any>>> extends AGUISubagentInfo {
|
|
41
|
+
name: TName;
|
|
42
|
+
/** Required here: the router and the synthetic tool both read it. */
|
|
43
|
+
description: string;
|
|
44
|
+
run: (ctx: SubagentRunContext) => AsyncIterable<StreamChunk> | Promise<AsyncIterable<StreamChunk>>;
|
|
45
|
+
tools?: TTools;
|
|
46
|
+
interrupts?: TInterrupts;
|
|
47
|
+
outputSchema?: TSchema;
|
|
48
|
+
subagents?: unknown;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Choice options for a `decide()` router. `main` is required plus every agent name.
|
|
52
|
+
*/
|
|
53
|
+
export type SubagentChoiceOptions<TAgents extends ReadonlyArray<DefinedAgent>> = {
|
|
54
|
+
main: string;
|
|
55
|
+
} & {
|
|
56
|
+
[K in TAgents[number]['name']]: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Define a named child agent. Pass the same object to `chat({ subagents })`.
|
|
60
|
+
* Pass the agents array to `useChat({ subagents })` when you render parts
|
|
61
|
+
* yourself. The hook uses it for types only. It does not call `run`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* const researcher = defineAgent({
|
|
66
|
+
* name: 'researcher',
|
|
67
|
+
* description: 'Looks up facts',
|
|
68
|
+
* run: (ctx) =>
|
|
69
|
+
* chat({
|
|
70
|
+
* adapter: openaiText('gpt-5.6'),
|
|
71
|
+
* messages: ctx.messages,
|
|
72
|
+
* threadId: ctx.threadId,
|
|
73
|
+
* runId: ctx.runId,
|
|
74
|
+
* parentRunId: ctx.parentRunId,
|
|
75
|
+
* subagentRunId: ctx.subagentRunId,
|
|
76
|
+
* resume: ctx.resume,
|
|
77
|
+
* }),
|
|
78
|
+
* })
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function defineAgent<const TName extends string, const TTools extends ReadonlyArray<SubagentTool> = readonly [], TSchema extends SchemaInput | undefined = undefined, const TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []>(agent: DefinedAgent<TName, TTools, TSchema, TInterrupts>): DefinedAgent<TName, TTools, TSchema, TInterrupts>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/activities/chat/agents/define-agent.ts
|
|
2
|
+
/**
|
|
3
|
+
* Define a named child agent. Pass the same object to `chat({ subagents })`.
|
|
4
|
+
* Pass the agents array to `useChat({ subagents })` when you render parts
|
|
5
|
+
* yourself. The hook uses it for types only. It does not call `run`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const researcher = defineAgent({
|
|
10
|
+
* name: 'researcher',
|
|
11
|
+
* description: 'Looks up facts',
|
|
12
|
+
* run: (ctx) =>
|
|
13
|
+
* chat({
|
|
14
|
+
* adapter: openaiText('gpt-5.6'),
|
|
15
|
+
* messages: ctx.messages,
|
|
16
|
+
* threadId: ctx.threadId,
|
|
17
|
+
* runId: ctx.runId,
|
|
18
|
+
* parentRunId: ctx.parentRunId,
|
|
19
|
+
* subagentRunId: ctx.subagentRunId,
|
|
20
|
+
* resume: ctx.resume,
|
|
21
|
+
* }),
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function defineAgent(agent) {
|
|
26
|
+
if (agent.name.trim() === "") throw new Error("defineAgent requires a non-empty name");
|
|
27
|
+
if (agent.name.trim() === "main") throw new Error("defineAgent cannot use the name 'main'. A router uses it for the parent.");
|
|
28
|
+
if (agent.description.trim() === "") throw new Error("defineAgent requires a non-empty description");
|
|
29
|
+
return agent;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { defineAgent };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=define-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-agent.js","names":[],"sources":["../../../../../src/activities/chat/agents/define-agent.ts"],"sourcesContent":["import type { SubagentInfo as AGUISubagentInfo } from '@ag-ui/core'\nimport type { InterruptDefinition } from '../../../interrupt-definition'\nimport type {\n AnyTool,\n ModelMessage,\n RunAgentResumeItem,\n SchemaInput,\n StreamChunk,\n UIMessage,\n} from '../../../types'\nimport type { AnyClientTool } from '../tools/tool-definition'\n\n/**\n * Context the library passes into {@link defineAgent} `run`.\n */\nexport interface SubagentRunContext {\n messages: Array<UIMessage | ModelMessage>\n abortSignal?: AbortSignal\n threadId: string\n /** Run id for the child `chat()`. */\n runId: string\n /**\n * The run this child run continues. It is the parent chat run on the first\n * run, and the interrupted parent run on a resume. Pass it to the child\n * `chat()`.\n */\n parentRunId: string\n /** Answers to this child's interrupts. Pass it to the child `chat()`. */\n resume?: Array<RunAgentResumeItem>\n /**\n * The child's AG-UI run id. Stays the same when an interrupted child\n * continues. Pass it to the child `chat()` so its middleware sees\n * `ctx.subagentRunId`.\n */\n subagentRunId: string\n parentSubagentRunId?: string\n}\n\n/**\n * A tool a child agent can carry into client part types.\n * Server tools and client tools both qualify.\n */\nexport type SubagentTool = AnyTool | AnyClientTool\n\n/**\n * A named child agent. `run` is a `chat()` call (or any stream of AG-UI chunks).\n * `TTools` and `TSchema` stay on the object so `useChat({ subagents })` can\n * type that child's parts.\n */\nexport interface DefinedAgent<\n TName extends string = string,\n TTools extends ReadonlyArray<SubagentTool> = ReadonlyArray<SubagentTool>,\n TSchema extends SchemaInput | undefined = SchemaInput | undefined,\n TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =\n ReadonlyArray<InterruptDefinition<any, any, any, any>>,\n> extends AGUISubagentInfo {\n name: TName\n /** Required here: the router and the synthetic tool both read it. */\n description: string\n run: (\n ctx: SubagentRunContext,\n ) => AsyncIterable<StreamChunk> | Promise<AsyncIterable<StreamChunk>>\n tools?: TTools\n interrupts?: TInterrupts\n outputSchema?: TSchema\n subagents?: unknown\n}\n\n/**\n * Choice options for a `decide()` router. `main` is required plus every agent name.\n */\nexport type SubagentChoiceOptions<TAgents extends ReadonlyArray<DefinedAgent>> =\n { main: string } & {\n [K in TAgents[number]['name']]: string\n }\n\n/**\n * Define a named child agent. Pass the same object to `chat({ subagents })`.\n * Pass the agents array to `useChat({ subagents })` when you render parts\n * yourself. The hook uses it for types only. It does not call `run`.\n *\n * @example\n * ```ts\n * const researcher = defineAgent({\n * name: 'researcher',\n * description: 'Looks up facts',\n * run: (ctx) =>\n * chat({\n * adapter: openaiText('gpt-5.6'),\n * messages: ctx.messages,\n * threadId: ctx.threadId,\n * runId: ctx.runId,\n * parentRunId: ctx.parentRunId,\n * subagentRunId: ctx.subagentRunId,\n * resume: ctx.resume,\n * }),\n * })\n * ```\n */\nexport function defineAgent<\n const TName extends string,\n const TTools extends ReadonlyArray<SubagentTool> = readonly [],\n TSchema extends SchemaInput | undefined = undefined,\n const TInterrupts extends ReadonlyArray<\n InterruptDefinition<any, any, any, any>\n > = readonly [],\n>(agent: DefinedAgent<TName, TTools, TSchema, TInterrupts>) {\n if (agent.name.trim() === '') {\n throw new Error('defineAgent requires a non-empty name')\n }\n // A router returns 'main' to keep the turn on the parent.\n if (agent.name.trim() === 'main') {\n throw new Error(\n \"defineAgent cannot use the name 'main'. A router uses it for the parent.\",\n )\n }\n if (agent.description.trim() === '') {\n throw new Error('defineAgent requires a non-empty description')\n }\n return agent\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAgB,YAOd,OAA0D;CAC1D,IAAI,MAAM,KAAK,KAAK,MAAM,IACxB,MAAM,IAAI,MAAM,uCAAuC;CAGzD,IAAI,MAAM,KAAK,KAAK,MAAM,QACxB,MAAM,IAAI,MACR,0EACF;CAEF,IAAI,MAAM,YAAY,KAAK,MAAM,IAC/B,MAAM,IAAI,MAAM,8CAA8C;CAEhE,OAAO;AACT"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { boolean, choice } from '../../evaluate/index.js';
|
|
2
|
+
import { DefinedAgent } from './define-agent.js';
|
|
3
|
+
import { SubagentOrder, SubagentRouterPick } from './spawn.js';
|
|
4
|
+
export interface SubagentRouteOptions<TAgents extends ReadonlyArray<DefinedAgent>> {
|
|
5
|
+
/**
|
|
6
|
+
* Question text for every agent. The key is the agent name.
|
|
7
|
+
* Omit this and each question uses that agent's description.
|
|
8
|
+
*/
|
|
9
|
+
when?: {
|
|
10
|
+
[K in TAgents[number]['name']]: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Agents that run after the other selected agents. The lead group starts
|
|
14
|
+
* together. The `then` agents then run one after another in list order, and
|
|
15
|
+
* each reads the text so far. Used only when the router picks at least one
|
|
16
|
+
* agent from each group. Otherwise `pick` returns `{ names, order }`.
|
|
17
|
+
*/
|
|
18
|
+
then?: ReadonlyArray<TAgents[number]['name']>;
|
|
19
|
+
}
|
|
20
|
+
type RouteQuestions<TAgents extends ReadonlyArray<DefinedAgent>> = {
|
|
21
|
+
[K in TAgents[number]['name']]: ReturnType<typeof boolean>;
|
|
22
|
+
} & {
|
|
23
|
+
order: ReturnType<typeof choice<{
|
|
24
|
+
parallel: string;
|
|
25
|
+
sequence: string;
|
|
26
|
+
}>>;
|
|
27
|
+
};
|
|
28
|
+
type RouteAnswers<TAgents extends ReadonlyArray<DefinedAgent>> = {
|
|
29
|
+
[K in TAgents[number]['name']]: {
|
|
30
|
+
value: boolean;
|
|
31
|
+
};
|
|
32
|
+
} & {
|
|
33
|
+
order: {
|
|
34
|
+
value: SubagentOrder;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Build `decide()` questions for a subagent router.
|
|
39
|
+
*
|
|
40
|
+
* One yes/no question per agent, plus an `order` choice.
|
|
41
|
+
* `pick` returns `main`, one name, `{ names, order }`, or `{ steps }`.
|
|
42
|
+
* Names follow the `agents` array order.
|
|
43
|
+
* `{ names, order }` overrides `subagents.order` for that turn.
|
|
44
|
+
* `then`: agents that run after the other selected agents. The lead group
|
|
45
|
+
* starts together. The `then` agents then run one after another in list
|
|
46
|
+
* order, and each reads the text so far. Used only when the router picks at
|
|
47
|
+
* least one agent from each group. Otherwise `pick` returns `{ names, order }`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function subagentRoute<const TAgents extends ReadonlyArray<DefinedAgent>>(agents: TAgents, options?: SubagentRouteOptions<TAgents>): {
|
|
50
|
+
questions: RouteQuestions<TAgents>;
|
|
51
|
+
pick: (result: RouteAnswers<TAgents>) => SubagentRouterPick;
|
|
52
|
+
};
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { boolean, choice } from "../../evaluate/index.js";
|
|
2
|
+
//#region src/activities/chat/agents/route.ts
|
|
3
|
+
var ORDER_KEY = "order";
|
|
4
|
+
/**
|
|
5
|
+
* Build `decide()` questions for a subagent router.
|
|
6
|
+
*
|
|
7
|
+
* One yes/no question per agent, plus an `order` choice.
|
|
8
|
+
* `pick` returns `main`, one name, `{ names, order }`, or `{ steps }`.
|
|
9
|
+
* Names follow the `agents` array order.
|
|
10
|
+
* `{ names, order }` overrides `subagents.order` for that turn.
|
|
11
|
+
* `then`: agents that run after the other selected agents. The lead group
|
|
12
|
+
* starts together. The `then` agents then run one after another in list
|
|
13
|
+
* order, and each reads the text so far. Used only when the router picks at
|
|
14
|
+
* least one agent from each group. Otherwise `pick` returns `{ names, order }`.
|
|
15
|
+
*/
|
|
16
|
+
function subagentRoute(agents, options) {
|
|
17
|
+
const namesInList = new Set(agents.map((agent) => agent.name));
|
|
18
|
+
for (const agent of agents) if (agent.name === ORDER_KEY) throw new Error("subagentRoute cannot use an agent named \"order\". Rename that agent.");
|
|
19
|
+
for (const name of options?.then ?? []) if (!namesInList.has(name)) throw new Error(`subagentRoute then includes unknown agent "${name}".`);
|
|
20
|
+
const questions = { order: choice({
|
|
21
|
+
instructions: "When more than one agent runs, how must they run?",
|
|
22
|
+
options: {
|
|
23
|
+
parallel: "Start them together. Use this when no agent must read text from another agent. Working on the same topic is not a reason to wait.",
|
|
24
|
+
sequence: "Run them in agent-list order. Use this only when a later agent must read the earlier agent text, such as research notes and then a draft article."
|
|
25
|
+
}
|
|
26
|
+
}) };
|
|
27
|
+
for (const agent of agents) {
|
|
28
|
+
const when = options?.when?.[agent.name];
|
|
29
|
+
questions[agent.name] = boolean({ instructions: when ?? agent.description });
|
|
30
|
+
}
|
|
31
|
+
function pick(result) {
|
|
32
|
+
const names = agents.map((agent) => agent.name).filter((name) => result[name].value);
|
|
33
|
+
if (names.length === 0) return "main";
|
|
34
|
+
const only = names.length === 1 ? names[0] : void 0;
|
|
35
|
+
if (only !== void 0) return only;
|
|
36
|
+
const later = new Set(options?.then ?? []);
|
|
37
|
+
const lead = names.filter((name) => !later.has(name));
|
|
38
|
+
const tail = names.filter((name) => later.has(name));
|
|
39
|
+
if (lead.length === 0 || tail.length === 0) return {
|
|
40
|
+
names,
|
|
41
|
+
order: result.order.value
|
|
42
|
+
};
|
|
43
|
+
return { steps: [lead.length > 1 ? {
|
|
44
|
+
names: lead,
|
|
45
|
+
order: "parallel"
|
|
46
|
+
} : { names: lead }, tail.length > 1 ? {
|
|
47
|
+
names: tail,
|
|
48
|
+
order: "sequence"
|
|
49
|
+
} : { names: tail }] };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
questions,
|
|
53
|
+
pick
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { subagentRoute };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","names":[],"sources":["../../../../../src/activities/chat/agents/route.ts"],"sourcesContent":["import { boolean, choice } from '../../evaluate/index'\nimport type { DefinedAgent } from './define-agent'\nimport type { SubagentOrder, SubagentRouterPick } from './spawn'\n\nconst ORDER_KEY = 'order'\n\nexport interface SubagentRouteOptions<\n TAgents extends ReadonlyArray<DefinedAgent>,\n> {\n /**\n * Question text for every agent. The key is the agent name.\n * Omit this and each question uses that agent's description.\n */\n when?: { [K in TAgents[number]['name']]: string }\n /**\n * Agents that run after the other selected agents. The lead group starts\n * together. The `then` agents then run one after another in list order, and\n * each reads the text so far. Used only when the router picks at least one\n * agent from each group. Otherwise `pick` returns `{ names, order }`.\n */\n then?: ReadonlyArray<TAgents[number]['name']>\n}\n\ntype RouteQuestions<TAgents extends ReadonlyArray<DefinedAgent>> = {\n [K in TAgents[number]['name']]: ReturnType<typeof boolean>\n} & {\n order: ReturnType<\n typeof choice<{\n parallel: string\n sequence: string\n }>\n >\n}\n\ntype RouteAnswers<TAgents extends ReadonlyArray<DefinedAgent>> = {\n [K in TAgents[number]['name']]: { value: boolean }\n} & {\n order: { value: SubagentOrder }\n}\n\n/**\n * Build `decide()` questions for a subagent router.\n *\n * One yes/no question per agent, plus an `order` choice.\n * `pick` returns `main`, one name, `{ names, order }`, or `{ steps }`.\n * Names follow the `agents` array order.\n * `{ names, order }` overrides `subagents.order` for that turn.\n * `then`: agents that run after the other selected agents. The lead group\n * starts together. The `then` agents then run one after another in list\n * order, and each reads the text so far. Used only when the router picks at\n * least one agent from each group. Otherwise `pick` returns `{ names, order }`.\n */\nexport function subagentRoute<\n const TAgents extends ReadonlyArray<DefinedAgent>,\n>(agents: TAgents, options?: SubagentRouteOptions<TAgents>) {\n const namesInList = new Set(agents.map((agent) => agent.name))\n for (const agent of agents) {\n if (agent.name === ORDER_KEY) {\n throw new Error(\n 'subagentRoute cannot use an agent named \"order\". Rename that agent.',\n )\n }\n }\n for (const name of options?.then ?? []) {\n if (!namesInList.has(name)) {\n throw new Error(`subagentRoute then includes unknown agent \"${name}\".`)\n }\n }\n\n const questions: Record<string, unknown> = {\n order: choice({\n instructions: 'When more than one agent runs, how must they run?',\n options: {\n parallel:\n 'Start them together. Use this when no agent must read text from another agent. Working on the same topic is not a reason to wait.',\n sequence:\n 'Run them in agent-list order. Use this only when a later agent must read the earlier agent text, such as research notes and then a draft article.',\n },\n }),\n }\n\n for (const agent of agents) {\n const when = options?.when?.[agent.name as TAgents[number]['name']]\n questions[agent.name] = boolean({\n instructions: when ?? agent.description,\n })\n }\n\n function pick(result: RouteAnswers<TAgents>): SubagentRouterPick {\n const names = agents\n .map((agent) => agent.name)\n .filter((name) => result[name as TAgents[number]['name']].value)\n if (names.length === 0) return 'main'\n const only = names.length === 1 ? names[0] : undefined\n if (only !== undefined) return only\n const later = new Set(options?.then ?? [])\n const lead = names.filter((name) => !later.has(name))\n const tail = names.filter((name) => later.has(name))\n if (lead.length === 0 || tail.length === 0) {\n return { names, order: result.order.value }\n }\n return {\n steps: [\n lead.length > 1\n ? { names: lead, order: 'parallel' as const }\n : { names: lead },\n tail.length > 1\n ? { names: tail, order: 'sequence' as const }\n : { names: tail },\n ],\n }\n }\n\n return { questions: questions as RouteQuestions<TAgents>, pick }\n}\n"],"mappings":";;AAIA,IAAM,YAAY;;;;;;;;;;;;;AAgDlB,SAAgB,cAEd,QAAiB,SAAyC;CAC1D,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;CAC7D,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,SAAS,WACjB,MAAM,IAAI,MACR,uEACF;CAGJ,KAAK,MAAM,QAAQ,SAAS,QAAQ,CAAC,GACnC,IAAI,CAAC,YAAY,IAAI,IAAI,GACvB,MAAM,IAAI,MAAM,8CAA8C,KAAK,GAAG;CAI1E,MAAM,YAAqC,EACzC,OAAO,OAAO;EACZ,cAAc;EACd,SAAS;GACP,UACE;GACF,UACE;EACJ;CACF,CAAC,EACH;CAEA,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,SAAS,OAAO,MAAM;EACnC,UAAU,MAAM,QAAQ,QAAQ,EAC9B,cAAc,QAAQ,MAAM,YAC9B,CAAC;CACH;CAEA,SAAS,KAAK,QAAmD;EAC/D,MAAM,QAAQ,OACX,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,QAAQ,SAAS,OAAO,KAAgC,CAAC,KAAK;EACjE,IAAI,MAAM,WAAW,GAAG,OAAO;EAC/B,MAAM,OAAO,MAAM,WAAW,IAAI,MAAM,KAAK,KAAA;EAC7C,IAAI,SAAS,KAAA,GAAW,OAAO;EAC/B,MAAM,QAAQ,IAAI,IAAI,SAAS,QAAQ,CAAC,CAAC;EACzC,MAAM,OAAO,MAAM,QAAQ,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC;EACpD,MAAM,OAAO,MAAM,QAAQ,SAAS,MAAM,IAAI,IAAI,CAAC;EACnD,IAAI,KAAK,WAAW,KAAK,KAAK,WAAW,GACvC,OAAO;GAAE;GAAO,OAAO,OAAO,MAAM;EAAM;EAE5C,OAAO,EACL,OAAO,CACL,KAAK,SAAS,IACV;GAAE,OAAO;GAAM,OAAO;EAAoB,IAC1C,EAAE,OAAO,KAAK,GAClB,KAAK,SAAS,IACV;GAAE,OAAO;GAAM,OAAO;EAAoB,IAC1C,EAAE,OAAO,KAAK,CACpB,EACF;CACF;CAEA,OAAO;EAAa;EAAsC;CAAK;AACjE"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { EventType, Interrupt, ModelMessage, RunAgentResumeItem, StreamChunk, TokenUsage, Tool, UIMessage } from '../../../types.js';
|
|
2
|
+
import { SpecTokenUsage } from '../../../utilities/ag-ui-usage.js';
|
|
3
|
+
import { DefinedAgent, SubagentRunContext } from './define-agent.js';
|
|
4
|
+
import { ChatMiddleware } from '../middleware/types.js';
|
|
5
|
+
import { SubagentTurn } from './turn.js';
|
|
6
|
+
export declare const SUBAGENT_STARTED = EventType.SUBAGENT_STARTED;
|
|
7
|
+
export declare const SUBAGENT_FINISHED = EventType.SUBAGENT_FINISHED;
|
|
8
|
+
export declare const SUBAGENT_ERROR = EventType.SUBAGENT_ERROR;
|
|
9
|
+
export type SubagentOrder = 'parallel' | 'sequence';
|
|
10
|
+
export interface SubagentRouterPlan {
|
|
11
|
+
names: ReadonlyArray<string>;
|
|
12
|
+
/** Overrides `subagents.order` for this turn. */
|
|
13
|
+
order?: SubagentOrder;
|
|
14
|
+
}
|
|
15
|
+
export interface SubagentStep {
|
|
16
|
+
names: ReadonlyArray<string>;
|
|
17
|
+
/** Overrides `subagents.order` for this step. */
|
|
18
|
+
order?: SubagentOrder;
|
|
19
|
+
}
|
|
20
|
+
export interface SubagentStepsPlan {
|
|
21
|
+
steps: ReadonlyArray<SubagentStep>;
|
|
22
|
+
}
|
|
23
|
+
export type SubagentRouterPick = 'main' | string | ReadonlyArray<string> | SubagentRouterPlan | SubagentStepsPlan;
|
|
24
|
+
export interface SubagentsBag<TAgents extends ReadonlyArray<DefinedAgent> = ReadonlyArray<DefinedAgent>> {
|
|
25
|
+
agents: TAgents;
|
|
26
|
+
router?: (ctx: {
|
|
27
|
+
messages: SubagentRunContext['messages'];
|
|
28
|
+
agents: NoInfer<TAgents>;
|
|
29
|
+
abortSignal?: AbortSignal;
|
|
30
|
+
}) => SubagentRouterPick | Promise<SubagentRouterPick>;
|
|
31
|
+
strategy?: 'exclusive' | 'handoff';
|
|
32
|
+
/**
|
|
33
|
+
* How a router list runs. `parallel` starts every name together.
|
|
34
|
+
* `sequence` runs each name after the previous one finishes, and passes
|
|
35
|
+
* that child's text to the next child.
|
|
36
|
+
*/
|
|
37
|
+
order?: 'parallel' | 'sequence';
|
|
38
|
+
sandbox?: 'own' | 'inherit';
|
|
39
|
+
}
|
|
40
|
+
/** What the children of one parent run left behind for the parent terminal. */
|
|
41
|
+
export interface SubagentSink {
|
|
42
|
+
interrupts: Array<Interrupt>;
|
|
43
|
+
/** One AG-UI entry per child model call. */
|
|
44
|
+
usage: Array<SpecTokenUsage>;
|
|
45
|
+
/** Summed full usage of the children, including cost. */
|
|
46
|
+
total?: TokenUsage;
|
|
47
|
+
}
|
|
48
|
+
export declare function createSubagentSink(): SubagentSink;
|
|
49
|
+
/** One child to start, or a suspended child to continue. */
|
|
50
|
+
export interface SpawnEntry {
|
|
51
|
+
name: string;
|
|
52
|
+
resume?: {
|
|
53
|
+
subagentRunId: string;
|
|
54
|
+
/** The child's own messages from the interrupted run. */
|
|
55
|
+
messages: Array<UIMessage | ModelMessage>;
|
|
56
|
+
entries: Array<RunAgentResumeItem>;
|
|
57
|
+
/** Text the child wrote before it stopped. */
|
|
58
|
+
text: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
interface SpawnContext {
|
|
62
|
+
messages: SubagentRunContext['messages'];
|
|
63
|
+
abortSignal?: AbortSignal;
|
|
64
|
+
threadId: string;
|
|
65
|
+
/** The parent chat run. */
|
|
66
|
+
parentRunId: string;
|
|
67
|
+
/** The interrupted parent run, on a resume. */
|
|
68
|
+
interruptedRunId?: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function createSubagentId(): string;
|
|
71
|
+
/**
|
|
72
|
+
* Bind child interrupts to the parent run. The client resumes the parent run,
|
|
73
|
+
* so each binding must name that run. The resumed child then validates with
|
|
74
|
+
* the parent's interrupted run id.
|
|
75
|
+
*/
|
|
76
|
+
export declare function rebindInterrupts(interrupts: ReadonlyArray<Interrupt>, runId: string): Array<Interrupt>;
|
|
77
|
+
export declare function normalizeRouterPick(pick: SubagentRouterPick, agents: ReadonlyArray<DefinedAgent>): {
|
|
78
|
+
steps: ReadonlyArray<SubagentStep>;
|
|
79
|
+
};
|
|
80
|
+
/** Add a finished child run's usage to the sink. */
|
|
81
|
+
export declare function collectUsage(sink: SubagentSink, finished?: StreamChunk): void;
|
|
82
|
+
/** A parent run's last chunk: it completed, or it failed. */
|
|
83
|
+
type ParentTerminal = Extract<StreamChunk, {
|
|
84
|
+
type: 'RUN_FINISHED' | 'RUN_ERROR';
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Put the children's usage on a parent terminal. `usage[]` keeps one entry per
|
|
88
|
+
* model call. `metadata.tanstack.usage` holds the summed cost and the other
|
|
89
|
+
* TanStack fields, so `fromSpecTokenUsage` reads the full total. Empties the
|
|
90
|
+
* sink, so the next parent terminal does not count it again.
|
|
91
|
+
*
|
|
92
|
+
* `RUN_ERROR` is accepted too: a turn that failed still spent whatever its
|
|
93
|
+
* children spent. Such a chunk carries no usage of its own, so `runUsage` and
|
|
94
|
+
* `fullUsage` return empty for it and the children's total stands alone.
|
|
95
|
+
*/
|
|
96
|
+
export declare function withChildUsage(chunk: ParentTerminal, sink: SubagentSink): ParentTerminal;
|
|
97
|
+
export declare function spawnAgentStream(agent: DefinedAgent, ctx: SubagentRunContext, sink?: SubagentSink, parentToolCallId?: string): AsyncIterable<StreamChunk>;
|
|
98
|
+
export declare function spawnNamedAgents(entries: ReadonlyArray<SpawnEntry>, bag: SubagentsBag, ctx: SpawnContext, sink?: SubagentSink): AsyncGenerator<import('../../..').AGUIEvent, void, any>;
|
|
99
|
+
/**
|
|
100
|
+
* Text of the named direct children, in `names` order. Text from nested
|
|
101
|
+
* children stays out: their chunks carry their own id.
|
|
102
|
+
*/
|
|
103
|
+
export declare function collectNamedText(chunks: Array<StreamChunk>, names: ReadonlyArray<string>): string;
|
|
104
|
+
/**
|
|
105
|
+
* Record the parent messages when the model calls a subagent tool, so the
|
|
106
|
+
* child reads the conversation as it is at that call.
|
|
107
|
+
*/
|
|
108
|
+
export declare function subagentCallMessages(names: ReadonlySet<string>): {
|
|
109
|
+
middleware: ChatMiddleware<unknown, never>;
|
|
110
|
+
messagesFor: (toolCallId: string | undefined) => ModelMessage<string | import('../../..').ContentPart<unknown, unknown, unknown, unknown, unknown>[] | null>[] | undefined;
|
|
111
|
+
};
|
|
112
|
+
export declare function createSyntheticSubagentTools(bag: SubagentsBag, parent: {
|
|
113
|
+
/** Messages the parent run started with. Used when no call was recorded. */
|
|
114
|
+
messages: SubagentRunContext['messages'];
|
|
115
|
+
/** The parent messages at a tool call. See subagentCallMessages. */
|
|
116
|
+
messagesFor?: (toolCallId: string | undefined) => SubagentRunContext['messages'] | undefined;
|
|
117
|
+
threadId: string;
|
|
118
|
+
runId: string;
|
|
119
|
+
interruptedRunId?: string;
|
|
120
|
+
abortSignal?: AbortSignal;
|
|
121
|
+
turn?: SubagentTurn;
|
|
122
|
+
sink: SubagentSink;
|
|
123
|
+
}): Array<Tool>;
|
|
124
|
+
export {};
|