agentsdk-agent 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shetan Mehra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,290 @@
1
+ # @agentsdk/agent
2
+
3
+ > Multi-step agent runtime for the AgentSDK — model + system prompt + tools + a step loop, with **agent-as-tool handoff** as a first-class primitive.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
+
7
+ `@agentsdk/agent` turns an `AgentConfig` (model + system prompt + tools +
8
+ max steps) into a running agent. Each step the model may emit text or tool
9
+ calls; tool calls are executed and their results fed back, until the model
10
+ stops calling tools or `maxSteps` is reached.
11
+
12
+ The standout feature is **agent-as-tool handoff**: an agent can be wrapped
13
+ as a callable tool and handed to another agent. When the parent invokes the
14
+ tool, the child agent runs to completion and its final text is returned as
15
+ the tool result. This enables true multi-agent orchestration — a "router"
16
+ agent can delegate to specialised researcher / writer / critic agents as if
17
+ they were tools.
18
+
19
+ > Built on top of [`@agentsdk/core`](https://github.com/shetanmehra/agentsdk/tree/main/packages/core#readme).
20
+
21
+ ---
22
+
23
+ ## Table of contents
24
+
25
+ - [Install](#install)
26
+ - [Quick start](#quick-start)
27
+ - [AgentConfig](#agentconfig)
28
+ - [Running an agent](#running-an-agent)
29
+ - [Step events & hooks](#step-events--hooks)
30
+ - [Memory strategies](#memory-strategies)
31
+ - [Agent-as-tool handoff](#agent-as-tool-handoff)
32
+ - [Multi-agent orchestration](#multi-agent-orchestration)
33
+ - [Result shape](#result-shape)
34
+ - [License](#license)
35
+
36
+ ---
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ bun add @agentsdk/agent @agentsdk/core
42
+ # or
43
+ npm install @agentsdk/agent @agentsdk/core
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Quick start
49
+
50
+ ```ts
51
+ import '@agentsdk/core/bootstrap' // auto-register available providers
52
+ import { runAgent, type AgentConfig } from '@agentsdk/agent'
53
+
54
+ const agent: AgentConfig = {
55
+ id: 'writer',
56
+ name: 'Writer',
57
+ model: 'gpt-4o-mini', // or 'glm-4.6', 'claude-3-5-sonnet-20241022', ...
58
+ provider: 'openai', // optional — auto-detected if omitted
59
+ systemPrompt: 'You are a concise technical writer.',
60
+ maxSteps: 5,
61
+ temperature: 0.7,
62
+ }
63
+
64
+ const result = await runAgent(agent, {
65
+ messages: [{ role: 'user', content: 'Explain async/await in 2 sentences.' }],
66
+ onStepFinish: (event) => console.log(`[step ${event.step}] ${event.kind}`),
67
+ })
68
+
69
+ console.log(result.text)
70
+ console.log(result.steps.length) // number of steps executed
71
+ console.log(result.cost.total) // aggregated USD cost
72
+ ```
73
+
74
+ > **Note:** The example uses OpenAI (`gpt-4o-mini`) which works anywhere with
75
+ > `OPENAI_API_KEY` set. To use GLM, set `ZAI_API_KEY`. See [`@agentsdk/core`](https://github.com/shetanmehra/agentsdk/tree/main/packages/core#readme)
76
+ > for full provider configuration.
77
+
78
+ ---
79
+
80
+ ## AgentConfig
81
+
82
+ ```ts
83
+ interface AgentConfig {
84
+ id: string // stable id within a graph
85
+ name: string // display name
86
+ provider?: string // optional — auto-detected (prefers glm, then openai, then anthropic)
87
+ model: string // e.g. "glm-4.6"
88
+ systemPrompt: string
89
+ tools?: ResolvedTool[] // tools the agent may call
90
+ maxSteps?: number // default 10
91
+ temperature?: number
92
+ maxTokens?: number
93
+ thinking?: boolean // enable provider reasoning mode
94
+ memoryStrategy?: MemoryStrategy // default "full"
95
+ memoryWindow?: number // for "window" strategy, default 8
96
+ providerOptions?: Record<string, unknown>
97
+ }
98
+
99
+ type MemoryStrategy = 'none' | 'full' | 'summary' | 'window'
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Running an agent
105
+
106
+ ```ts
107
+ import { runAgent, type AgentRunOptions } from '@agentsdk/agent'
108
+
109
+ interface AgentRunOptions {
110
+ messages: Message[] // seed input
111
+ onStepFinish?: StepHook // called after each step
112
+ runId?: string // auto-generated if omitted
113
+ maxSteps?: number // override config.maxSteps for this run
114
+ }
115
+
116
+ const result = await runAgent(config, options)
117
+ ```
118
+
119
+ The step loop:
120
+
121
+ 1. Seed history with the agent's system prompt + the input messages.
122
+ 2. Apply the memory strategy to bound history size.
123
+ 3. Call the provider's `generateText`.
124
+ 4. If the model emitted tool calls, execute them, append tool results, go to 2.
125
+ 5. If the model returned pure text, that's the final answer — stop.
126
+ 6. If `maxSteps` is hit mid-tool-call, mark `truncated: true` and synthesise
127
+ a final summary by running one more non-tool generation.
128
+
129
+ ---
130
+
131
+ ## Step events & hooks
132
+
133
+ The `onStepFinish` hook fires after every step (text answer, tool call,
134
+ tool result, or error). Use it for live UI updates, logging, or credit
135
+ deduction per step.
136
+
137
+ ```ts
138
+ interface StepEvent {
139
+ step: number // 1-indexed
140
+ kind: 'text' | 'tool_call' | 'tool_result' | 'error'
141
+ text?: string // assistant text (text/tool_call)
142
+ toolCalls?: ToolCall[] // tool calls requested
143
+ toolResults?: ToolResult[] // tool results produced
144
+ error?: string // when kind === 'error'
145
+ usage: Usage
146
+ cost: Cost
147
+ timestamp: string // ISO
148
+ }
149
+
150
+ type StepHook = (event: StepEvent) => void | Promise<void>
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Memory strategies
156
+
157
+ Control how conversation history is carried between steps:
158
+
159
+ | Strategy | Behaviour |
160
+ | ---------- | -------------------------------------------------------------------------- |
161
+ | `none` | Keep only system prompt + last user message |
162
+ | `full` | Keep the entire history (default) |
163
+ | `summary` | Stub — currently behaves like `window`; a real summariser can be plugged in |
164
+ | `window` | Keep system prompt(s) + the last `memoryWindow` messages (default 8) |
165
+
166
+ ```ts
167
+ const agent: AgentConfig = {
168
+ // ...
169
+ memoryStrategy: 'window',
170
+ memoryWindow: 12,
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Agent-as-tool handoff
177
+
178
+ > The core differentiator vs Vercel AI SDK.
179
+
180
+ Wrap any agent as a callable tool, then hand it to a parent agent:
181
+
182
+ ```ts
183
+ import { asTool } from '@agentsdk/agent'
184
+
185
+ const researcher: AgentConfig = {
186
+ id: 'researcher',
187
+ name: 'Researcher',
188
+ model: 'glm-4.6',
189
+ systemPrompt: 'You research topics thoroughly. Always cite sources.',
190
+ maxSteps: 8,
191
+ }
192
+
193
+ const researcherTool = asTool(researcher, {
194
+ description: 'Hand off to the Researcher agent for in-depth research.',
195
+ inputKey: 'input', // arg name that becomes the user message
196
+ onChildStep: (event) => { // forwarded child step events
197
+ console.log(` [researcher step ${event.step}]`, event.kind)
198
+ },
199
+ })
200
+
201
+ const parent: AgentConfig = {
202
+ id: 'router',
203
+ name: 'Router',
204
+ model: 'glm-4.6',
205
+ systemPrompt: 'You are a router. Delegate research tasks to the researcher tool.',
206
+ tools: [researcherTool],
207
+ maxSteps: 6,
208
+ }
209
+ ```
210
+
211
+ When the parent calls `researcher({ input: '...' })`, the child agent runs to
212
+ completion and the parent receives:
213
+
214
+ ```ts
215
+ {
216
+ text: '<child final answer>',
217
+ agentId: 'researcher',
218
+ stepsExecuted: 3,
219
+ usage: { ... },
220
+ cost: { ... },
221
+ truncated: false,
222
+ }
223
+ ```
224
+
225
+ ### `HandoffOptions`
226
+
227
+ ```ts
228
+ interface HandoffOptions {
229
+ description?: string
230
+ parameters?: Record<string, unknown> // JSON Schema; defaults to a single `input` string
231
+ inputKey?: string // default "input"
232
+ seedMessages?: Message[] // extra prepended messages for the child
233
+ onChildStep?: StepHook // live child step events
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Multi-agent orchestration
240
+
241
+ The `withHandoffs` helper composes a parent's tool list from ordinary tools
242
+ plus a set of child agents:
243
+
244
+ ```ts
245
+ import { withHandoffs } from '@agentsdk/agent'
246
+
247
+ const parent: AgentConfig = {
248
+ id: 'orchestrator',
249
+ name: 'Orchestrator',
250
+ model: 'glm-4.6',
251
+ systemPrompt: 'Coordinate the researcher and writer agents.',
252
+ tools: withHandoffs(
253
+ [searchTool, calcTool], // ordinary tools
254
+ [
255
+ { agent: researcher, opts: { description: 'Research a topic' } },
256
+ { agent: writer, opts: { description: 'Write a draft' } },
257
+ ],
258
+ ),
259
+ maxSteps: 12,
260
+ }
261
+ ```
262
+
263
+ This pattern scales — one orchestrator can fan out to any number of
264
+ specialised child agents, each with their own model, prompt, and tool set.
265
+
266
+ ---
267
+
268
+ ## Result shape
269
+
270
+ ```ts
271
+ interface AgentRunResult {
272
+ agentId: string
273
+ text: string // final answer
274
+ messages: Message[] // full post-run history
275
+ steps: StepEvent[] // every step, in order
276
+ usage: Usage // aggregated across steps
277
+ cost: Cost // aggregated across steps
278
+ stepsExecuted: number
279
+ truncated: boolean // true if maxSteps hit mid-tool
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## License
286
+
287
+ MIT — see [LICENSE](./LICENSE). Copyright (c) 2026 Shetan Mehra.
288
+
289
+ This package is part of the [AgentSDK](https://github.com/shetanmehra/agentsdk)
290
+ monorepo.
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-55J6XMHW.js.map
3
+ //# sourceMappingURL=chunk-55J6XMHW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-55J6XMHW.js"}
@@ -0,0 +1,65 @@
1
+ import { AgentConfig, AgentRunOptions, AgentRunResult, MemoryStrategy } from './types.js';
2
+ export { StepEvent, StepHook, StepKind } from './types.js';
3
+ import * as agentsdk_core from 'agentsdk-core';
4
+ import { Message, ResolvedTool } from 'agentsdk-core';
5
+
6
+ /**
7
+ * AgentSDK Agent Runtime — Step loop
8
+ * MIT License — see LICENSE. Copyright (c) 2026 Shetan Mehra.
9
+ *
10
+ * The core agent execution loop:
11
+ * 1. Seed history with the agent's system prompt + the input messages.
12
+ * 2. Call the provider's generateText (with automatic retry on transient errors).
13
+ * 3. If the model emitted tool calls, execute them in PARALLEL and append
14
+ * tool results.
15
+ * 4. Otherwise, the agent is done — return the final text.
16
+ * 5. Apply the memory strategy before each step to bound history size.
17
+ * 6. Stop when maxSteps is hit or the model stops calling tools.
18
+ */
19
+
20
+ declare function runAgent(config: AgentConfig, options: AgentRunOptions): Promise<AgentRunResult>;
21
+
22
+ /**
23
+ * AgentSDK Agent Runtime — Memory strategies
24
+ * MIT License — see LICENSE. Copyright (c) 2026 Shetan Mehra.
25
+ *
26
+ * Reduces the message history between steps according to the agent's
27
+ * `memoryStrategy`. Each function returns the (possibly trimmed) history.
28
+ */
29
+
30
+ /** Apply the memory strategy to a message history. */
31
+ declare function applyMemory(messages: Message[], strategy: MemoryStrategy, windowSize?: number): Message[];
32
+
33
+ interface HandoffOptions {
34
+ /** Description the parent agent sees for this "tool". */
35
+ description?: string;
36
+ /** Input schema describing what the parent should pass. Defaults to a single `input` string. */
37
+ parameters?: Record<string, unknown>;
38
+ /** The argument key that becomes the user message to the child agent. Default: "input". */
39
+ inputKey?: string;
40
+ /** Extra seed messages to prepend for the child agent (beyond the input). */
41
+ seedMessages?: agentsdk_core.Message[];
42
+ /** Hook fired for every child step (for live UI updates). */
43
+ onChildStep?: AgentRunOptions['onStepFinish'];
44
+ /** Maximum handoff depth before recursion is rejected. Default: 5. */
45
+ maxDepth?: number;
46
+ }
47
+ /**
48
+ * Wrap an agent as a tool that another agent can call. The resulting tool can
49
+ * be appended to a parent agent's `tools` array.
50
+ *
51
+ * Includes a recursion guard: if the same child agent appears more than
52
+ * `maxDepth` times in the call chain (e.g. A→B→A→B→...), the tool throws
53
+ * instead of looping forever.
54
+ */
55
+ declare function asTool(child: AgentConfig, opts?: HandoffOptions): ResolvedTool;
56
+ /**
57
+ * Convenience: build a parent agent's tool list by mixing ordinary tools with
58
+ * a set of child agents (each converted to a tool via `asTool`).
59
+ */
60
+ declare function withHandoffs(baseTools: ResolvedTool[], children: Array<{
61
+ agent: AgentConfig;
62
+ opts?: HandoffOptions;
63
+ }>): ResolvedTool[];
64
+
65
+ export { AgentConfig, AgentRunOptions, AgentRunResult, type HandoffOptions, MemoryStrategy, applyMemory, asTool, runAgent, withHandoffs };
package/dist/index.js ADDED
@@ -0,0 +1,283 @@
1
+ import './chunk-55J6XMHW.js';
2
+ import { addUsage, addCost, normalizeError, generateText, AgentSDKError } from 'agentsdk-core';
3
+
4
+ // src/memory.ts
5
+ function applyMemory(messages, strategy, windowSize = 8) {
6
+ switch (strategy) {
7
+ case "none":
8
+ return keepHeadAndLastUser(messages);
9
+ case "full":
10
+ return messages;
11
+ case "summary":
12
+ warnSummaryDeprecated();
13
+ return applyWindow(messages, windowSize);
14
+ case "window":
15
+ default:
16
+ return applyWindow(messages, windowSize);
17
+ }
18
+ }
19
+ function applyWindow(messages, windowSize) {
20
+ const system = [];
21
+ const rest = [];
22
+ for (const m of messages) {
23
+ (m.role === "system" ? system : rest).push(m);
24
+ }
25
+ const tail = rest.slice(-Math.max(1, windowSize));
26
+ return [...system, ...tail];
27
+ }
28
+ function keepHeadAndLastUser(messages) {
29
+ const system = messages.filter((m) => m.role === "system");
30
+ const lastUser = [...messages].reverse().find((m) => m.role === "user");
31
+ return [...system, ...lastUser ? [lastUser] : []];
32
+ }
33
+ var summaryWarned = false;
34
+ function warnSummaryDeprecated() {
35
+ if (summaryWarned) return;
36
+ summaryWarned = true;
37
+ console.warn(
38
+ '[agentsdk:agent] memoryStrategy "summary" is not yet implemented; falling back to "window" behaviour. Use "window" or "full" to silence this warning.'
39
+ );
40
+ }
41
+
42
+ // src/runtime.ts
43
+ var DEFAULT_MAX_STEPS = 10;
44
+ var DEFAULT_MAX_RETRIES = 2;
45
+ var RETRYABLE_TYPES = /* @__PURE__ */ new Set(["rate_limit", "server", "timeout"]);
46
+ var RETRY_BASE_DELAY_MS = 800;
47
+ async function runAgent(config, options) {
48
+ const maxSteps = options.maxSteps ?? config.maxSteps ?? DEFAULT_MAX_STEPS;
49
+ const runId = options.runId ?? makeRunId();
50
+ const onStep = options.onStepFinish;
51
+ let history = [
52
+ { role: "system", content: config.systemPrompt },
53
+ ...options.messages
54
+ ];
55
+ const steps = [];
56
+ let totalUsage = {
57
+ promptTokens: 0,
58
+ completionTokens: 0,
59
+ totalTokens: 0
60
+ };
61
+ let totalCost = { input: 0, output: 0, total: 0, currency: "USD" };
62
+ let finalText = "";
63
+ let truncated = false;
64
+ for (let step = 1; step <= maxSteps; step++) {
65
+ const memStrategy = config.memoryStrategy ?? "full";
66
+ const windowSize = config.memoryWindow ?? 8;
67
+ const visible = applyMemory(history, memStrategy, windowSize);
68
+ let event;
69
+ try {
70
+ const result = await generateTextWithRetry(
71
+ {
72
+ model: config.model,
73
+ messages: visible,
74
+ tools: config.tools,
75
+ temperature: config.temperature,
76
+ maxTokens: config.maxTokens,
77
+ thinking: config.thinking,
78
+ providerOptions: config.providerOptions
79
+ },
80
+ { provider: config.provider }
81
+ );
82
+ totalUsage = addUsage(totalUsage, result.usage);
83
+ totalCost = addCost(totalCost, result.cost);
84
+ const assistantMsg = {
85
+ role: "assistant",
86
+ content: result.text,
87
+ toolCalls: result.toolCalls.length ? result.toolCalls : void 0
88
+ };
89
+ history.push(assistantMsg);
90
+ if (result.toolCalls.length === 0) {
91
+ finalText = result.text;
92
+ event = {
93
+ step,
94
+ kind: "text",
95
+ text: result.text,
96
+ usage: result.usage,
97
+ cost: result.cost,
98
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
99
+ };
100
+ steps.push(event);
101
+ await onStep?.(event);
102
+ break;
103
+ }
104
+ const toolResults = await Promise.all(
105
+ result.toolCalls.map((tc) => executeToolCall(tc, config, step, runId))
106
+ );
107
+ for (const toolResult of toolResults) {
108
+ history.push({
109
+ role: "tool",
110
+ content: stringifyToolResult(toolResult.result),
111
+ name: toolResult.name,
112
+ toolCallId: toolResult.toolCallId
113
+ });
114
+ }
115
+ event = {
116
+ step,
117
+ kind: "tool_call",
118
+ text: result.text,
119
+ toolCalls: result.toolCalls,
120
+ toolResults,
121
+ usage: result.usage,
122
+ cost: result.cost,
123
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
124
+ };
125
+ steps.push(event);
126
+ await onStep?.(event);
127
+ if (step === maxSteps) truncated = true;
128
+ } catch (err) {
129
+ const norm = normalizeError(err, config.provider);
130
+ event = {
131
+ step,
132
+ kind: "error",
133
+ error: norm.message,
134
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
135
+ };
136
+ steps.push(event);
137
+ await onStep?.(event);
138
+ throw norm;
139
+ }
140
+ }
141
+ if (!finalText && steps.length > 0) {
142
+ const last = await generateTextWithRetry(
143
+ {
144
+ model: config.model,
145
+ messages: [
146
+ ...applyMemory(history, config.memoryStrategy ?? "full", config.memoryWindow ?? 8),
147
+ {
148
+ role: "system",
149
+ content: "Summarise the result of the previous tool calls in a concise final answer for the user."
150
+ }
151
+ ],
152
+ temperature: config.temperature,
153
+ maxTokens: config.maxTokens,
154
+ providerOptions: config.providerOptions
155
+ },
156
+ { provider: config.provider }
157
+ );
158
+ finalText = last.text;
159
+ totalUsage = addUsage(totalUsage, last.usage);
160
+ totalCost = addCost(totalCost, last.cost);
161
+ }
162
+ return {
163
+ agentId: config.id,
164
+ text: finalText,
165
+ messages: history,
166
+ steps,
167
+ usage: totalUsage,
168
+ cost: totalCost,
169
+ stepsExecuted: steps.length,
170
+ truncated
171
+ };
172
+ }
173
+ async function generateTextWithRetry(params, opts) {
174
+ const maxRetries = DEFAULT_MAX_RETRIES;
175
+ let lastError;
176
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
177
+ try {
178
+ return await generateText(params, opts);
179
+ } catch (err) {
180
+ lastError = err;
181
+ const providerId = opts?.provider;
182
+ const norm = err instanceof AgentSDKError ? err : normalizeError(err, providerId);
183
+ if (attempt === maxRetries || !RETRYABLE_TYPES.has(norm.type)) {
184
+ throw norm;
185
+ }
186
+ const baseDelay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
187
+ const jitter = baseDelay * (0.8 + Math.random() * 0.4);
188
+ await new Promise((resolve) => setTimeout(resolve, jitter));
189
+ }
190
+ }
191
+ throw lastError;
192
+ }
193
+ async function executeToolCall(call, config, step, runId) {
194
+ const tool = config.tools?.find((t) => t.name === call.name);
195
+ if (!tool || typeof tool.execute !== "function") {
196
+ return {
197
+ toolCallId: call.id,
198
+ name: call.name,
199
+ result: { error: `Tool "${call.name}" is not defined or has no executor.` },
200
+ isError: true
201
+ };
202
+ }
203
+ try {
204
+ const out = await tool.execute(call.arguments, { runId, step });
205
+ return { toolCallId: call.id, name: call.name, result: out };
206
+ } catch (err) {
207
+ const msg = err instanceof Error ? err.message : String(err);
208
+ return {
209
+ toolCallId: call.id,
210
+ name: call.name,
211
+ result: { error: msg },
212
+ isError: true
213
+ };
214
+ }
215
+ }
216
+ function stringifyToolResult(result) {
217
+ if (typeof result === "string") return result;
218
+ try {
219
+ return JSON.stringify(result);
220
+ } catch {
221
+ return String(result);
222
+ }
223
+ }
224
+ function makeRunId() {
225
+ return `run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
226
+ }
227
+ function asTool(child, opts = {}) {
228
+ const inputKey = opts.inputKey ?? "input";
229
+ const maxDepth = opts.maxDepth ?? 5;
230
+ return {
231
+ name: child.id,
232
+ description: opts.description ?? `Hand off to the "${child.name}" agent. Pass a natural-language ${inputKey} describing what the child agent should do.`,
233
+ parameters: opts.parameters ?? {
234
+ type: "object",
235
+ properties: {
236
+ [inputKey]: {
237
+ type: "string",
238
+ description: "The task or question to delegate to this agent."
239
+ }
240
+ },
241
+ required: [inputKey],
242
+ additionalProperties: false
243
+ },
244
+ async execute(args, ctx) {
245
+ const chain = ctx.runId.split(":");
246
+ const occurrences = chain.filter((x) => x === child.id).length;
247
+ if (occurrences > maxDepth) {
248
+ throw new AgentSDKError(
249
+ `Handoff recursion limit exceeded: agent "${child.id}" appears ${occurrences} times in the call chain (max ${maxDepth}). This usually means two agents are handing off to each other in a loop. Check your agent tool lists for cycles.`,
250
+ { type: "invalid_request" }
251
+ );
252
+ }
253
+ const input = String(args?.[inputKey] ?? "");
254
+ const messages = [
255
+ ...opts.seedMessages ?? [],
256
+ { role: "user", content: input }
257
+ ];
258
+ const result = await runAgent(child, {
259
+ messages,
260
+ runId: `${ctx.runId}:${child.id}`,
261
+ onStepFinish: opts.onChildStep
262
+ });
263
+ return {
264
+ text: result.text,
265
+ agentId: result.agentId,
266
+ stepsExecuted: result.stepsExecuted,
267
+ usage: result.usage,
268
+ cost: result.cost,
269
+ truncated: result.truncated
270
+ };
271
+ }
272
+ };
273
+ }
274
+ function withHandoffs(baseTools, children) {
275
+ return [
276
+ ...baseTools,
277
+ ...children.map(({ agent, opts }) => asTool(agent, opts))
278
+ ];
279
+ }
280
+
281
+ export { applyMemory, asTool, runAgent, withHandoffs };
282
+ //# sourceMappingURL=index.js.map
283
+ //# sourceMappingURL=index.js.map