@prestyj/agent 4.2.15

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 Nolan Grout
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,96 @@
1
+ # @prestyj/agent
2
+
3
+ <p align="center">
4
+ <strong>Agent loop with multi-turn tool execution. Build agents that think, act, and loop.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@prestyj/agent"><img src="https://img.shields.io/npm/v/@prestyj/agent?style=for-the-badge" alt="npm version"></a>
9
+ <a href="../../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
10
+ </p>
11
+
12
+ Give an LLM tools. It calls them. Results go back in. It loops until it's done. That's it.
13
+
14
+ Built on top of [`@prestyj/ai`](../ai/README.md). Part of the [EZCoder](../../README.md) monorepo.
15
+
16
+ ---
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm i @prestyj/agent
22
+ ```
23
+
24
+ ---
25
+
26
+ ## How it works
27
+
28
+ Create an `Agent` with a provider, model, and tools. Call `agent.prompt()` to start a conversation.
29
+
30
+ - **`for await`** gives you streaming events (`text_delta`, `tool_call_start`, `tool_call_end`, `agent_done`, etc.)
31
+ - **`await`** gives you the final result (`message`, `totalTurns`, `totalUsage`)
32
+
33
+ Same dual-nature pattern as `@prestyj/ai`. The `Agent` class maintains conversation history — each `prompt()` call continues the conversation.
34
+
35
+ For full control, use `agentLoop()` directly — a pure async generator that takes a messages array and options.
36
+
37
+ ### Tools
38
+
39
+ Define tools with a name, description, Zod schema for parameters, and an `execute` function. The execute function receives typed args and a `ToolContext` with `signal`, `toolCallId`, and `onUpdate`.
40
+
41
+ Return a string, or a `{ content, details }` object for structured results. If `execute` throws, the error becomes a tool result (not a crash). The agent sees the error and can retry or adjust.
42
+
43
+ ### Safety
44
+
45
+ - `maxTurns` (default: 100) prevents runaway loops
46
+ - `AbortSignal` support for cancellation
47
+ - Zod validation on tool args
48
+ - `maxContinuations` (default: 5) caps consecutive `pause_turn` continuations
49
+
50
+ ---
51
+
52
+ ## Events
53
+
54
+ | Event | Description |
55
+ |---|---|
56
+ | `text_delta` | Incremental text output |
57
+ | `thinking_delta` | Extended thinking output |
58
+ | `tool_call_start` | Tool invocation started (name, args) |
59
+ | `tool_call_update` | Progress update from a running tool |
60
+ | `tool_call_end` | Tool finished (result, duration, isError) |
61
+ | `server_tool_call` | Server-side tool invocation |
62
+ | `server_tool_result` | Server-side tool result |
63
+ | `turn_end` | One LLM call completed (stop reason, usage) |
64
+ | `agent_done` | All turns finished (total turns, total usage) |
65
+ | `error` | Fatal error |
66
+
67
+ ---
68
+
69
+ ## Options
70
+
71
+ | Option | Type | Description |
72
+ |---|---|---|
73
+ | `provider` | `"anthropic" \| "openai" \| "glm" \| "moonshot"` | Required |
74
+ | `model` | `string` | Required |
75
+ | `system` | `string` | System prompt |
76
+ | `tools` | `AgentTool[]` | Tools with Zod schemas and execute functions |
77
+ | `serverTools` | `ServerToolDefinition[]` | Server-side tool definitions |
78
+ | `maxTurns` | `number` | Max LLM calls (default: 100) |
79
+ | `maxTokens` | `number` | Max output tokens per turn |
80
+ | `temperature` | `number` | Sampling temperature |
81
+ | `thinking` | `"low" \| "medium" \| "high" \| "max"` | Extended thinking |
82
+ | `apiKey` | `string` | Provider API key |
83
+ | `baseUrl` | `string` | Custom endpoint |
84
+ | `signal` | `AbortSignal` | Cancellation |
85
+ | `cacheRetention` | `"none" \| "short" \| "long"` | Prompt cache preference |
86
+ | `compaction` | `boolean` | Server-side compaction (Anthropic only) |
87
+ | `maxContinuations` | `number` | Max pause_turn continuations (default: 5) |
88
+ | `transformContext` | `(messages) => messages` | Transform messages before each LLM call |
89
+
90
+ `transformContext` is called before each LLM call. Use it for compaction, truncation, or injecting dynamic context.
91
+
92
+ ---
93
+
94
+ ## License
95
+
96
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,376 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Agent: () => Agent,
24
+ AgentStream: () => AgentStream,
25
+ agentLoop: () => agentLoop,
26
+ isContextOverflow: () => isContextOverflow
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/agent.ts
31
+ var import_ai2 = require("@prestyj/ai");
32
+
33
+ // src/agent-loop.ts
34
+ var import_ai = require("@prestyj/ai");
35
+ var DEFAULT_MAX_TURNS = 100;
36
+ function isContextOverflow(err) {
37
+ if (!(err instanceof Error)) return false;
38
+ const msg = err.message.toLowerCase();
39
+ return msg.includes("prompt is too long") || msg.includes("context_length_exceeded") || msg.includes("maximum context length") || msg.includes("token") && msg.includes("exceed");
40
+ }
41
+ function isOverloaded(err) {
42
+ if (!(err instanceof Error)) return false;
43
+ const msg = err.message.toLowerCase();
44
+ if (msg.includes("insufficient balance") || msg.includes("quota exceeded") || msg.includes("billing")) {
45
+ return false;
46
+ }
47
+ return msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("529");
48
+ }
49
+ async function* agentLoop(messages, options) {
50
+ const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
51
+ const maxContinuations = options.maxContinuations ?? 5;
52
+ const toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
53
+ const totalUsage = { inputTokens: 0, outputTokens: 0 };
54
+ let turn = 0;
55
+ let consecutivePauses = 0;
56
+ let overflowRetries = 0;
57
+ let overloadRetries = 0;
58
+ const MAX_OVERFLOW_RETRIES = 3;
59
+ const MAX_OVERLOAD_RETRIES = 3;
60
+ const OVERLOAD_RETRY_DELAY_MS = 3e3;
61
+ while (turn < maxTurns) {
62
+ options.signal?.throwIfAborted();
63
+ turn++;
64
+ if (options.transformContext) {
65
+ const transformed = await options.transformContext(messages);
66
+ if (transformed !== messages) {
67
+ messages.length = 0;
68
+ messages.push(...transformed);
69
+ }
70
+ }
71
+ let response;
72
+ try {
73
+ const result = (0, import_ai.stream)({
74
+ provider: options.provider,
75
+ model: options.model,
76
+ messages,
77
+ tools: options.tools,
78
+ serverTools: options.serverTools,
79
+ webSearch: options.webSearch,
80
+ maxTokens: options.maxTokens,
81
+ temperature: options.temperature,
82
+ thinking: options.thinking,
83
+ apiKey: options.apiKey,
84
+ baseUrl: options.baseUrl,
85
+ signal: options.signal,
86
+ accountId: options.accountId,
87
+ cacheRetention: options.cacheRetention,
88
+ compaction: options.compaction
89
+ });
90
+ result.response.catch(() => {
91
+ });
92
+ for await (const event of result) {
93
+ if (event.type === "text_delta") {
94
+ yield { type: "text_delta", text: event.text };
95
+ } else if (event.type === "thinking_delta") {
96
+ yield { type: "thinking_delta", text: event.text };
97
+ } else if (event.type === "server_toolcall") {
98
+ yield {
99
+ type: "server_tool_call",
100
+ id: event.id,
101
+ name: event.name,
102
+ input: event.input
103
+ };
104
+ } else if (event.type === "server_toolresult") {
105
+ yield {
106
+ type: "server_tool_result",
107
+ toolUseId: event.toolUseId,
108
+ resultType: event.resultType,
109
+ data: event.data
110
+ };
111
+ }
112
+ }
113
+ response = await result.response;
114
+ } catch (err) {
115
+ if (overflowRetries < MAX_OVERFLOW_RETRIES && isContextOverflow(err) && options.transformContext) {
116
+ overflowRetries++;
117
+ const transformed = await options.transformContext(messages, { force: true });
118
+ if (transformed !== messages) {
119
+ messages.length = 0;
120
+ messages.push(...transformed);
121
+ }
122
+ turn--;
123
+ continue;
124
+ }
125
+ if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {
126
+ overloadRetries++;
127
+ await new Promise((r) => setTimeout(r, OVERLOAD_RETRY_DELAY_MS));
128
+ turn--;
129
+ continue;
130
+ }
131
+ throw err;
132
+ }
133
+ overflowRetries = 0;
134
+ overloadRetries = 0;
135
+ totalUsage.inputTokens += response.usage.inputTokens;
136
+ totalUsage.outputTokens += response.usage.outputTokens;
137
+ if (response.usage.cacheRead) {
138
+ totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;
139
+ }
140
+ if (response.usage.cacheWrite) {
141
+ totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
142
+ }
143
+ messages.push(response.message);
144
+ yield {
145
+ type: "turn_end",
146
+ turn,
147
+ stopReason: response.stopReason,
148
+ usage: response.usage
149
+ };
150
+ if (response.stopReason === "pause_turn") {
151
+ consecutivePauses++;
152
+ if (consecutivePauses >= maxContinuations) {
153
+ break;
154
+ }
155
+ continue;
156
+ }
157
+ consecutivePauses = 0;
158
+ if (response.stopReason !== "tool_use") {
159
+ yield {
160
+ type: "agent_done",
161
+ totalTurns: turn,
162
+ totalUsage: { ...totalUsage }
163
+ };
164
+ return {
165
+ message: response.message,
166
+ totalTurns: turn,
167
+ totalUsage: { ...totalUsage }
168
+ };
169
+ }
170
+ const allToolCalls = extractToolCalls(response.message.content);
171
+ const toolCalls = [];
172
+ const toolResults = [];
173
+ for (const tc of allToolCalls) {
174
+ if (tc.name.startsWith("$")) {
175
+ toolResults.push({
176
+ type: "tool_result",
177
+ toolCallId: tc.id,
178
+ content: JSON.stringify(tc.args)
179
+ });
180
+ } else {
181
+ toolCalls.push(tc);
182
+ }
183
+ }
184
+ const eventStream = new import_ai.EventStream();
185
+ const executions = toolCalls.map(async (toolCall) => {
186
+ const startTime = Date.now();
187
+ eventStream.push({
188
+ type: "tool_call_start",
189
+ toolCallId: toolCall.id,
190
+ name: toolCall.name,
191
+ args: toolCall.args
192
+ });
193
+ let resultContent;
194
+ let details;
195
+ let isError = false;
196
+ const tool = toolMap.get(toolCall.name);
197
+ if (!tool) {
198
+ resultContent = `Unknown tool: ${toolCall.name}`;
199
+ isError = true;
200
+ } else {
201
+ try {
202
+ const parsed = tool.parameters.parse(toolCall.args);
203
+ const ctx = {
204
+ signal: options.signal ?? AbortSignal.timeout(3e5),
205
+ toolCallId: toolCall.id,
206
+ onUpdate: (update) => {
207
+ eventStream.push({
208
+ type: "tool_call_update",
209
+ toolCallId: toolCall.id,
210
+ update
211
+ });
212
+ }
213
+ };
214
+ const raw = await tool.execute(parsed, ctx);
215
+ const normalized = normalizeToolResult(raw);
216
+ resultContent = normalized.content;
217
+ details = normalized.details;
218
+ } catch (err) {
219
+ isError = true;
220
+ resultContent = err instanceof Error ? err.message : String(err);
221
+ }
222
+ }
223
+ const durationMs = Date.now() - startTime;
224
+ eventStream.push({
225
+ type: "tool_call_end",
226
+ toolCallId: toolCall.id,
227
+ result: resultContent,
228
+ details,
229
+ isError,
230
+ durationMs
231
+ });
232
+ return { toolCallId: toolCall.id, content: resultContent, isError };
233
+ });
234
+ const abortHandler = () => eventStream.abort(new Error("aborted"));
235
+ options.signal?.addEventListener("abort", abortHandler, { once: true });
236
+ let toolResultsFinalized = false;
237
+ Promise.all(executions).then((results) => {
238
+ if (toolResultsFinalized) return;
239
+ for (const tc of toolCalls) {
240
+ const r = results.find((x) => x.toolCallId === tc.id);
241
+ toolResults.push({
242
+ type: "tool_result",
243
+ toolCallId: tc.id,
244
+ content: r.content,
245
+ isError: r.isError || void 0
246
+ });
247
+ }
248
+ eventStream.close();
249
+ }).catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));
250
+ try {
251
+ for await (const event of eventStream) {
252
+ yield event;
253
+ }
254
+ } finally {
255
+ options.signal?.removeEventListener("abort", abortHandler);
256
+ toolResultsFinalized = true;
257
+ for (const tc of toolCalls) {
258
+ if (!toolResults.some((r) => r.toolCallId === tc.id)) {
259
+ toolResults.push({
260
+ type: "tool_result",
261
+ toolCallId: tc.id,
262
+ content: "Tool execution was aborted.",
263
+ isError: true
264
+ });
265
+ }
266
+ }
267
+ messages.push({ role: "tool", content: toolResults });
268
+ }
269
+ }
270
+ let lastAssistant;
271
+ for (let i = messages.length - 1; i >= 0; i--) {
272
+ if (messages[i].role === "assistant") {
273
+ lastAssistant = messages[i];
274
+ break;
275
+ }
276
+ }
277
+ yield {
278
+ type: "agent_done",
279
+ totalTurns: turn,
280
+ totalUsage: { ...totalUsage }
281
+ };
282
+ return {
283
+ message: lastAssistant ?? { role: "assistant", content: [] },
284
+ totalTurns: turn,
285
+ totalUsage: { ...totalUsage }
286
+ };
287
+ }
288
+ function normalizeToolResult(raw) {
289
+ return typeof raw === "string" ? { content: raw } : raw;
290
+ }
291
+ function extractToolCalls(content) {
292
+ if (typeof content === "string") return [];
293
+ return content.filter((part) => part.type === "tool_call");
294
+ }
295
+
296
+ // src/agent.ts
297
+ var AgentStream = class {
298
+ events;
299
+ resultPromise;
300
+ resolveResult;
301
+ rejectResult;
302
+ hasConsumer = false;
303
+ constructor(generator, onDone) {
304
+ this.events = new import_ai2.EventStream();
305
+ this.resultPromise = new Promise((resolve, reject) => {
306
+ this.resolveResult = resolve;
307
+ this.rejectResult = reject;
308
+ });
309
+ this.pump(generator, onDone);
310
+ }
311
+ async pump(generator, onDone) {
312
+ try {
313
+ let next = await generator.next();
314
+ while (!next.done) {
315
+ this.events.push(next.value);
316
+ next = await generator.next();
317
+ }
318
+ this.events.close();
319
+ this.resolveResult(next.value);
320
+ } catch (err) {
321
+ const error = err instanceof Error ? err : new Error(String(err));
322
+ this.events.abort(error);
323
+ this.rejectResult(error);
324
+ } finally {
325
+ onDone();
326
+ }
327
+ }
328
+ [Symbol.asyncIterator]() {
329
+ this.hasConsumer = true;
330
+ return this.events[Symbol.asyncIterator]();
331
+ }
332
+ then(onfulfilled, onrejected) {
333
+ this.drainEvents().catch(() => {
334
+ });
335
+ return this.resultPromise.then(onfulfilled, onrejected);
336
+ }
337
+ async drainEvents() {
338
+ if (this.hasConsumer) return;
339
+ this.hasConsumer = true;
340
+ for await (const _ of this.events) {
341
+ }
342
+ }
343
+ };
344
+ var Agent = class {
345
+ messages = [];
346
+ _running = false;
347
+ options;
348
+ constructor(options) {
349
+ this.options = options;
350
+ if (options.system) {
351
+ this.messages.push({ role: "system", content: options.system });
352
+ }
353
+ }
354
+ get running() {
355
+ return this._running;
356
+ }
357
+ prompt(content) {
358
+ if (this._running) {
359
+ throw new Error("Agent is already running");
360
+ }
361
+ this._running = true;
362
+ this.messages.push({ role: "user", content });
363
+ const generator = agentLoop(this.messages, this.options);
364
+ return new AgentStream(generator, () => {
365
+ this._running = false;
366
+ });
367
+ }
368
+ };
369
+ // Annotate the CommonJS export names for ESM import in node:
370
+ 0 && (module.exports = {
371
+ Agent,
372
+ AgentStream,
373
+ agentLoop,
374
+ isContextOverflow
375
+ });
376
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["// Core\nexport { Agent, AgentStream } from \"./agent.js\";\nexport { agentLoop, isContextOverflow } from \"./agent-loop.js\";\n\n// Types\nexport type {\n StructuredToolResult,\n ToolExecuteResult,\n ToolContext,\n AgentTool,\n AgentTextDeltaEvent,\n AgentThinkingDeltaEvent,\n AgentToolCallStartEvent,\n AgentToolCallUpdateEvent,\n AgentToolCallEndEvent,\n AgentServerToolCallEvent,\n AgentServerToolResultEvent,\n AgentTurnEndEvent,\n AgentDoneEvent,\n AgentErrorEvent,\n AgentEvent,\n AgentOptions,\n AgentResult,\n} from \"./types.js\";\n","import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const generator = agentLoop(this.messages, this.options);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 100;\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n */\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n // Billing/balance errors often come as 429 but should not be retried\n if (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\")\n ) {\n return false;\n }\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let consecutivePauses = 0;\n let overflowRetries = 0;\n let overloadRetries = 0;\n const MAX_OVERFLOW_RETRIES = 3;\n const MAX_OVERLOAD_RETRIES = 3;\n const OVERLOAD_RETRY_DELAY_MS = 3_000;\n\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n }\n\n // ── Call LLM with overflow recovery ──\n let response;\n try {\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: options.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n compaction: options.compaction,\n });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas\n for await (const event of result) {\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n }\n }\n\n response = await result.response;\n } catch (err) {\n // Context overflow: force-compact via transformContext and retry (up to 3 times)\n if (\n overflowRetries < MAX_OVERFLOW_RETRIES &&\n isContextOverflow(err) &&\n options.transformContext\n ) {\n overflowRetries++;\n const transformed = await options.transformContext(messages, { force: true });\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n turn--; // Don't count the failed turn\n continue;\n }\n // Overloaded / rate-limited: wait 3s and retry (up to 3 times)\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n await new Promise((r) => setTimeout(r, OVERLOAD_RETRY_DELAY_MS));\n turn--; // Don't count the failed turn\n continue;\n }\n throw err;\n }\n\n // Reset retry counters after successful call\n overflowRetries = 0;\n overloadRetries = 0;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // If not tool_use, we're done\n if (response.stopReason !== \"tool_use\") {\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: string;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: resultContent,\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n for (const tc of toolCalls) {\n const r = results.find((x) => x.toolCallId === tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n for (const tc of toolCalls) {\n if (!toolResults.some((r) => r.toolCallId === tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n messages.push({ role: \"tool\", content: toolResults });\n }\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n"],"mappings":"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","names":["import_ai"]}
@@ -0,0 +1,154 @@
1
+ import { z } from 'zod';
2
+ import { StreamOptions, Tool, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
3
+
4
+ interface StructuredToolResult {
5
+ content: string;
6
+ details?: unknown;
7
+ }
8
+ type ToolExecuteResult = string | StructuredToolResult;
9
+ interface ToolContext {
10
+ signal: AbortSignal;
11
+ toolCallId: string;
12
+ onUpdate?: (update: unknown) => void;
13
+ }
14
+ interface AgentTool<T extends z.ZodType = z.ZodType> extends Tool {
15
+ parameters: T;
16
+ execute: (args: z.infer<T>, context: ToolContext) => ToolExecuteResult | Promise<ToolExecuteResult>;
17
+ }
18
+ interface AgentTextDeltaEvent {
19
+ type: "text_delta";
20
+ text: string;
21
+ }
22
+ interface AgentThinkingDeltaEvent {
23
+ type: "thinking_delta";
24
+ text: string;
25
+ }
26
+ interface AgentToolCallStartEvent {
27
+ type: "tool_call_start";
28
+ toolCallId: string;
29
+ name: string;
30
+ args: Record<string, unknown>;
31
+ }
32
+ interface AgentToolCallUpdateEvent {
33
+ type: "tool_call_update";
34
+ toolCallId: string;
35
+ update: unknown;
36
+ }
37
+ interface AgentToolCallEndEvent {
38
+ type: "tool_call_end";
39
+ toolCallId: string;
40
+ result: string;
41
+ details?: unknown;
42
+ isError: boolean;
43
+ durationMs: number;
44
+ }
45
+ interface AgentTurnEndEvent {
46
+ type: "turn_end";
47
+ turn: number;
48
+ stopReason: StopReason;
49
+ usage: Usage;
50
+ }
51
+ interface AgentDoneEvent {
52
+ type: "agent_done";
53
+ totalTurns: number;
54
+ totalUsage: Usage;
55
+ }
56
+ interface AgentErrorEvent {
57
+ type: "error";
58
+ error: Error;
59
+ }
60
+ interface AgentServerToolCallEvent {
61
+ type: "server_tool_call";
62
+ id: string;
63
+ name: string;
64
+ input: unknown;
65
+ }
66
+ interface AgentServerToolResultEvent {
67
+ type: "server_tool_result";
68
+ toolUseId: string;
69
+ resultType: string;
70
+ data: unknown;
71
+ }
72
+ type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentTurnEndEvent | AgentDoneEvent | AgentErrorEvent;
73
+ interface AgentOptions {
74
+ provider: StreamOptions["provider"];
75
+ model: string;
76
+ system?: string;
77
+ tools?: AgentTool[];
78
+ serverTools?: ServerToolDefinition[];
79
+ maxTurns?: number;
80
+ maxTokens?: number;
81
+ temperature?: number;
82
+ thinking?: StreamOptions["thinking"];
83
+ apiKey?: string;
84
+ baseUrl?: string;
85
+ signal?: AbortSignal;
86
+ accountId?: string;
87
+ cacheRetention?: StreamOptions["cacheRetention"];
88
+ /** Enable provider-native web search. */
89
+ webSearch?: boolean;
90
+ /** Enable server-side compaction (Anthropic only, beta). */
91
+ compaction?: boolean;
92
+ /** Max consecutive pause_turn continuations before stopping (default: 5).
93
+ * Prevents infinite loops when server-side tools keep pausing. */
94
+ maxContinuations?: number;
95
+ /**
96
+ * Called before each LLM call. Allows the caller to inspect and transform
97
+ * the messages array (e.g. compaction, truncation). Return the same array
98
+ * for no-op, or a new array to replace the conversation context.
99
+ *
100
+ * When `options.force` is true, the caller should compact unconditionally
101
+ * (e.g. after a context overflow error from the API).
102
+ */
103
+ transformContext?: (messages: Message[], options?: {
104
+ force?: boolean;
105
+ }) => Message[] | Promise<Message[]>;
106
+ }
107
+ interface AgentResult {
108
+ message: AssistantMessage;
109
+ totalTurns: number;
110
+ totalUsage: Usage;
111
+ }
112
+
113
+ /**
114
+ * Dual-nature result: async iterable for streaming events,
115
+ * thenable for awaiting the final AgentResult.
116
+ *
117
+ * ```ts
118
+ * // Stream events
119
+ * for await (const event of agent.prompt("hello")) { ... }
120
+ *
121
+ * // Or just await the result
122
+ * const result = await agent.prompt("hello");
123
+ * ```
124
+ */
125
+ declare class AgentStream implements AsyncIterable<AgentEvent> {
126
+ private events;
127
+ private resultPromise;
128
+ private resolveResult;
129
+ private rejectResult;
130
+ private hasConsumer;
131
+ constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void);
132
+ private pump;
133
+ [Symbol.asyncIterator](): AsyncIterator<AgentEvent>;
134
+ then<TResult1 = AgentResult, TResult2 = never>(onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
135
+ private drainEvents;
136
+ }
137
+ declare class Agent {
138
+ private messages;
139
+ private _running;
140
+ private options;
141
+ constructor(options: AgentOptions);
142
+ get running(): boolean;
143
+ prompt(content: string): AgentStream;
144
+ }
145
+
146
+ /**
147
+ * Detect context window overflow errors from LLM providers.
148
+ * Anthropic: "prompt is too long: N tokens > M maximum"
149
+ * OpenAI: "context_length_exceeded" / "maximum context length"
150
+ */
151
+ declare function isContextOverflow(err: unknown): boolean;
152
+ declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
153
+
154
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentOptions, type AgentResult, type AgentServerToolCallEvent, type AgentServerToolResultEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type StructuredToolResult, type ToolContext, type ToolExecuteResult, agentLoop, isContextOverflow };
@@ -0,0 +1,154 @@
1
+ import { z } from 'zod';
2
+ import { StreamOptions, Tool, ServerToolDefinition, Message, StopReason, Usage, AssistantMessage } from '@prestyj/ai';
3
+
4
+ interface StructuredToolResult {
5
+ content: string;
6
+ details?: unknown;
7
+ }
8
+ type ToolExecuteResult = string | StructuredToolResult;
9
+ interface ToolContext {
10
+ signal: AbortSignal;
11
+ toolCallId: string;
12
+ onUpdate?: (update: unknown) => void;
13
+ }
14
+ interface AgentTool<T extends z.ZodType = z.ZodType> extends Tool {
15
+ parameters: T;
16
+ execute: (args: z.infer<T>, context: ToolContext) => ToolExecuteResult | Promise<ToolExecuteResult>;
17
+ }
18
+ interface AgentTextDeltaEvent {
19
+ type: "text_delta";
20
+ text: string;
21
+ }
22
+ interface AgentThinkingDeltaEvent {
23
+ type: "thinking_delta";
24
+ text: string;
25
+ }
26
+ interface AgentToolCallStartEvent {
27
+ type: "tool_call_start";
28
+ toolCallId: string;
29
+ name: string;
30
+ args: Record<string, unknown>;
31
+ }
32
+ interface AgentToolCallUpdateEvent {
33
+ type: "tool_call_update";
34
+ toolCallId: string;
35
+ update: unknown;
36
+ }
37
+ interface AgentToolCallEndEvent {
38
+ type: "tool_call_end";
39
+ toolCallId: string;
40
+ result: string;
41
+ details?: unknown;
42
+ isError: boolean;
43
+ durationMs: number;
44
+ }
45
+ interface AgentTurnEndEvent {
46
+ type: "turn_end";
47
+ turn: number;
48
+ stopReason: StopReason;
49
+ usage: Usage;
50
+ }
51
+ interface AgentDoneEvent {
52
+ type: "agent_done";
53
+ totalTurns: number;
54
+ totalUsage: Usage;
55
+ }
56
+ interface AgentErrorEvent {
57
+ type: "error";
58
+ error: Error;
59
+ }
60
+ interface AgentServerToolCallEvent {
61
+ type: "server_tool_call";
62
+ id: string;
63
+ name: string;
64
+ input: unknown;
65
+ }
66
+ interface AgentServerToolResultEvent {
67
+ type: "server_tool_result";
68
+ toolUseId: string;
69
+ resultType: string;
70
+ data: unknown;
71
+ }
72
+ type AgentEvent = AgentTextDeltaEvent | AgentThinkingDeltaEvent | AgentToolCallStartEvent | AgentToolCallUpdateEvent | AgentToolCallEndEvent | AgentServerToolCallEvent | AgentServerToolResultEvent | AgentTurnEndEvent | AgentDoneEvent | AgentErrorEvent;
73
+ interface AgentOptions {
74
+ provider: StreamOptions["provider"];
75
+ model: string;
76
+ system?: string;
77
+ tools?: AgentTool[];
78
+ serverTools?: ServerToolDefinition[];
79
+ maxTurns?: number;
80
+ maxTokens?: number;
81
+ temperature?: number;
82
+ thinking?: StreamOptions["thinking"];
83
+ apiKey?: string;
84
+ baseUrl?: string;
85
+ signal?: AbortSignal;
86
+ accountId?: string;
87
+ cacheRetention?: StreamOptions["cacheRetention"];
88
+ /** Enable provider-native web search. */
89
+ webSearch?: boolean;
90
+ /** Enable server-side compaction (Anthropic only, beta). */
91
+ compaction?: boolean;
92
+ /** Max consecutive pause_turn continuations before stopping (default: 5).
93
+ * Prevents infinite loops when server-side tools keep pausing. */
94
+ maxContinuations?: number;
95
+ /**
96
+ * Called before each LLM call. Allows the caller to inspect and transform
97
+ * the messages array (e.g. compaction, truncation). Return the same array
98
+ * for no-op, or a new array to replace the conversation context.
99
+ *
100
+ * When `options.force` is true, the caller should compact unconditionally
101
+ * (e.g. after a context overflow error from the API).
102
+ */
103
+ transformContext?: (messages: Message[], options?: {
104
+ force?: boolean;
105
+ }) => Message[] | Promise<Message[]>;
106
+ }
107
+ interface AgentResult {
108
+ message: AssistantMessage;
109
+ totalTurns: number;
110
+ totalUsage: Usage;
111
+ }
112
+
113
+ /**
114
+ * Dual-nature result: async iterable for streaming events,
115
+ * thenable for awaiting the final AgentResult.
116
+ *
117
+ * ```ts
118
+ * // Stream events
119
+ * for await (const event of agent.prompt("hello")) { ... }
120
+ *
121
+ * // Or just await the result
122
+ * const result = await agent.prompt("hello");
123
+ * ```
124
+ */
125
+ declare class AgentStream implements AsyncIterable<AgentEvent> {
126
+ private events;
127
+ private resultPromise;
128
+ private resolveResult;
129
+ private rejectResult;
130
+ private hasConsumer;
131
+ constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void);
132
+ private pump;
133
+ [Symbol.asyncIterator](): AsyncIterator<AgentEvent>;
134
+ then<TResult1 = AgentResult, TResult2 = never>(onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
135
+ private drainEvents;
136
+ }
137
+ declare class Agent {
138
+ private messages;
139
+ private _running;
140
+ private options;
141
+ constructor(options: AgentOptions);
142
+ get running(): boolean;
143
+ prompt(content: string): AgentStream;
144
+ }
145
+
146
+ /**
147
+ * Detect context window overflow errors from LLM providers.
148
+ * Anthropic: "prompt is too long: N tokens > M maximum"
149
+ * OpenAI: "context_length_exceeded" / "maximum context length"
150
+ */
151
+ declare function isContextOverflow(err: unknown): boolean;
152
+ declare function agentLoop(messages: Message[], options: AgentOptions): AsyncGenerator<AgentEvent, AgentResult>;
153
+
154
+ export { Agent, type AgentDoneEvent, type AgentErrorEvent, type AgentEvent, type AgentOptions, type AgentResult, type AgentServerToolCallEvent, type AgentServerToolResultEvent, AgentStream, type AgentTextDeltaEvent, type AgentThinkingDeltaEvent, type AgentTool, type AgentToolCallEndEvent, type AgentToolCallStartEvent, type AgentToolCallUpdateEvent, type AgentTurnEndEvent, type StructuredToolResult, type ToolContext, type ToolExecuteResult, agentLoop, isContextOverflow };
package/dist/index.js ADDED
@@ -0,0 +1,349 @@
1
+ // src/agent.ts
2
+ import { EventStream as EventStream2 } from "@prestyj/ai";
3
+
4
+ // src/agent-loop.ts
5
+ import {
6
+ stream,
7
+ EventStream
8
+ } from "@prestyj/ai";
9
+ var DEFAULT_MAX_TURNS = 100;
10
+ function isContextOverflow(err) {
11
+ if (!(err instanceof Error)) return false;
12
+ const msg = err.message.toLowerCase();
13
+ return msg.includes("prompt is too long") || msg.includes("context_length_exceeded") || msg.includes("maximum context length") || msg.includes("token") && msg.includes("exceed");
14
+ }
15
+ function isOverloaded(err) {
16
+ if (!(err instanceof Error)) return false;
17
+ const msg = err.message.toLowerCase();
18
+ if (msg.includes("insufficient balance") || msg.includes("quota exceeded") || msg.includes("billing")) {
19
+ return false;
20
+ }
21
+ return msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("529");
22
+ }
23
+ async function* agentLoop(messages, options) {
24
+ const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
25
+ const maxContinuations = options.maxContinuations ?? 5;
26
+ const toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
27
+ const totalUsage = { inputTokens: 0, outputTokens: 0 };
28
+ let turn = 0;
29
+ let consecutivePauses = 0;
30
+ let overflowRetries = 0;
31
+ let overloadRetries = 0;
32
+ const MAX_OVERFLOW_RETRIES = 3;
33
+ const MAX_OVERLOAD_RETRIES = 3;
34
+ const OVERLOAD_RETRY_DELAY_MS = 3e3;
35
+ while (turn < maxTurns) {
36
+ options.signal?.throwIfAborted();
37
+ turn++;
38
+ if (options.transformContext) {
39
+ const transformed = await options.transformContext(messages);
40
+ if (transformed !== messages) {
41
+ messages.length = 0;
42
+ messages.push(...transformed);
43
+ }
44
+ }
45
+ let response;
46
+ try {
47
+ const result = stream({
48
+ provider: options.provider,
49
+ model: options.model,
50
+ messages,
51
+ tools: options.tools,
52
+ serverTools: options.serverTools,
53
+ webSearch: options.webSearch,
54
+ maxTokens: options.maxTokens,
55
+ temperature: options.temperature,
56
+ thinking: options.thinking,
57
+ apiKey: options.apiKey,
58
+ baseUrl: options.baseUrl,
59
+ signal: options.signal,
60
+ accountId: options.accountId,
61
+ cacheRetention: options.cacheRetention,
62
+ compaction: options.compaction
63
+ });
64
+ result.response.catch(() => {
65
+ });
66
+ for await (const event of result) {
67
+ if (event.type === "text_delta") {
68
+ yield { type: "text_delta", text: event.text };
69
+ } else if (event.type === "thinking_delta") {
70
+ yield { type: "thinking_delta", text: event.text };
71
+ } else if (event.type === "server_toolcall") {
72
+ yield {
73
+ type: "server_tool_call",
74
+ id: event.id,
75
+ name: event.name,
76
+ input: event.input
77
+ };
78
+ } else if (event.type === "server_toolresult") {
79
+ yield {
80
+ type: "server_tool_result",
81
+ toolUseId: event.toolUseId,
82
+ resultType: event.resultType,
83
+ data: event.data
84
+ };
85
+ }
86
+ }
87
+ response = await result.response;
88
+ } catch (err) {
89
+ if (overflowRetries < MAX_OVERFLOW_RETRIES && isContextOverflow(err) && options.transformContext) {
90
+ overflowRetries++;
91
+ const transformed = await options.transformContext(messages, { force: true });
92
+ if (transformed !== messages) {
93
+ messages.length = 0;
94
+ messages.push(...transformed);
95
+ }
96
+ turn--;
97
+ continue;
98
+ }
99
+ if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {
100
+ overloadRetries++;
101
+ await new Promise((r) => setTimeout(r, OVERLOAD_RETRY_DELAY_MS));
102
+ turn--;
103
+ continue;
104
+ }
105
+ throw err;
106
+ }
107
+ overflowRetries = 0;
108
+ overloadRetries = 0;
109
+ totalUsage.inputTokens += response.usage.inputTokens;
110
+ totalUsage.outputTokens += response.usage.outputTokens;
111
+ if (response.usage.cacheRead) {
112
+ totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;
113
+ }
114
+ if (response.usage.cacheWrite) {
115
+ totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
116
+ }
117
+ messages.push(response.message);
118
+ yield {
119
+ type: "turn_end",
120
+ turn,
121
+ stopReason: response.stopReason,
122
+ usage: response.usage
123
+ };
124
+ if (response.stopReason === "pause_turn") {
125
+ consecutivePauses++;
126
+ if (consecutivePauses >= maxContinuations) {
127
+ break;
128
+ }
129
+ continue;
130
+ }
131
+ consecutivePauses = 0;
132
+ if (response.stopReason !== "tool_use") {
133
+ yield {
134
+ type: "agent_done",
135
+ totalTurns: turn,
136
+ totalUsage: { ...totalUsage }
137
+ };
138
+ return {
139
+ message: response.message,
140
+ totalTurns: turn,
141
+ totalUsage: { ...totalUsage }
142
+ };
143
+ }
144
+ const allToolCalls = extractToolCalls(response.message.content);
145
+ const toolCalls = [];
146
+ const toolResults = [];
147
+ for (const tc of allToolCalls) {
148
+ if (tc.name.startsWith("$")) {
149
+ toolResults.push({
150
+ type: "tool_result",
151
+ toolCallId: tc.id,
152
+ content: JSON.stringify(tc.args)
153
+ });
154
+ } else {
155
+ toolCalls.push(tc);
156
+ }
157
+ }
158
+ const eventStream = new EventStream();
159
+ const executions = toolCalls.map(async (toolCall) => {
160
+ const startTime = Date.now();
161
+ eventStream.push({
162
+ type: "tool_call_start",
163
+ toolCallId: toolCall.id,
164
+ name: toolCall.name,
165
+ args: toolCall.args
166
+ });
167
+ let resultContent;
168
+ let details;
169
+ let isError = false;
170
+ const tool = toolMap.get(toolCall.name);
171
+ if (!tool) {
172
+ resultContent = `Unknown tool: ${toolCall.name}`;
173
+ isError = true;
174
+ } else {
175
+ try {
176
+ const parsed = tool.parameters.parse(toolCall.args);
177
+ const ctx = {
178
+ signal: options.signal ?? AbortSignal.timeout(3e5),
179
+ toolCallId: toolCall.id,
180
+ onUpdate: (update) => {
181
+ eventStream.push({
182
+ type: "tool_call_update",
183
+ toolCallId: toolCall.id,
184
+ update
185
+ });
186
+ }
187
+ };
188
+ const raw = await tool.execute(parsed, ctx);
189
+ const normalized = normalizeToolResult(raw);
190
+ resultContent = normalized.content;
191
+ details = normalized.details;
192
+ } catch (err) {
193
+ isError = true;
194
+ resultContent = err instanceof Error ? err.message : String(err);
195
+ }
196
+ }
197
+ const durationMs = Date.now() - startTime;
198
+ eventStream.push({
199
+ type: "tool_call_end",
200
+ toolCallId: toolCall.id,
201
+ result: resultContent,
202
+ details,
203
+ isError,
204
+ durationMs
205
+ });
206
+ return { toolCallId: toolCall.id, content: resultContent, isError };
207
+ });
208
+ const abortHandler = () => eventStream.abort(new Error("aborted"));
209
+ options.signal?.addEventListener("abort", abortHandler, { once: true });
210
+ let toolResultsFinalized = false;
211
+ Promise.all(executions).then((results) => {
212
+ if (toolResultsFinalized) return;
213
+ for (const tc of toolCalls) {
214
+ const r = results.find((x) => x.toolCallId === tc.id);
215
+ toolResults.push({
216
+ type: "tool_result",
217
+ toolCallId: tc.id,
218
+ content: r.content,
219
+ isError: r.isError || void 0
220
+ });
221
+ }
222
+ eventStream.close();
223
+ }).catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));
224
+ try {
225
+ for await (const event of eventStream) {
226
+ yield event;
227
+ }
228
+ } finally {
229
+ options.signal?.removeEventListener("abort", abortHandler);
230
+ toolResultsFinalized = true;
231
+ for (const tc of toolCalls) {
232
+ if (!toolResults.some((r) => r.toolCallId === tc.id)) {
233
+ toolResults.push({
234
+ type: "tool_result",
235
+ toolCallId: tc.id,
236
+ content: "Tool execution was aborted.",
237
+ isError: true
238
+ });
239
+ }
240
+ }
241
+ messages.push({ role: "tool", content: toolResults });
242
+ }
243
+ }
244
+ let lastAssistant;
245
+ for (let i = messages.length - 1; i >= 0; i--) {
246
+ if (messages[i].role === "assistant") {
247
+ lastAssistant = messages[i];
248
+ break;
249
+ }
250
+ }
251
+ yield {
252
+ type: "agent_done",
253
+ totalTurns: turn,
254
+ totalUsage: { ...totalUsage }
255
+ };
256
+ return {
257
+ message: lastAssistant ?? { role: "assistant", content: [] },
258
+ totalTurns: turn,
259
+ totalUsage: { ...totalUsage }
260
+ };
261
+ }
262
+ function normalizeToolResult(raw) {
263
+ return typeof raw === "string" ? { content: raw } : raw;
264
+ }
265
+ function extractToolCalls(content) {
266
+ if (typeof content === "string") return [];
267
+ return content.filter((part) => part.type === "tool_call");
268
+ }
269
+
270
+ // src/agent.ts
271
+ var AgentStream = class {
272
+ events;
273
+ resultPromise;
274
+ resolveResult;
275
+ rejectResult;
276
+ hasConsumer = false;
277
+ constructor(generator, onDone) {
278
+ this.events = new EventStream2();
279
+ this.resultPromise = new Promise((resolve, reject) => {
280
+ this.resolveResult = resolve;
281
+ this.rejectResult = reject;
282
+ });
283
+ this.pump(generator, onDone);
284
+ }
285
+ async pump(generator, onDone) {
286
+ try {
287
+ let next = await generator.next();
288
+ while (!next.done) {
289
+ this.events.push(next.value);
290
+ next = await generator.next();
291
+ }
292
+ this.events.close();
293
+ this.resolveResult(next.value);
294
+ } catch (err) {
295
+ const error = err instanceof Error ? err : new Error(String(err));
296
+ this.events.abort(error);
297
+ this.rejectResult(error);
298
+ } finally {
299
+ onDone();
300
+ }
301
+ }
302
+ [Symbol.asyncIterator]() {
303
+ this.hasConsumer = true;
304
+ return this.events[Symbol.asyncIterator]();
305
+ }
306
+ then(onfulfilled, onrejected) {
307
+ this.drainEvents().catch(() => {
308
+ });
309
+ return this.resultPromise.then(onfulfilled, onrejected);
310
+ }
311
+ async drainEvents() {
312
+ if (this.hasConsumer) return;
313
+ this.hasConsumer = true;
314
+ for await (const _ of this.events) {
315
+ }
316
+ }
317
+ };
318
+ var Agent = class {
319
+ messages = [];
320
+ _running = false;
321
+ options;
322
+ constructor(options) {
323
+ this.options = options;
324
+ if (options.system) {
325
+ this.messages.push({ role: "system", content: options.system });
326
+ }
327
+ }
328
+ get running() {
329
+ return this._running;
330
+ }
331
+ prompt(content) {
332
+ if (this._running) {
333
+ throw new Error("Agent is already running");
334
+ }
335
+ this._running = true;
336
+ this.messages.push({ role: "user", content });
337
+ const generator = agentLoop(this.messages, this.options);
338
+ return new AgentStream(generator, () => {
339
+ this._running = false;
340
+ });
341
+ }
342
+ };
343
+ export {
344
+ Agent,
345
+ AgentStream,
346
+ agentLoop,
347
+ isContextOverflow
348
+ };
349
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent.ts","../src/agent-loop.ts"],"sourcesContent":["import { EventStream, type Message } from \"@prestyj/ai\";\nimport { agentLoop } from \"./agent-loop.js\";\nimport type { AgentEvent, AgentOptions, AgentResult } from \"./types.js\";\n\n// ── AgentStream ─────────────────────────────────────────────\n\n/**\n * Dual-nature result: async iterable for streaming events,\n * thenable for awaiting the final AgentResult.\n *\n * ```ts\n * // Stream events\n * for await (const event of agent.prompt(\"hello\")) { ... }\n *\n * // Or just await the result\n * const result = await agent.prompt(\"hello\");\n * ```\n */\nexport class AgentStream implements AsyncIterable<AgentEvent> {\n private events: EventStream<AgentEvent>;\n private resultPromise: Promise<AgentResult>;\n private resolveResult!: (r: AgentResult) => void;\n private rejectResult!: (e: Error) => void;\n private hasConsumer = false;\n\n constructor(generator: AsyncGenerator<AgentEvent, AgentResult>, onDone: () => void) {\n this.events = new EventStream<AgentEvent>();\n this.resultPromise = new Promise<AgentResult>((resolve, reject) => {\n this.resolveResult = resolve;\n this.rejectResult = reject;\n });\n this.pump(generator, onDone);\n }\n\n private async pump(\n generator: AsyncGenerator<AgentEvent, AgentResult>,\n onDone: () => void,\n ): Promise<void> {\n try {\n let next = await generator.next();\n while (!next.done) {\n this.events.push(next.value);\n next = await generator.next();\n }\n this.events.close();\n this.resolveResult(next.value);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.events.abort(error);\n this.rejectResult(error);\n } finally {\n onDone();\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<AgentEvent> {\n this.hasConsumer = true;\n return this.events[Symbol.asyncIterator]();\n }\n\n then<TResult1 = AgentResult, TResult2 = never>(\n onfulfilled?: ((value: AgentResult) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n this.drainEvents().catch(() => {});\n return this.resultPromise.then(onfulfilled, onrejected);\n }\n\n private async drainEvents(): Promise<void> {\n if (this.hasConsumer) return;\n this.hasConsumer = true;\n for await (const _ of this.events) {\n // consume silently\n }\n }\n}\n\n// ── Agent ───────────────────────────────────────────────────\n\nexport class Agent {\n private messages: Message[] = [];\n private _running = false;\n private options: AgentOptions;\n\n constructor(options: AgentOptions) {\n this.options = options;\n if (options.system) {\n this.messages.push({ role: \"system\", content: options.system });\n }\n }\n\n get running(): boolean {\n return this._running;\n }\n\n prompt(content: string): AgentStream {\n if (this._running) {\n throw new Error(\"Agent is already running\");\n }\n this._running = true;\n\n this.messages.push({ role: \"user\", content });\n\n const generator = agentLoop(this.messages, this.options);\n return new AgentStream(generator, () => {\n this._running = false;\n });\n }\n}\n","import {\n stream,\n EventStream,\n type Message,\n type ToolCall,\n type ToolResult,\n type Usage,\n type ContentPart,\n type AssistantMessage,\n} from \"@prestyj/ai\";\nimport type {\n AgentEvent,\n AgentOptions,\n AgentResult,\n AgentTool,\n ToolContext,\n ToolExecuteResult,\n StructuredToolResult,\n} from \"./types.js\";\n\nconst DEFAULT_MAX_TURNS = 100;\n\n/**\n * Detect context window overflow errors from LLM providers.\n * Anthropic: \"prompt is too long: N tokens > M maximum\"\n * OpenAI: \"context_length_exceeded\" / \"maximum context length\"\n */\nexport function isContextOverflow(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"prompt is too long\") ||\n msg.includes(\"context_length_exceeded\") ||\n msg.includes(\"maximum context length\") ||\n (msg.includes(\"token\") && msg.includes(\"exceed\"))\n );\n}\n\n/**\n * Detect overloaded/rate-limit errors from LLM providers.\n * HTTP 429 (rate limit) or 529/503 (overloaded).\n */\nexport function isOverloaded(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n // Billing/balance errors often come as 429 but should not be retried\n if (\n msg.includes(\"insufficient balance\") ||\n msg.includes(\"quota exceeded\") ||\n msg.includes(\"billing\")\n ) {\n return false;\n }\n return (\n msg.includes(\"overloaded\") ||\n msg.includes(\"rate limit\") ||\n msg.includes(\"too many requests\") ||\n msg.includes(\"429\") ||\n msg.includes(\"529\")\n );\n}\n\nexport async function* agentLoop(\n messages: Message[],\n options: AgentOptions,\n): AsyncGenerator<AgentEvent, AgentResult> {\n const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;\n const maxContinuations = options.maxContinuations ?? 5;\n const toolMap = new Map<string, AgentTool>((options.tools ?? []).map((t) => [t.name, t]));\n\n const totalUsage: Usage = { inputTokens: 0, outputTokens: 0 };\n let turn = 0;\n let consecutivePauses = 0;\n let overflowRetries = 0;\n let overloadRetries = 0;\n const MAX_OVERFLOW_RETRIES = 3;\n const MAX_OVERLOAD_RETRIES = 3;\n const OVERLOAD_RETRY_DELAY_MS = 3_000;\n\n while (turn < maxTurns) {\n options.signal?.throwIfAborted();\n turn++;\n\n // ── Mid-loop context transform (compaction / truncation) ──\n if (options.transformContext) {\n const transformed = await options.transformContext(messages);\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n }\n\n // ── Call LLM with overflow recovery ──\n let response;\n try {\n const result = stream({\n provider: options.provider,\n model: options.model,\n messages,\n tools: options.tools,\n serverTools: options.serverTools,\n webSearch: options.webSearch,\n maxTokens: options.maxTokens,\n temperature: options.temperature,\n thinking: options.thinking,\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n signal: options.signal,\n accountId: options.accountId,\n cacheRetention: options.cacheRetention,\n compaction: options.compaction,\n });\n\n // Suppress unhandled rejection if the iterator path throws first\n result.response.catch(() => {});\n\n // Forward streaming deltas\n for await (const event of result) {\n if (event.type === \"text_delta\") {\n yield { type: \"text_delta\" as const, text: event.text };\n } else if (event.type === \"thinking_delta\") {\n yield { type: \"thinking_delta\" as const, text: event.text };\n } else if (event.type === \"server_toolcall\") {\n yield {\n type: \"server_tool_call\" as const,\n id: event.id,\n name: event.name,\n input: event.input,\n };\n } else if (event.type === \"server_toolresult\") {\n yield {\n type: \"server_tool_result\" as const,\n toolUseId: event.toolUseId,\n resultType: event.resultType,\n data: event.data,\n };\n }\n }\n\n response = await result.response;\n } catch (err) {\n // Context overflow: force-compact via transformContext and retry (up to 3 times)\n if (\n overflowRetries < MAX_OVERFLOW_RETRIES &&\n isContextOverflow(err) &&\n options.transformContext\n ) {\n overflowRetries++;\n const transformed = await options.transformContext(messages, { force: true });\n if (transformed !== messages) {\n messages.length = 0;\n messages.push(...transformed);\n }\n turn--; // Don't count the failed turn\n continue;\n }\n // Overloaded / rate-limited: wait 3s and retry (up to 3 times)\n if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {\n overloadRetries++;\n await new Promise((r) => setTimeout(r, OVERLOAD_RETRY_DELAY_MS));\n turn--; // Don't count the failed turn\n continue;\n }\n throw err;\n }\n\n // Reset retry counters after successful call\n overflowRetries = 0;\n overloadRetries = 0;\n\n // Accumulate usage\n totalUsage.inputTokens += response.usage.inputTokens;\n totalUsage.outputTokens += response.usage.outputTokens;\n if (response.usage.cacheRead) {\n totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;\n }\n if (response.usage.cacheWrite) {\n totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;\n }\n\n // Append assistant message to conversation\n messages.push(response.message);\n\n yield {\n type: \"turn_end\" as const,\n turn,\n stopReason: response.stopReason,\n usage: response.usage,\n };\n\n // Server-side tool hit iteration limit — re-send to continue.\n // Do NOT add an extra user message; the API detects the trailing\n // server_tool_use block and resumes automatically.\n if (response.stopReason === \"pause_turn\") {\n consecutivePauses++;\n if (consecutivePauses >= maxContinuations) {\n break; // Safety limit — fall through to agent_done below\n }\n continue;\n }\n consecutivePauses = 0;\n\n // If not tool_use, we're done\n if (response.stopReason !== \"tool_use\") {\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n return {\n message: response.message,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n }\n\n // Extract tool calls — separate client-executed from provider built-in (e.g. Moonshot $web_search)\n const allToolCalls = extractToolCalls(response.message.content);\n const toolCalls: ToolCall[] = [];\n const toolResults: ToolResult[] = [];\n\n for (const tc of allToolCalls) {\n if (tc.name.startsWith(\"$\")) {\n // Provider built-in tool (e.g. Moonshot $web_search) — not locally executed.\n // Still needs a tool_result for the message history round-trip.\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: JSON.stringify(tc.args),\n });\n } else {\n toolCalls.push(tc);\n }\n }\n const eventStream = new EventStream<AgentEvent>();\n\n // Launch all tool calls in parallel\n const executions = toolCalls.map(async (toolCall) => {\n const startTime = Date.now();\n\n eventStream.push({\n type: \"tool_call_start\" as const,\n toolCallId: toolCall.id,\n name: toolCall.name,\n args: toolCall.args,\n });\n\n let resultContent: string;\n let details: unknown;\n let isError = false;\n\n const tool = toolMap.get(toolCall.name);\n if (!tool) {\n resultContent = `Unknown tool: ${toolCall.name}`;\n isError = true;\n } else {\n try {\n const parsed = tool.parameters.parse(toolCall.args);\n const ctx: ToolContext = {\n signal: options.signal ?? AbortSignal.timeout(300_000),\n toolCallId: toolCall.id,\n onUpdate: (update: unknown) => {\n eventStream.push({\n type: \"tool_call_update\" as const,\n toolCallId: toolCall.id,\n update,\n });\n },\n };\n const raw = await tool.execute(parsed, ctx);\n const normalized = normalizeToolResult(raw);\n resultContent = normalized.content;\n details = normalized.details;\n } catch (err) {\n isError = true;\n resultContent = err instanceof Error ? err.message : String(err);\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n eventStream.push({\n type: \"tool_call_end\" as const,\n toolCallId: toolCall.id,\n result: resultContent,\n details,\n isError,\n durationMs,\n });\n\n return { toolCallId: toolCall.id, content: resultContent, isError };\n });\n\n // Abort the tool event stream when the signal fires so Ctrl+C\n // doesn't hang waiting for long-running tools to finish.\n const abortHandler = () => eventStream.abort(new Error(\"aborted\"));\n options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\n // Close event stream when all tools complete.\n // Track whether the finally block has already consumed toolResults\n // to prevent the race where .then() mutates toolResults after\n // messages.push() has already captured the array by reference.\n let toolResultsFinalized = false;\n\n Promise.all(executions)\n .then((results) => {\n if (toolResultsFinalized) return;\n for (const tc of toolCalls) {\n const r = results.find((x) => x.toolCallId === tc.id)!;\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: r.content,\n isError: r.isError || undefined,\n });\n }\n eventStream.close();\n })\n .catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));\n\n // Yield events as they arrive from parallel tools\n try {\n for await (const event of eventStream) {\n yield event;\n }\n } finally {\n options.signal?.removeEventListener(\"abort\", abortHandler);\n\n // Prevent the Promise.all .then() from mutating toolResults after\n // we finalize and push them into messages.\n toolResultsFinalized = true;\n\n // Ensure every tool_use has a matching tool_result, even on abort.\n // Without this, an aborted turn leaves an orphaned tool_use in the\n // message history which causes Anthropic API 400 errors on the next\n // request.\n for (const tc of toolCalls) {\n if (!toolResults.some((r) => r.toolCallId === tc.id)) {\n toolResults.push({\n type: \"tool_result\",\n toolCallId: tc.id,\n content: \"Tool execution was aborted.\",\n isError: true,\n });\n }\n }\n messages.push({ role: \"tool\", content: toolResults });\n }\n }\n\n // Exceeded max turns — return last assistant message\n let lastAssistant: AssistantMessage | undefined;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"assistant\") {\n lastAssistant = messages[i] as AssistantMessage;\n break;\n }\n }\n\n yield {\n type: \"agent_done\" as const,\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n\n return {\n message: lastAssistant ?? { role: \"assistant\" as const, content: [] },\n totalTurns: turn,\n totalUsage: { ...totalUsage },\n };\n}\n\nfunction normalizeToolResult(raw: ToolExecuteResult): StructuredToolResult {\n return typeof raw === \"string\" ? { content: raw } : raw;\n}\n\nfunction extractToolCalls(content: string | ContentPart[]): ToolCall[] {\n if (typeof content === \"string\") return [];\n return content.filter((part): part is ToolCall => part.type === \"tool_call\");\n}\n"],"mappings":"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","names":["EventStream","EventStream"]}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@prestyj/agent",
3
+ "version": "4.2.15",
4
+ "type": "module",
5
+ "description": "Agentic loop system with tool execution for LLMs",
6
+ "license": "MIT",
7
+ "author": "Nolan Grout",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ezcoder/ezcoder.git",
11
+ "directory": "packages/agent"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "dependencies": {
27
+ "zod": "^4.3.6",
28
+ "@prestyj/ai": "4.2.15"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.9.3",
32
+ "vitest": "^4.0.18"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "check": "tsc --noEmit"
40
+ }
41
+ }