@prestyj/agent 4.2.16 → 4.2.45
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 +1 -1
- package/README.md +1 -1
- package/dist/index.cjs +276 -190
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +274 -190
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Give an LLM tools. It calls them. Results go back in. It loops until it's done. That's it.
|
|
13
13
|
|
|
14
|
-
Built on top of [`@prestyj/ai`](../ai/README.md). Part of the [EZCoder](../../README.md) monorepo.
|
|
14
|
+
Built on top of [`@prestyj/ai`](../ai/README.md). Part of the [EZCoder Framework](../../README.md) monorepo.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,8 @@ __export(index_exports, {
|
|
|
23
23
|
Agent: () => Agent,
|
|
24
24
|
AgentStream: () => AgentStream,
|
|
25
25
|
agentLoop: () => agentLoop,
|
|
26
|
+
isAbortError: () => isAbortError,
|
|
27
|
+
isBillingError: () => isBillingError,
|
|
26
28
|
isContextOverflow: () => isContextOverflow
|
|
27
29
|
});
|
|
28
30
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -33,17 +35,26 @@ var import_ai2 = require("@prestyj/ai");
|
|
|
33
35
|
// src/agent-loop.ts
|
|
34
36
|
var import_ai = require("@prestyj/ai");
|
|
35
37
|
var DEFAULT_MAX_TURNS = 100;
|
|
38
|
+
function isAbortError(err) {
|
|
39
|
+
if (!(err instanceof Error)) return false;
|
|
40
|
+
if (err.name === "AbortError") return true;
|
|
41
|
+
const msg = err.message.toLowerCase();
|
|
42
|
+
return msg.includes("aborted") || msg.includes("abort");
|
|
43
|
+
}
|
|
36
44
|
function isContextOverflow(err) {
|
|
37
45
|
if (!(err instanceof Error)) return false;
|
|
38
46
|
const msg = err.message.toLowerCase();
|
|
39
47
|
return msg.includes("prompt is too long") || msg.includes("context_length_exceeded") || msg.includes("maximum context length") || msg.includes("token") && msg.includes("exceed");
|
|
40
48
|
}
|
|
49
|
+
function isBillingError(err) {
|
|
50
|
+
if (!(err instanceof Error)) return false;
|
|
51
|
+
const msg = err.message.toLowerCase();
|
|
52
|
+
return msg.includes("insufficient balance") || msg.includes("no resource package") || msg.includes("quota exceeded") || msg.includes("billing") || msg.includes("recharge");
|
|
53
|
+
}
|
|
41
54
|
function isOverloaded(err) {
|
|
42
55
|
if (!(err instanceof Error)) return false;
|
|
56
|
+
if (isBillingError(err)) return false;
|
|
43
57
|
const msg = err.message.toLowerCase();
|
|
44
|
-
if (msg.includes("insufficient balance") || msg.includes("quota exceeded") || msg.includes("billing")) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
58
|
return msg.includes("overloaded") || msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429") || msg.includes("529");
|
|
48
59
|
}
|
|
49
60
|
async function* agentLoop(messages, options) {
|
|
@@ -55,217 +66,263 @@ async function* agentLoop(messages, options) {
|
|
|
55
66
|
let consecutivePauses = 0;
|
|
56
67
|
let overflowRetries = 0;
|
|
57
68
|
let overloadRetries = 0;
|
|
69
|
+
let emptyResponseRetries = 0;
|
|
58
70
|
const MAX_OVERFLOW_RETRIES = 3;
|
|
59
71
|
const MAX_OVERLOAD_RETRIES = 3;
|
|
72
|
+
const MAX_EMPTY_RESPONSE_RETRIES = 3;
|
|
60
73
|
const OVERLOAD_RETRY_DELAY_MS = 3e3;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 });
|
|
74
|
+
try {
|
|
75
|
+
while (turn < maxTurns) {
|
|
76
|
+
options.signal?.throwIfAborted();
|
|
77
|
+
turn++;
|
|
78
|
+
if (options.transformContext) {
|
|
79
|
+
const transformed = await options.transformContext(messages);
|
|
118
80
|
if (transformed !== messages) {
|
|
119
81
|
messages.length = 0;
|
|
120
82
|
messages.push(...transformed);
|
|
121
83
|
}
|
|
122
|
-
turn--;
|
|
123
|
-
continue;
|
|
124
84
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
85
|
+
let response;
|
|
86
|
+
try {
|
|
87
|
+
const result = (0, import_ai.stream)({
|
|
88
|
+
provider: options.provider,
|
|
89
|
+
model: options.model,
|
|
90
|
+
messages,
|
|
91
|
+
tools: options.tools,
|
|
92
|
+
serverTools: options.serverTools,
|
|
93
|
+
webSearch: options.webSearch,
|
|
94
|
+
maxTokens: options.maxTokens,
|
|
95
|
+
temperature: options.temperature,
|
|
96
|
+
thinking: options.thinking,
|
|
97
|
+
apiKey: options.apiKey,
|
|
98
|
+
baseUrl: options.baseUrl,
|
|
99
|
+
signal: options.signal,
|
|
100
|
+
accountId: options.accountId,
|
|
101
|
+
cacheRetention: options.cacheRetention,
|
|
102
|
+
compaction: options.compaction
|
|
103
|
+
});
|
|
104
|
+
result.response.catch(() => {
|
|
105
|
+
});
|
|
106
|
+
for await (const event of result) {
|
|
107
|
+
if (event.type === "text_delta") {
|
|
108
|
+
yield { type: "text_delta", text: event.text };
|
|
109
|
+
} else if (event.type === "thinking_delta") {
|
|
110
|
+
yield { type: "thinking_delta", text: event.text };
|
|
111
|
+
} else if (event.type === "server_toolcall") {
|
|
112
|
+
yield {
|
|
113
|
+
type: "server_tool_call",
|
|
114
|
+
id: event.id,
|
|
115
|
+
name: event.name,
|
|
116
|
+
input: event.input
|
|
117
|
+
};
|
|
118
|
+
} else if (event.type === "server_toolresult") {
|
|
119
|
+
yield {
|
|
120
|
+
type: "server_tool_result",
|
|
121
|
+
toolUseId: event.toolUseId,
|
|
122
|
+
resultType: event.resultType,
|
|
123
|
+
data: event.data
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
response = await result.response;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
if (overflowRetries < MAX_OVERFLOW_RETRIES && isContextOverflow(err) && options.transformContext) {
|
|
130
|
+
overflowRetries++;
|
|
131
|
+
const transformed = await options.transformContext(messages, { force: true });
|
|
132
|
+
if (transformed !== messages) {
|
|
133
|
+
messages.length = 0;
|
|
134
|
+
messages.push(...transformed);
|
|
135
|
+
}
|
|
136
|
+
turn--;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (overloadRetries < MAX_OVERLOAD_RETRIES && isOverloaded(err)) {
|
|
140
|
+
overloadRetries++;
|
|
141
|
+
await new Promise((r) => setTimeout(r, OVERLOAD_RETRY_DELAY_MS));
|
|
142
|
+
turn--;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (isAbortError(err) || options.signal?.aborted) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
throw err;
|
|
130
149
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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;
|
|
150
|
+
overflowRetries = 0;
|
|
151
|
+
overloadRetries = 0;
|
|
152
|
+
if (response.usage.outputTokens === 0 && (response.message.content === "" || Array.isArray(response.message.content) && response.message.content.length === 0)) {
|
|
153
|
+
if (emptyResponseRetries < MAX_EMPTY_RESPONSE_RETRIES) {
|
|
154
|
+
emptyResponseRetries++;
|
|
155
|
+
turn--;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
154
158
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
emptyResponseRetries = 0;
|
|
160
|
+
totalUsage.inputTokens += response.usage.inputTokens;
|
|
161
|
+
totalUsage.outputTokens += response.usage.outputTokens;
|
|
162
|
+
if (response.usage.cacheRead) {
|
|
163
|
+
totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + response.usage.cacheRead;
|
|
164
|
+
}
|
|
165
|
+
if (response.usage.cacheWrite) {
|
|
166
|
+
totalUsage.cacheWrite = (totalUsage.cacheWrite ?? 0) + response.usage.cacheWrite;
|
|
167
|
+
}
|
|
168
|
+
messages.push(response.message);
|
|
159
169
|
yield {
|
|
160
|
-
type: "
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
message: response.message,
|
|
166
|
-
totalTurns: turn,
|
|
167
|
-
totalUsage: { ...totalUsage }
|
|
170
|
+
type: "turn_end",
|
|
171
|
+
turn,
|
|
172
|
+
stopReason: response.stopReason,
|
|
173
|
+
usage: response.usage
|
|
168
174
|
};
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
toolResults.push({
|
|
176
|
-
type: "tool_result",
|
|
177
|
-
toolCallId: tc.id,
|
|
178
|
-
content: JSON.stringify(tc.args)
|
|
179
|
-
});
|
|
180
|
-
} else {
|
|
181
|
-
toolCalls.push(tc);
|
|
175
|
+
if (response.stopReason === "pause_turn") {
|
|
176
|
+
consecutivePauses++;
|
|
177
|
+
if (consecutivePauses >= maxContinuations) {
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
continue;
|
|
182
181
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
});
|
|
182
|
+
consecutivePauses = 0;
|
|
183
|
+
const allToolCalls = extractToolCalls(response.message.content);
|
|
184
|
+
if (response.stopReason !== "tool_use" && allToolCalls.length === 0) {
|
|
185
|
+
if (options.getSteeringMessages) {
|
|
186
|
+
const steering = await options.getSteeringMessages();
|
|
187
|
+
if (steering && steering.length > 0) {
|
|
188
|
+
for (const msg of steering) {
|
|
189
|
+
yield { type: "steering_message", content: msg.content };
|
|
190
|
+
messages.push(msg);
|
|
212
191
|
}
|
|
213
|
-
|
|
214
|
-
|
|
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);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
221
194
|
}
|
|
195
|
+
yield {
|
|
196
|
+
type: "agent_done",
|
|
197
|
+
totalTurns: turn,
|
|
198
|
+
totalUsage: { ...totalUsage }
|
|
199
|
+
};
|
|
200
|
+
return {
|
|
201
|
+
message: response.message,
|
|
202
|
+
totalTurns: turn,
|
|
203
|
+
totalUsage: { ...totalUsage }
|
|
204
|
+
};
|
|
222
205
|
}
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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;
|
|
206
|
+
const toolCalls = [];
|
|
207
|
+
const toolResults = [];
|
|
208
|
+
for (const tc of allToolCalls) {
|
|
209
|
+
if (tc.name.startsWith("$")) {
|
|
210
|
+
toolResults.push({
|
|
211
|
+
type: "tool_result",
|
|
212
|
+
toolCallId: tc.id,
|
|
213
|
+
content: JSON.stringify(tc.args)
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
toolCalls.push(tc);
|
|
217
|
+
}
|
|
253
218
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
219
|
+
const eventStream = new import_ai.EventStream();
|
|
220
|
+
const executions = toolCalls.map(async (toolCall) => {
|
|
221
|
+
const startTime = Date.now();
|
|
222
|
+
eventStream.push({
|
|
223
|
+
type: "tool_call_start",
|
|
224
|
+
toolCallId: toolCall.id,
|
|
225
|
+
name: toolCall.name,
|
|
226
|
+
args: toolCall.args
|
|
227
|
+
});
|
|
228
|
+
let resultContent;
|
|
229
|
+
let details;
|
|
230
|
+
let isError = false;
|
|
231
|
+
const tool = toolMap.get(toolCall.name);
|
|
232
|
+
if (!tool) {
|
|
233
|
+
resultContent = `Unknown tool: ${toolCall.name}`;
|
|
234
|
+
isError = true;
|
|
235
|
+
} else {
|
|
236
|
+
try {
|
|
237
|
+
const parsed = tool.parameters.parse(toolCall.args);
|
|
238
|
+
const ctx = {
|
|
239
|
+
signal: options.signal ?? AbortSignal.timeout(3e5),
|
|
240
|
+
toolCallId: toolCall.id,
|
|
241
|
+
onUpdate: (update) => {
|
|
242
|
+
eventStream.push({
|
|
243
|
+
type: "tool_call_update",
|
|
244
|
+
toolCallId: toolCall.id,
|
|
245
|
+
update
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const raw = await tool.execute(parsed, ctx);
|
|
250
|
+
const normalized = normalizeToolResult(raw);
|
|
251
|
+
resultContent = normalized.content;
|
|
252
|
+
details = normalized.details;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
isError = true;
|
|
255
|
+
resultContent = err instanceof Error ? err.message : String(err);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const durationMs = Date.now() - startTime;
|
|
259
|
+
eventStream.push({
|
|
260
|
+
type: "tool_call_end",
|
|
261
|
+
toolCallId: toolCall.id,
|
|
262
|
+
result: resultContent,
|
|
263
|
+
details,
|
|
264
|
+
isError,
|
|
265
|
+
durationMs
|
|
266
|
+
});
|
|
267
|
+
return { toolCallId: toolCall.id, content: resultContent, isError };
|
|
268
|
+
});
|
|
269
|
+
const abortHandler = () => eventStream.abort(new Error("aborted"));
|
|
270
|
+
options.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
271
|
+
let toolResultsFinalized = false;
|
|
272
|
+
Promise.all(executions).then((results) => {
|
|
273
|
+
if (toolResultsFinalized) return;
|
|
274
|
+
const resultsMap = new Map(results.map((r) => [r.toolCallId, r]));
|
|
275
|
+
for (const tc of toolCalls) {
|
|
276
|
+
const r = resultsMap.get(tc.id);
|
|
259
277
|
toolResults.push({
|
|
260
278
|
type: "tool_result",
|
|
261
279
|
toolCallId: tc.id,
|
|
262
|
-
content:
|
|
263
|
-
isError:
|
|
280
|
+
content: r.content,
|
|
281
|
+
isError: r.isError || void 0
|
|
264
282
|
});
|
|
265
283
|
}
|
|
284
|
+
eventStream.close();
|
|
285
|
+
}).catch((err) => eventStream.abort(err instanceof Error ? err : new Error(String(err))));
|
|
286
|
+
let toolsAborted = false;
|
|
287
|
+
try {
|
|
288
|
+
for await (const event of eventStream) {
|
|
289
|
+
yield event;
|
|
290
|
+
}
|
|
291
|
+
} catch (err) {
|
|
292
|
+
if (isAbortError(err) || options.signal?.aborted) {
|
|
293
|
+
toolsAborted = true;
|
|
294
|
+
} else {
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
} finally {
|
|
298
|
+
options.signal?.removeEventListener("abort", abortHandler);
|
|
299
|
+
toolResultsFinalized = true;
|
|
300
|
+
const resolvedIds = new Set(toolResults.map((r) => r.toolCallId));
|
|
301
|
+
for (const tc of toolCalls) {
|
|
302
|
+
if (!resolvedIds.has(tc.id)) {
|
|
303
|
+
toolResults.push({
|
|
304
|
+
type: "tool_result",
|
|
305
|
+
toolCallId: tc.id,
|
|
306
|
+
content: "Tool execution was aborted.",
|
|
307
|
+
isError: true
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
messages.push({ role: "tool", content: toolResults });
|
|
312
|
+
}
|
|
313
|
+
if (toolsAborted) break;
|
|
314
|
+
if (options.getSteeringMessages) {
|
|
315
|
+
const steering = await options.getSteeringMessages();
|
|
316
|
+
if (steering && steering.length > 0) {
|
|
317
|
+
for (const msg of steering) {
|
|
318
|
+
yield { type: "steering_message", content: msg.content };
|
|
319
|
+
messages.push(msg);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
266
322
|
}
|
|
267
|
-
messages.push({ role: "tool", content: toolResults });
|
|
268
323
|
}
|
|
324
|
+
} finally {
|
|
325
|
+
sanitizeOrphanedServerTools(messages);
|
|
269
326
|
}
|
|
270
327
|
let lastAssistant;
|
|
271
328
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -292,6 +349,33 @@ function extractToolCalls(content) {
|
|
|
292
349
|
if (typeof content === "string") return [];
|
|
293
350
|
return content.filter((part) => part.type === "tool_call");
|
|
294
351
|
}
|
|
352
|
+
function sanitizeOrphanedServerTools(messages) {
|
|
353
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
354
|
+
const msg = messages[i];
|
|
355
|
+
if (msg.role !== "assistant") continue;
|
|
356
|
+
if (typeof msg.content === "string" || !Array.isArray(msg.content)) break;
|
|
357
|
+
const serverToolIds = /* @__PURE__ */ new Set();
|
|
358
|
+
const resultToolIds = /* @__PURE__ */ new Set();
|
|
359
|
+
for (const part of msg.content) {
|
|
360
|
+
if (part.type === "server_tool_call") serverToolIds.add(part.id);
|
|
361
|
+
if (part.type === "server_tool_result") resultToolIds.add(part.toolUseId);
|
|
362
|
+
}
|
|
363
|
+
const orphanedIds = /* @__PURE__ */ new Set();
|
|
364
|
+
for (const id of serverToolIds) {
|
|
365
|
+
if (!resultToolIds.has(id)) orphanedIds.add(id);
|
|
366
|
+
}
|
|
367
|
+
if (orphanedIds.size === 0) break;
|
|
368
|
+
const filtered = msg.content.filter(
|
|
369
|
+
(part) => !(part.type === "server_tool_call" && orphanedIds.has(part.id))
|
|
370
|
+
);
|
|
371
|
+
if (filtered.length === 0) {
|
|
372
|
+
messages.splice(i, 1);
|
|
373
|
+
} else {
|
|
374
|
+
msg.content = filtered;
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
295
379
|
|
|
296
380
|
// src/agent.ts
|
|
297
381
|
var AgentStream = class {
|
|
@@ -371,6 +455,8 @@ var Agent = class {
|
|
|
371
455
|
Agent,
|
|
372
456
|
AgentStream,
|
|
373
457
|
agentLoop,
|
|
458
|
+
isAbortError,
|
|
459
|
+
isBillingError,
|
|
374
460
|
isContextOverflow
|
|
375
461
|
});
|
|
376
462
|
//# sourceMappingURL=index.cjs.map
|