@cuylabs/agent-core 0.3.0 → 0.5.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/README.md +216 -41
- package/dist/builder-RcTZuYnO.d.ts +34 -0
- package/dist/capabilities/index.d.ts +97 -0
- package/dist/capabilities/index.js +46 -0
- package/dist/chunk-6TDTQJ4P.js +116 -0
- package/dist/chunk-7MUFEN4K.js +559 -0
- package/dist/chunk-BDBZ3SLK.js +745 -0
- package/dist/chunk-DWYX7ASF.js +26 -0
- package/dist/chunk-FG4MD5MU.js +54 -0
- package/dist/chunk-IMGQOTU2.js +2019 -0
- package/dist/chunk-IVUJDISU.js +556 -0
- package/dist/chunk-LRHOS4ZN.js +584 -0
- package/dist/chunk-OTUGSCED.js +691 -0
- package/dist/chunk-P6YF7USR.js +182 -0
- package/dist/chunk-QAQADS4X.js +258 -0
- package/dist/chunk-QWFMX226.js +879 -0
- package/dist/{chunk-6VKLWNRE.js → chunk-SDSBEQXG.js} +1 -132
- package/dist/chunk-VBWWUHWI.js +724 -0
- package/dist/chunk-VEKUXUVF.js +41 -0
- package/dist/chunk-X635CM2F.js +305 -0
- package/dist/chunk-YUUJK53A.js +91 -0
- package/dist/chunk-ZXAKHMWH.js +283 -0
- package/dist/config-D2xeGEHK.d.ts +52 -0
- package/dist/context/index.d.ts +259 -0
- package/dist/context/index.js +26 -0
- package/dist/identifiers-BLUxFqV_.d.ts +12 -0
- package/dist/index-p0kOsVsE.d.ts +1067 -0
- package/dist/index-tmhaADz5.d.ts +198 -0
- package/dist/index.d.ts +185 -4316
- package/dist/index.js +1238 -5368
- package/dist/mcp/index.d.ts +26 -0
- package/dist/mcp/index.js +14 -0
- package/dist/messages-BYWGn8TY.d.ts +110 -0
- package/dist/middleware/index.d.ts +7 -0
- package/dist/middleware/index.js +12 -0
- package/dist/models/index.d.ts +33 -0
- package/dist/models/index.js +12 -0
- package/dist/network-D76DS5ot.d.ts +5 -0
- package/dist/prompt/index.d.ts +224 -0
- package/dist/prompt/index.js +45 -0
- package/dist/reasoning/index.d.ts +71 -0
- package/dist/reasoning/index.js +47 -0
- package/dist/registry-CuRWWtcT.d.ts +164 -0
- package/dist/resolver-DOfZ-xuk.d.ts +254 -0
- package/dist/runner-C7aMP_x3.d.ts +596 -0
- package/dist/runtime/index.d.ts +357 -0
- package/dist/runtime/index.js +64 -0
- package/dist/session-manager-Uawm2Le7.d.ts +274 -0
- package/dist/skill/index.d.ts +103 -0
- package/dist/skill/index.js +39 -0
- package/dist/storage/index.d.ts +167 -0
- package/dist/storage/index.js +50 -0
- package/dist/sub-agent/index.d.ts +14 -0
- package/dist/sub-agent/index.js +15 -0
- package/dist/tool/index.d.ts +173 -1
- package/dist/tool/index.js +12 -3
- package/dist/tool-DYp6-cC3.d.ts +239 -0
- package/dist/tool-pFAnJc5Y.d.ts +419 -0
- package/dist/tracker-DClqYqTj.d.ts +96 -0
- package/dist/tracking/index.d.ts +109 -0
- package/dist/tracking/index.js +20 -0
- package/dist/types-CQaXbRsS.d.ts +47 -0
- package/dist/types-MM1JoX5T.d.ts +810 -0
- package/dist/types-VQgymC1N.d.ts +156 -0
- package/package.json +89 -5
- package/dist/index-QR704uRr.d.ts +0 -472
|
@@ -0,0 +1,2019 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeAgentToolCall
|
|
3
|
+
} from "./chunk-FG4MD5MU.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_CONTEXT_LIMITS
|
|
6
|
+
} from "./chunk-QAQADS4X.js";
|
|
7
|
+
import {
|
|
8
|
+
buildReasoningOptionsSync
|
|
9
|
+
} from "./chunk-X635CM2F.js";
|
|
10
|
+
|
|
11
|
+
// src/runtime/observer.ts
|
|
12
|
+
function defaultAgentTaskCheckpointStrategy(input) {
|
|
13
|
+
switch (input.event.type) {
|
|
14
|
+
case "step-finish":
|
|
15
|
+
return "step-finish";
|
|
16
|
+
case "tool-result":
|
|
17
|
+
return "tool-result";
|
|
18
|
+
case "tool-error":
|
|
19
|
+
return "tool-error";
|
|
20
|
+
case "turn-boundary":
|
|
21
|
+
return input.event.boundary;
|
|
22
|
+
case "complete":
|
|
23
|
+
return "task-complete";
|
|
24
|
+
default:
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/runtime/task-runner.ts
|
|
30
|
+
import { randomUUID } from "crypto";
|
|
31
|
+
|
|
32
|
+
// src/runtime/turn-state.ts
|
|
33
|
+
var EMPTY_USAGE = {
|
|
34
|
+
inputTokens: 0,
|
|
35
|
+
outputTokens: 0,
|
|
36
|
+
totalTokens: 0
|
|
37
|
+
};
|
|
38
|
+
function cloneUsage(usage) {
|
|
39
|
+
return { ...usage };
|
|
40
|
+
}
|
|
41
|
+
function normalizeUsage(usage) {
|
|
42
|
+
if (!usage) {
|
|
43
|
+
return cloneUsage(EMPTY_USAGE);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
47
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
48
|
+
totalTokens: usage.totalTokens ?? 0
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function removeActiveToolCall(toolCalls, toolCallId) {
|
|
52
|
+
return toolCalls.filter((toolCall) => toolCall.toolCallId !== toolCallId);
|
|
53
|
+
}
|
|
54
|
+
function createAgentTurnState(options) {
|
|
55
|
+
return {
|
|
56
|
+
sessionId: options.sessionId,
|
|
57
|
+
phase: "initializing",
|
|
58
|
+
step: 0,
|
|
59
|
+
response: "",
|
|
60
|
+
usage: cloneUsage(EMPTY_USAGE),
|
|
61
|
+
eventCount: 0,
|
|
62
|
+
activeToolCalls: [],
|
|
63
|
+
resolvedToolCalls: [],
|
|
64
|
+
startedAt: options.startedAt,
|
|
65
|
+
updatedAt: options.startedAt
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function advanceAgentTurnState(state, event, updatedAt, options = {}) {
|
|
69
|
+
const next = {
|
|
70
|
+
...state,
|
|
71
|
+
usage: cloneUsage(state.usage),
|
|
72
|
+
activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
|
|
73
|
+
resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
|
|
74
|
+
...toolCall
|
|
75
|
+
})),
|
|
76
|
+
eventCount: state.eventCount + 1,
|
|
77
|
+
lastEvent: event,
|
|
78
|
+
updatedAt
|
|
79
|
+
};
|
|
80
|
+
switch (event.type) {
|
|
81
|
+
case "step-start":
|
|
82
|
+
next.phase = "running-model";
|
|
83
|
+
next.step = event.step;
|
|
84
|
+
next.maxSteps = event.maxSteps;
|
|
85
|
+
next.activeToolCalls = [];
|
|
86
|
+
next.resolvedToolCalls = [];
|
|
87
|
+
next.lastFinishReason = void 0;
|
|
88
|
+
delete next.error;
|
|
89
|
+
break;
|
|
90
|
+
case "text-delta":
|
|
91
|
+
next.response += event.text;
|
|
92
|
+
break;
|
|
93
|
+
case "tool-start":
|
|
94
|
+
next.phase = "running-tools";
|
|
95
|
+
next.activeToolCalls = [
|
|
96
|
+
...removeActiveToolCall(next.activeToolCalls, event.toolCallId),
|
|
97
|
+
{
|
|
98
|
+
toolCallId: event.toolCallId,
|
|
99
|
+
toolName: event.toolName,
|
|
100
|
+
input: event.input,
|
|
101
|
+
startedAt: updatedAt,
|
|
102
|
+
...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
break;
|
|
106
|
+
case "tool-result":
|
|
107
|
+
next.phase = "running-tools";
|
|
108
|
+
next.activeToolCalls = removeActiveToolCall(
|
|
109
|
+
next.activeToolCalls,
|
|
110
|
+
event.toolCallId
|
|
111
|
+
);
|
|
112
|
+
next.resolvedToolCalls = [
|
|
113
|
+
...next.resolvedToolCalls,
|
|
114
|
+
{
|
|
115
|
+
toolCallId: event.toolCallId,
|
|
116
|
+
toolName: event.toolName,
|
|
117
|
+
outcome: "result",
|
|
118
|
+
value: event.result,
|
|
119
|
+
resolvedAt: updatedAt,
|
|
120
|
+
...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
|
|
121
|
+
}
|
|
122
|
+
];
|
|
123
|
+
break;
|
|
124
|
+
case "tool-error":
|
|
125
|
+
next.phase = "running-tools";
|
|
126
|
+
next.activeToolCalls = removeActiveToolCall(
|
|
127
|
+
next.activeToolCalls,
|
|
128
|
+
event.toolCallId
|
|
129
|
+
);
|
|
130
|
+
next.resolvedToolCalls = [
|
|
131
|
+
...next.resolvedToolCalls,
|
|
132
|
+
{
|
|
133
|
+
toolCallId: event.toolCallId,
|
|
134
|
+
toolName: event.toolName,
|
|
135
|
+
outcome: "error",
|
|
136
|
+
value: event.error,
|
|
137
|
+
resolvedAt: updatedAt,
|
|
138
|
+
...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
break;
|
|
142
|
+
case "step-finish":
|
|
143
|
+
next.phase = "committing-step";
|
|
144
|
+
next.step = event.step;
|
|
145
|
+
next.usage = normalizeUsage(event.usage ?? next.usage);
|
|
146
|
+
next.lastFinishReason = event.finishReason;
|
|
147
|
+
break;
|
|
148
|
+
case "turn-boundary":
|
|
149
|
+
next.lastBoundary = {
|
|
150
|
+
kind: event.boundary,
|
|
151
|
+
createdAt: updatedAt,
|
|
152
|
+
...event.step !== void 0 ? { step: event.step } : {},
|
|
153
|
+
...event.messageRole ? { messageRole: event.messageRole } : {},
|
|
154
|
+
...event.pendingToolCallCount !== void 0 ? { pendingToolCallCount: event.pendingToolCallCount } : {}
|
|
155
|
+
};
|
|
156
|
+
switch (event.boundary) {
|
|
157
|
+
case "input-commit-start":
|
|
158
|
+
case "input-commit-finish":
|
|
159
|
+
case "intervention-commit-start":
|
|
160
|
+
case "intervention-commit-finish":
|
|
161
|
+
next.phase = "committing-input";
|
|
162
|
+
break;
|
|
163
|
+
case "step-commit-start":
|
|
164
|
+
case "step-commit-finish":
|
|
165
|
+
next.phase = "committing-step";
|
|
166
|
+
break;
|
|
167
|
+
case "output-commit-start":
|
|
168
|
+
case "output-commit-finish":
|
|
169
|
+
next.phase = "committing-output";
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
case "complete":
|
|
174
|
+
next.phase = "completed";
|
|
175
|
+
next.usage = normalizeUsage(event.usage ?? next.usage);
|
|
176
|
+
break;
|
|
177
|
+
case "error":
|
|
178
|
+
next.phase = "failed";
|
|
179
|
+
next.error = event.error.message;
|
|
180
|
+
break;
|
|
181
|
+
default:
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
return next;
|
|
185
|
+
}
|
|
186
|
+
function failAgentTurnState(state, error, updatedAt) {
|
|
187
|
+
return {
|
|
188
|
+
...state,
|
|
189
|
+
usage: cloneUsage(state.usage),
|
|
190
|
+
activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
|
|
191
|
+
resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
|
|
192
|
+
...toolCall
|
|
193
|
+
})),
|
|
194
|
+
phase: "failed",
|
|
195
|
+
error: error.message,
|
|
196
|
+
updatedAt
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/runtime/task-runner.ts
|
|
201
|
+
function normalizeNonEmpty(value, label) {
|
|
202
|
+
const normalized = value.trim();
|
|
203
|
+
if (!normalized) {
|
|
204
|
+
throw new Error(`${label} must not be empty`);
|
|
205
|
+
}
|
|
206
|
+
return normalized;
|
|
207
|
+
}
|
|
208
|
+
function sanitizeSegment(input) {
|
|
209
|
+
return input.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
210
|
+
}
|
|
211
|
+
function buildRuntimeSessionId(prefix, key) {
|
|
212
|
+
const timestamp = Date.now().toString(36);
|
|
213
|
+
const suffix = randomUUID().slice(0, 8);
|
|
214
|
+
const normalizedPrefix = sanitizeSegment(prefix) || "runtime";
|
|
215
|
+
const normalizedKey = key ? sanitizeSegment(key) : "";
|
|
216
|
+
if (normalizedKey) {
|
|
217
|
+
return `${normalizedPrefix}:${normalizedKey}:${timestamp}:${suffix}`;
|
|
218
|
+
}
|
|
219
|
+
return `${normalizedPrefix}:${timestamp}:${suffix}`;
|
|
220
|
+
}
|
|
221
|
+
function nowIso() {
|
|
222
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
223
|
+
}
|
|
224
|
+
async function notifyObservers(observers, invoke) {
|
|
225
|
+
for (const observer of observers) {
|
|
226
|
+
try {
|
|
227
|
+
await invoke(observer);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.warn(
|
|
230
|
+
"[agent-task-runner] observer error:",
|
|
231
|
+
error instanceof Error ? error.message : String(error)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function createAgentTaskRunner(agent, options = {}) {
|
|
237
|
+
const prefix = options.sessionPrefix ?? "runtime";
|
|
238
|
+
const baseObservers = options.observers ?? [];
|
|
239
|
+
const checkpointStrategy = options.checkpointStrategy ?? defaultAgentTaskCheckpointStrategy;
|
|
240
|
+
return async (payload, context = {}) => {
|
|
241
|
+
const message = normalizeNonEmpty(payload.message, "payload.message");
|
|
242
|
+
const resolvedSessionId = payload.sessionId?.trim() || options.resolveSessionId?.(payload, context)?.trim() || buildRuntimeSessionId(prefix, context.fallbackSessionKey);
|
|
243
|
+
const sessionId = normalizeNonEmpty(resolvedSessionId, "sessionId");
|
|
244
|
+
const startedAt = nowIso();
|
|
245
|
+
const run = {
|
|
246
|
+
payload,
|
|
247
|
+
context,
|
|
248
|
+
sessionId,
|
|
249
|
+
startedAt
|
|
250
|
+
};
|
|
251
|
+
const toolCalls = [];
|
|
252
|
+
let turnState = createAgentTurnState({
|
|
253
|
+
sessionId,
|
|
254
|
+
startedAt
|
|
255
|
+
});
|
|
256
|
+
const createSnapshot = () => ({
|
|
257
|
+
sessionId,
|
|
258
|
+
response: turnState.response,
|
|
259
|
+
usage: { ...turnState.usage },
|
|
260
|
+
toolCalls: toolCalls.map((toolCall) => ({ ...toolCall })),
|
|
261
|
+
eventCount: turnState.eventCount,
|
|
262
|
+
activeStep: turnState.step > 0 ? turnState.step : void 0,
|
|
263
|
+
lastEvent: turnState.lastEvent,
|
|
264
|
+
error: turnState.error,
|
|
265
|
+
startedAt: turnState.startedAt,
|
|
266
|
+
updatedAt: turnState.updatedAt,
|
|
267
|
+
turnState
|
|
268
|
+
});
|
|
269
|
+
const emitCheckpoint = async (reason, event) => {
|
|
270
|
+
if (baseObservers.length === 0) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const checkpoint = {
|
|
274
|
+
run,
|
|
275
|
+
reason,
|
|
276
|
+
snapshot: createSnapshot(),
|
|
277
|
+
event,
|
|
278
|
+
createdAt: turnState.updatedAt
|
|
279
|
+
};
|
|
280
|
+
await notifyObservers(baseObservers, async (observer) => {
|
|
281
|
+
await observer.onCheckpoint?.(checkpoint);
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
await notifyObservers(baseObservers, async (observer) => {
|
|
285
|
+
await observer.onTaskStart?.(run, createSnapshot());
|
|
286
|
+
});
|
|
287
|
+
await emitCheckpoint("task-start");
|
|
288
|
+
const activateCtx = baseObservers.find((o) => o.activateContext)?.activateContext?.bind(void 0, sessionId);
|
|
289
|
+
try {
|
|
290
|
+
const processChatStream = async () => {
|
|
291
|
+
for await (const event of agent.chat(sessionId, message, {
|
|
292
|
+
abort: context.signal,
|
|
293
|
+
system: payload.system
|
|
294
|
+
})) {
|
|
295
|
+
turnState = advanceAgentTurnState(turnState, event, nowIso());
|
|
296
|
+
if (event.type === "tool-result") {
|
|
297
|
+
toolCalls.push({ name: event.toolName, result: event.result });
|
|
298
|
+
}
|
|
299
|
+
const snapshot = createSnapshot();
|
|
300
|
+
await notifyObservers(baseObservers, async (observer) => {
|
|
301
|
+
await observer.onTaskEvent?.(run, event, snapshot);
|
|
302
|
+
});
|
|
303
|
+
const checkpointReason = checkpointStrategy({
|
|
304
|
+
run,
|
|
305
|
+
event,
|
|
306
|
+
snapshot
|
|
307
|
+
});
|
|
308
|
+
if (checkpointReason) {
|
|
309
|
+
await emitCheckpoint(checkpointReason, event);
|
|
310
|
+
}
|
|
311
|
+
if (event.type === "error") {
|
|
312
|
+
throw event.error;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
if (activateCtx) {
|
|
317
|
+
await activateCtx(processChatStream);
|
|
318
|
+
} else {
|
|
319
|
+
await processChatStream();
|
|
320
|
+
}
|
|
321
|
+
const result = {
|
|
322
|
+
response: turnState.response,
|
|
323
|
+
sessionId,
|
|
324
|
+
usage: { ...turnState.usage },
|
|
325
|
+
toolCalls
|
|
326
|
+
};
|
|
327
|
+
await notifyObservers(baseObservers, async (observer) => {
|
|
328
|
+
await observer.onTaskComplete?.(run, result, createSnapshot());
|
|
329
|
+
});
|
|
330
|
+
return result;
|
|
331
|
+
} catch (error) {
|
|
332
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
333
|
+
turnState = failAgentTurnState(turnState, normalizedError, nowIso());
|
|
334
|
+
await notifyObservers(baseObservers, async (observer) => {
|
|
335
|
+
await observer.onTaskError?.(run, normalizedError, createSnapshot());
|
|
336
|
+
});
|
|
337
|
+
await emitCheckpoint("task-error");
|
|
338
|
+
throw normalizedError;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/runtime/model-messages.ts
|
|
344
|
+
function convertAgentMessagesToModelMessages(messages) {
|
|
345
|
+
return messages.flatMap((message) => {
|
|
346
|
+
switch (message.role) {
|
|
347
|
+
case "user":
|
|
348
|
+
return [{ role: "user", content: message.content }];
|
|
349
|
+
case "assistant": {
|
|
350
|
+
if (message.toolCalls && message.toolCalls.length > 0) {
|
|
351
|
+
const toolCallParts = message.toolCalls.map(
|
|
352
|
+
(toolCall) => ({
|
|
353
|
+
type: "tool-call",
|
|
354
|
+
toolCallId: toolCall.toolCallId,
|
|
355
|
+
toolName: toolCall.toolName,
|
|
356
|
+
input: toolCall.args
|
|
357
|
+
})
|
|
358
|
+
);
|
|
359
|
+
return [{ role: "assistant", content: toolCallParts }];
|
|
360
|
+
}
|
|
361
|
+
return [{ role: "assistant", content: message.content }];
|
|
362
|
+
}
|
|
363
|
+
case "tool":
|
|
364
|
+
return [
|
|
365
|
+
{
|
|
366
|
+
role: "tool",
|
|
367
|
+
content: [
|
|
368
|
+
{
|
|
369
|
+
type: "tool-result",
|
|
370
|
+
toolCallId: message.toolCallId ?? "",
|
|
371
|
+
toolName: message.toolName ?? "",
|
|
372
|
+
output: {
|
|
373
|
+
type: "text",
|
|
374
|
+
value: typeof message.result === "string" ? message.result : JSON.stringify(message.result)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
case "system":
|
|
381
|
+
return [{ role: "system", content: message.content }];
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/runtime/turn-engine/commit-batch.ts
|
|
387
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
388
|
+
function createAgentTurnBoundaryEvent(boundary, metadata = {}) {
|
|
389
|
+
return {
|
|
390
|
+
type: "turn-boundary",
|
|
391
|
+
boundary,
|
|
392
|
+
...metadata.step !== void 0 ? { step: metadata.step } : {},
|
|
393
|
+
...metadata.messageRole ? { messageRole: metadata.messageRole } : {},
|
|
394
|
+
...metadata.pendingToolCallCount !== void 0 ? { pendingToolCallCount: metadata.pendingToolCallCount } : {}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function createAgentTurnStepCommitBatch(step, snapshot, options = {}) {
|
|
398
|
+
if (snapshot.toolCalls.length === 0) {
|
|
399
|
+
return void 0;
|
|
400
|
+
}
|
|
401
|
+
const createdAt = options.createdAt ?? /* @__PURE__ */ new Date();
|
|
402
|
+
const assistantMessage = {
|
|
403
|
+
id: options.assistantMessageId ?? randomUUID2(),
|
|
404
|
+
role: "assistant",
|
|
405
|
+
content: "",
|
|
406
|
+
finish: "tool-calls",
|
|
407
|
+
toolCalls: snapshot.toolCalls.map(({ toolCallId, toolName, args }) => ({
|
|
408
|
+
toolCallId,
|
|
409
|
+
toolName,
|
|
410
|
+
args
|
|
411
|
+
})),
|
|
412
|
+
createdAt
|
|
413
|
+
};
|
|
414
|
+
const toolMessages = snapshot.toolResults.map(
|
|
415
|
+
({ toolCallId, toolName, result }) => ({
|
|
416
|
+
id: options.toolMessageIds?.[toolCallId] ?? randomUUID2(),
|
|
417
|
+
role: "tool",
|
|
418
|
+
content: typeof result === "string" ? result : JSON.stringify(result),
|
|
419
|
+
toolCallId,
|
|
420
|
+
toolName,
|
|
421
|
+
result,
|
|
422
|
+
createdAt
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
return {
|
|
426
|
+
startBoundary: createAgentTurnBoundaryEvent("step-commit-start", {
|
|
427
|
+
step,
|
|
428
|
+
pendingToolCallCount: snapshot.toolCalls.length
|
|
429
|
+
}),
|
|
430
|
+
finishBoundary: createAgentTurnBoundaryEvent("step-commit-finish", {
|
|
431
|
+
step
|
|
432
|
+
}),
|
|
433
|
+
messages: [assistantMessage, ...toolMessages]
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function createAgentTurnInputCommit(options) {
|
|
437
|
+
const message = {
|
|
438
|
+
id: options.id ?? randomUUID2(),
|
|
439
|
+
role: "user",
|
|
440
|
+
content: options.content,
|
|
441
|
+
createdAt: options.createdAt ?? /* @__PURE__ */ new Date(),
|
|
442
|
+
...options.system ? { system: options.system } : {}
|
|
443
|
+
};
|
|
444
|
+
return {
|
|
445
|
+
startBoundary: createAgentTurnBoundaryEvent("input-commit-start", {
|
|
446
|
+
messageRole: "user"
|
|
447
|
+
}),
|
|
448
|
+
finishBoundary: createAgentTurnBoundaryEvent("input-commit-finish", {
|
|
449
|
+
messageRole: "user"
|
|
450
|
+
}),
|
|
451
|
+
messages: [message]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function createAgentTurnInterventionCommit(options) {
|
|
455
|
+
const message = {
|
|
456
|
+
id: options.id,
|
|
457
|
+
role: "user",
|
|
458
|
+
content: options.content,
|
|
459
|
+
createdAt: options.createdAt ?? /* @__PURE__ */ new Date()
|
|
460
|
+
};
|
|
461
|
+
return {
|
|
462
|
+
startBoundary: createAgentTurnBoundaryEvent("intervention-commit-start", {
|
|
463
|
+
messageRole: "user"
|
|
464
|
+
}),
|
|
465
|
+
finishBoundary: createAgentTurnBoundaryEvent("intervention-commit-finish", {
|
|
466
|
+
messageRole: "user"
|
|
467
|
+
}),
|
|
468
|
+
messages: [message]
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function createAgentTurnOutputCommit(options) {
|
|
472
|
+
if (!options.text) {
|
|
473
|
+
return void 0;
|
|
474
|
+
}
|
|
475
|
+
const message = {
|
|
476
|
+
id: options.id ?? randomUUID2(),
|
|
477
|
+
role: "assistant",
|
|
478
|
+
content: options.text,
|
|
479
|
+
...options.usage ? { tokens: options.usage } : {},
|
|
480
|
+
createdAt: options.createdAt ?? /* @__PURE__ */ new Date()
|
|
481
|
+
};
|
|
482
|
+
return {
|
|
483
|
+
startBoundary: createAgentTurnBoundaryEvent("output-commit-start", {
|
|
484
|
+
messageRole: "assistant"
|
|
485
|
+
}),
|
|
486
|
+
finishBoundary: createAgentTurnBoundaryEvent("output-commit-finish", {
|
|
487
|
+
messageRole: "assistant"
|
|
488
|
+
}),
|
|
489
|
+
messages: [message]
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/runtime/turn-engine/engine.ts
|
|
494
|
+
var AgentTurnEngine = class {
|
|
495
|
+
turnState;
|
|
496
|
+
pendingToolCalls = /* @__PURE__ */ new Map();
|
|
497
|
+
pendingToolResults = /* @__PURE__ */ new Map();
|
|
498
|
+
getToolReplayPolicy;
|
|
499
|
+
constructor(options) {
|
|
500
|
+
this.turnState = createAgentTurnState({
|
|
501
|
+
sessionId: options.sessionId,
|
|
502
|
+
startedAt: options.startedAt
|
|
503
|
+
});
|
|
504
|
+
this.getToolReplayPolicy = options.getToolReplayPolicy;
|
|
505
|
+
}
|
|
506
|
+
getState() {
|
|
507
|
+
return structuredClone(this.turnState);
|
|
508
|
+
}
|
|
509
|
+
hasPendingToolCalls() {
|
|
510
|
+
return this.pendingToolCalls.size > 0;
|
|
511
|
+
}
|
|
512
|
+
createStepCommitSnapshot() {
|
|
513
|
+
if (this.pendingToolCalls.size === 0) {
|
|
514
|
+
return void 0;
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
toolCalls: Array.from(this.pendingToolCalls.entries()).map(
|
|
518
|
+
([toolCallId, { toolName, input, replayPolicy }]) => ({
|
|
519
|
+
toolCallId,
|
|
520
|
+
toolName,
|
|
521
|
+
args: input,
|
|
522
|
+
...replayPolicy ? { replayPolicy } : {}
|
|
523
|
+
})
|
|
524
|
+
),
|
|
525
|
+
toolResults: Array.from(this.pendingToolCalls.entries()).map(([toolCallId, { toolName, replayPolicy }]) => {
|
|
526
|
+
const resultData = this.pendingToolResults.get(toolCallId);
|
|
527
|
+
if (!resultData) {
|
|
528
|
+
return void 0;
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
toolCallId,
|
|
532
|
+
toolName,
|
|
533
|
+
result: resultData.result,
|
|
534
|
+
...replayPolicy ? { replayPolicy } : {}
|
|
535
|
+
};
|
|
536
|
+
}).filter((result) => result !== void 0)
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
recordEvent(event, updatedAt) {
|
|
540
|
+
const toolReplayPolicy = "toolName" in event ? this.getToolReplayPolicy?.(event.toolName) : void 0;
|
|
541
|
+
this.turnState = advanceAgentTurnState(this.turnState, event, updatedAt, {
|
|
542
|
+
toolReplayPolicy
|
|
543
|
+
});
|
|
544
|
+
switch (event.type) {
|
|
545
|
+
case "tool-start":
|
|
546
|
+
this.pendingToolCalls.set(event.toolCallId, {
|
|
547
|
+
toolName: event.toolName,
|
|
548
|
+
input: event.input,
|
|
549
|
+
replayPolicy: toolReplayPolicy
|
|
550
|
+
});
|
|
551
|
+
break;
|
|
552
|
+
case "tool-result":
|
|
553
|
+
this.pendingToolResults.set(event.toolCallId, {
|
|
554
|
+
toolName: event.toolName,
|
|
555
|
+
result: event.result,
|
|
556
|
+
replayPolicy: toolReplayPolicy
|
|
557
|
+
});
|
|
558
|
+
break;
|
|
559
|
+
case "tool-error":
|
|
560
|
+
this.pendingToolResults.set(event.toolCallId, {
|
|
561
|
+
toolName: event.toolName,
|
|
562
|
+
result: `Error: ${event.error}`,
|
|
563
|
+
replayPolicy: toolReplayPolicy
|
|
564
|
+
});
|
|
565
|
+
break;
|
|
566
|
+
default:
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
return this.getState();
|
|
570
|
+
}
|
|
571
|
+
createBoundaryEvent(boundary, metadata = {}) {
|
|
572
|
+
return createAgentTurnBoundaryEvent(boundary, metadata);
|
|
573
|
+
}
|
|
574
|
+
createInputCommit(options) {
|
|
575
|
+
return createAgentTurnInputCommit(options);
|
|
576
|
+
}
|
|
577
|
+
createInterventionCommit(options) {
|
|
578
|
+
return createAgentTurnInterventionCommit(options);
|
|
579
|
+
}
|
|
580
|
+
consumeStepCommit(step) {
|
|
581
|
+
const snapshot = this.createStepCommitSnapshot();
|
|
582
|
+
const batch = snapshot ? createAgentTurnStepCommitBatch(step, snapshot) : void 0;
|
|
583
|
+
if (!batch) {
|
|
584
|
+
return void 0;
|
|
585
|
+
}
|
|
586
|
+
this.pendingToolCalls.clear();
|
|
587
|
+
this.pendingToolResults.clear();
|
|
588
|
+
return batch;
|
|
589
|
+
}
|
|
590
|
+
createOutputCommit(options) {
|
|
591
|
+
return createAgentTurnOutputCommit(options);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
function createAgentTurnEngine(options) {
|
|
595
|
+
return new AgentTurnEngine(options);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// src/runtime/turn-runner/prepare.ts
|
|
599
|
+
function prepareModelStep(options) {
|
|
600
|
+
const modelMessages = Array.from(options.toModelMessages(options.messages));
|
|
601
|
+
return {
|
|
602
|
+
step: options.step,
|
|
603
|
+
messages: options.messages,
|
|
604
|
+
modelMessages,
|
|
605
|
+
llmInput: {
|
|
606
|
+
sessionID: options.sessionId,
|
|
607
|
+
model: options.config.model,
|
|
608
|
+
system: options.systemPrompts,
|
|
609
|
+
messages: modelMessages,
|
|
610
|
+
abort: options.abort,
|
|
611
|
+
tools: options.tools,
|
|
612
|
+
mcpTools: options.mcpTools,
|
|
613
|
+
cwd: options.config.cwd,
|
|
614
|
+
host: options.host,
|
|
615
|
+
temperature: options.config.temperature,
|
|
616
|
+
topP: options.config.topP,
|
|
617
|
+
maxOutputTokens: options.config.maxOutputTokens,
|
|
618
|
+
maxSteps: options.config.maxSteps,
|
|
619
|
+
reasoningLevel: options.reasoningLevel,
|
|
620
|
+
customStreamProvider: options.config.streamProvider,
|
|
621
|
+
turnTracker: options.turnTracker,
|
|
622
|
+
intervention: options.intervention,
|
|
623
|
+
middleware: options.middleware,
|
|
624
|
+
toolExecutionMode: options.toolExecutionMode,
|
|
625
|
+
telemetry: options.config.telemetry
|
|
626
|
+
},
|
|
627
|
+
processor: {
|
|
628
|
+
maxSteps: options.config.maxSteps,
|
|
629
|
+
doomLoopThreshold: options.config.doomLoopThreshold,
|
|
630
|
+
enforceDoomLoop: options.config.enforceDoomLoop,
|
|
631
|
+
onDoomLoop: options.config.onDoomLoop,
|
|
632
|
+
contextTokenLimit: options.config.contextWindow ? options.config.contextWindow - DEFAULT_CONTEXT_LIMITS.reserveTokens : void 0
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// src/execution/llm/toolset.ts
|
|
638
|
+
import { tool, zodSchema } from "ai";
|
|
639
|
+
async function buildToolSet(options) {
|
|
640
|
+
const toolSet = {};
|
|
641
|
+
const executionMode = options.executionMode ?? "auto";
|
|
642
|
+
for (const [id, info] of Object.entries(options.tools)) {
|
|
643
|
+
const initialized = await info.init({ cwd: options.cwd });
|
|
644
|
+
toolSet[id] = executionMode === "auto" ? tool({
|
|
645
|
+
description: initialized.description,
|
|
646
|
+
inputSchema: zodSchema(initialized.parameters),
|
|
647
|
+
execute: async (params) => (await executeAgentToolCall({
|
|
648
|
+
toolName: id,
|
|
649
|
+
tool: info,
|
|
650
|
+
params,
|
|
651
|
+
cwd: options.cwd,
|
|
652
|
+
abort: options.abort,
|
|
653
|
+
sessionID: options.sessionID,
|
|
654
|
+
messageID: options.messageID,
|
|
655
|
+
...options.host ? { host: options.host } : {},
|
|
656
|
+
...options.turnTracker ? { turnTracker: options.turnTracker } : {},
|
|
657
|
+
...options.middleware ? { middleware: options.middleware } : {}
|
|
658
|
+
})).output
|
|
659
|
+
}) : tool({
|
|
660
|
+
description: initialized.description,
|
|
661
|
+
inputSchema: zodSchema(initialized.parameters)
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
return toolSet;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/execution/llm/stream.ts
|
|
668
|
+
import {
|
|
669
|
+
stepCountIs,
|
|
670
|
+
streamText
|
|
671
|
+
} from "ai";
|
|
672
|
+
|
|
673
|
+
// src/errors/classify.ts
|
|
674
|
+
function isRetryableCategory(category) {
|
|
675
|
+
switch (category) {
|
|
676
|
+
case "rate_limit":
|
|
677
|
+
case "overloaded":
|
|
678
|
+
case "network":
|
|
679
|
+
case "timeout":
|
|
680
|
+
return true;
|
|
681
|
+
case "auth":
|
|
682
|
+
case "invalid_request":
|
|
683
|
+
case "context_overflow":
|
|
684
|
+
case "content_filter":
|
|
685
|
+
case "cancelled":
|
|
686
|
+
case "unknown":
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function classifyFromStatusAndMessage(status, message) {
|
|
691
|
+
const lowerMessage = message.toLowerCase();
|
|
692
|
+
if (status) {
|
|
693
|
+
if (status === 429) return "rate_limit";
|
|
694
|
+
if (status === 401 || status === 403) return "auth";
|
|
695
|
+
if (status === 400) {
|
|
696
|
+
if (lowerMessage.includes("context") || lowerMessage.includes("token")) {
|
|
697
|
+
return "context_overflow";
|
|
698
|
+
}
|
|
699
|
+
return "invalid_request";
|
|
700
|
+
}
|
|
701
|
+
if (status === 503 || status === 502) return "overloaded";
|
|
702
|
+
if (status >= 500) return "network";
|
|
703
|
+
}
|
|
704
|
+
if (lowerMessage.includes("rate") && lowerMessage.includes("limit")) {
|
|
705
|
+
return "rate_limit";
|
|
706
|
+
}
|
|
707
|
+
if (lowerMessage.includes("overload") || lowerMessage.includes("capacity")) {
|
|
708
|
+
return "overloaded";
|
|
709
|
+
}
|
|
710
|
+
if (lowerMessage.includes("too_many_requests")) {
|
|
711
|
+
return "rate_limit";
|
|
712
|
+
}
|
|
713
|
+
if (lowerMessage.includes("unauthorized") || lowerMessage.includes("invalid api key")) {
|
|
714
|
+
return "auth";
|
|
715
|
+
}
|
|
716
|
+
if (lowerMessage.includes("context") && lowerMessage.includes("length")) {
|
|
717
|
+
return "context_overflow";
|
|
718
|
+
}
|
|
719
|
+
if (lowerMessage.includes("content") && lowerMessage.includes("filter")) {
|
|
720
|
+
return "content_filter";
|
|
721
|
+
}
|
|
722
|
+
if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out")) {
|
|
723
|
+
return "timeout";
|
|
724
|
+
}
|
|
725
|
+
if (lowerMessage.includes("network") || lowerMessage.includes("econnrefused") || lowerMessage.includes("econnreset")) {
|
|
726
|
+
return "network";
|
|
727
|
+
}
|
|
728
|
+
return "unknown";
|
|
729
|
+
}
|
|
730
|
+
function parseRetryDelay(headers) {
|
|
731
|
+
const retryAfterMs = headers["retry-after-ms"];
|
|
732
|
+
if (retryAfterMs) {
|
|
733
|
+
const parsed = parseFloat(retryAfterMs);
|
|
734
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
735
|
+
return parsed;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const retryAfter = headers["retry-after"];
|
|
739
|
+
if (retryAfter) {
|
|
740
|
+
const seconds = parseFloat(retryAfter);
|
|
741
|
+
if (!Number.isNaN(seconds) && seconds > 0) {
|
|
742
|
+
return Math.ceil(seconds * 1e3);
|
|
743
|
+
}
|
|
744
|
+
const dateMs = Date.parse(retryAfter);
|
|
745
|
+
if (!Number.isNaN(dateMs)) {
|
|
746
|
+
const delayMs = dateMs - Date.now();
|
|
747
|
+
if (delayMs > 0) {
|
|
748
|
+
return Math.ceil(delayMs);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return void 0;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/errors/extract.ts
|
|
756
|
+
function extractFromAISDKError(error) {
|
|
757
|
+
const result = {};
|
|
758
|
+
const anyError = error;
|
|
759
|
+
if (typeof anyError.status === "number") {
|
|
760
|
+
result.status = anyError.status;
|
|
761
|
+
} else if (typeof anyError.statusCode === "number") {
|
|
762
|
+
result.status = anyError.statusCode;
|
|
763
|
+
}
|
|
764
|
+
if (anyError.responseHeaders && typeof anyError.responseHeaders === "object") {
|
|
765
|
+
result.headers = anyError.responseHeaders;
|
|
766
|
+
} else if (anyError.headers && typeof anyError.headers === "object") {
|
|
767
|
+
result.headers = anyError.headers;
|
|
768
|
+
}
|
|
769
|
+
if (anyError.data && typeof anyError.data === "object") {
|
|
770
|
+
const data = anyError.data;
|
|
771
|
+
if (data.type === "error" && typeof data.error === "object") {
|
|
772
|
+
const innerError = data.error;
|
|
773
|
+
if (innerError.type === "too_many_requests") {
|
|
774
|
+
result.category = "rate_limit";
|
|
775
|
+
} else if (innerError.type === "overloaded") {
|
|
776
|
+
result.category = "overloaded";
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (typeof data.isRetryable === "boolean" && !data.isRetryable && !result.category) {
|
|
780
|
+
result.category = "invalid_request";
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return result;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/errors/llm-error.ts
|
|
787
|
+
var LLMError = class _LLMError extends Error {
|
|
788
|
+
category;
|
|
789
|
+
status;
|
|
790
|
+
headers;
|
|
791
|
+
provider;
|
|
792
|
+
model;
|
|
793
|
+
isRetryable;
|
|
794
|
+
retryDelayMs;
|
|
795
|
+
constructor(options) {
|
|
796
|
+
super(options.message, { cause: options.cause });
|
|
797
|
+
this.name = "LLMError";
|
|
798
|
+
this.status = options.status;
|
|
799
|
+
this.headers = options.headers;
|
|
800
|
+
this.provider = options.provider;
|
|
801
|
+
this.model = options.model;
|
|
802
|
+
this.category = options.category ?? classifyFromStatusAndMessage(
|
|
803
|
+
options.status,
|
|
804
|
+
options.message
|
|
805
|
+
);
|
|
806
|
+
this.isRetryable = isRetryableCategory(this.category);
|
|
807
|
+
this.retryDelayMs = this.headers ? parseRetryDelay(this.headers) : void 0;
|
|
808
|
+
}
|
|
809
|
+
static from(error, context) {
|
|
810
|
+
if (error instanceof _LLMError) {
|
|
811
|
+
return error;
|
|
812
|
+
}
|
|
813
|
+
if (error instanceof Error) {
|
|
814
|
+
if (error.name === "AbortError" || error.message.includes("abort")) {
|
|
815
|
+
return new _LLMError({
|
|
816
|
+
message: error.message,
|
|
817
|
+
category: "cancelled",
|
|
818
|
+
cause: error,
|
|
819
|
+
...context
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
return new _LLMError({
|
|
823
|
+
message: error.message,
|
|
824
|
+
cause: error,
|
|
825
|
+
...extractFromAISDKError(error),
|
|
826
|
+
...context
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
return new _LLMError({
|
|
830
|
+
message: String(error),
|
|
831
|
+
category: "unknown",
|
|
832
|
+
...context
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
get description() {
|
|
836
|
+
const parts = [this.message];
|
|
837
|
+
if (this.provider) parts.unshift(`[${this.provider}]`);
|
|
838
|
+
if (this.status) parts.push(`(HTTP ${this.status})`);
|
|
839
|
+
if (this.isRetryable && this.retryDelayMs) {
|
|
840
|
+
parts.push(`retry in ${Math.ceil(this.retryDelayMs / 1e3)}s`);
|
|
841
|
+
}
|
|
842
|
+
return parts.join(" ");
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
// src/errors/utils.ts
|
|
847
|
+
function isRetryable(error) {
|
|
848
|
+
if (error instanceof LLMError) {
|
|
849
|
+
return error.isRetryable;
|
|
850
|
+
}
|
|
851
|
+
return LLMError.from(error).isRetryable;
|
|
852
|
+
}
|
|
853
|
+
function getRetryDelay(error) {
|
|
854
|
+
if (error instanceof LLMError) {
|
|
855
|
+
return error.isRetryable ? error.retryDelayMs : void 0;
|
|
856
|
+
}
|
|
857
|
+
const llmError = LLMError.from(error);
|
|
858
|
+
return llmError.isRetryable ? llmError.retryDelayMs : void 0;
|
|
859
|
+
}
|
|
860
|
+
function getErrorCategory(error) {
|
|
861
|
+
if (error instanceof LLMError) {
|
|
862
|
+
return error.category;
|
|
863
|
+
}
|
|
864
|
+
return LLMError.from(error).category;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/retry.ts
|
|
868
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
869
|
+
maxAttempts: 3,
|
|
870
|
+
initialDelayMs: 2e3,
|
|
871
|
+
backoffFactor: 2,
|
|
872
|
+
maxDelayMs: 3e4,
|
|
873
|
+
jitter: true
|
|
874
|
+
};
|
|
875
|
+
function createRetryState() {
|
|
876
|
+
return {
|
|
877
|
+
attempt: 0,
|
|
878
|
+
errors: [],
|
|
879
|
+
canRetry: true,
|
|
880
|
+
nextDelayMs: void 0
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
function calculateDelay(attempt, error, config) {
|
|
884
|
+
if (error?.retryDelayMs) {
|
|
885
|
+
return error.retryDelayMs;
|
|
886
|
+
}
|
|
887
|
+
const baseDelay = config.initialDelayMs * Math.pow(config.backoffFactor, attempt - 1);
|
|
888
|
+
const cappedDelay = Math.min(baseDelay, config.maxDelayMs);
|
|
889
|
+
if (config.jitter) {
|
|
890
|
+
const jitterRange = cappedDelay * 0.25;
|
|
891
|
+
const jitter = (Math.random() - 0.5) * 2 * jitterRange;
|
|
892
|
+
return Math.max(0, Math.round(cappedDelay + jitter));
|
|
893
|
+
}
|
|
894
|
+
return Math.round(cappedDelay);
|
|
895
|
+
}
|
|
896
|
+
async function sleep(ms, signal) {
|
|
897
|
+
return new Promise((resolve, reject) => {
|
|
898
|
+
if (signal?.aborted) {
|
|
899
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const timeoutId = setTimeout(() => {
|
|
903
|
+
cleanup();
|
|
904
|
+
resolve();
|
|
905
|
+
}, ms);
|
|
906
|
+
const abortHandler = () => {
|
|
907
|
+
clearTimeout(timeoutId);
|
|
908
|
+
cleanup();
|
|
909
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
910
|
+
};
|
|
911
|
+
const cleanup = () => {
|
|
912
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
913
|
+
};
|
|
914
|
+
signal?.addEventListener("abort", abortHandler, { once: true });
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
async function withRetry(fn, config, signal) {
|
|
918
|
+
const mergedConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
919
|
+
const state = createRetryState();
|
|
920
|
+
while (true) {
|
|
921
|
+
state.attempt++;
|
|
922
|
+
try {
|
|
923
|
+
return await fn(state.attempt);
|
|
924
|
+
} catch (error) {
|
|
925
|
+
const llmError = LLMError.from(error);
|
|
926
|
+
state.errors.push(llmError);
|
|
927
|
+
const shouldRetry2 = state.attempt < mergedConfig.maxAttempts && isRetryable(llmError) && !signal?.aborted;
|
|
928
|
+
if (!shouldRetry2) {
|
|
929
|
+
throw new LLMError({
|
|
930
|
+
message: `Failed after ${state.attempt} attempt(s): ${llmError.message}`,
|
|
931
|
+
category: llmError.category,
|
|
932
|
+
status: llmError.status,
|
|
933
|
+
headers: llmError.headers,
|
|
934
|
+
provider: llmError.provider,
|
|
935
|
+
model: llmError.model,
|
|
936
|
+
cause: llmError
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
const delayMs = calculateDelay(state.attempt, llmError, mergedConfig);
|
|
940
|
+
state.nextDelayMs = delayMs;
|
|
941
|
+
config?.onRetry?.(state.attempt, delayMs, llmError);
|
|
942
|
+
await sleep(delayMs, signal);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function createRetryHandler(options) {
|
|
947
|
+
const config = options ?? {};
|
|
948
|
+
const signal = options?.signal;
|
|
949
|
+
return async (createStream) => {
|
|
950
|
+
return withRetry(createStream, config, signal);
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function shouldRetry(error, attempt, maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts) {
|
|
954
|
+
if (attempt >= maxAttempts) return false;
|
|
955
|
+
return isRetryable(error);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/execution/llm/types.ts
|
|
959
|
+
var OUTPUT_TOKEN_MAX = 32e3;
|
|
960
|
+
|
|
961
|
+
// src/execution/llm/stream.ts
|
|
962
|
+
async function createCustomStream(input) {
|
|
963
|
+
const system = input.system.filter(Boolean).join("\n");
|
|
964
|
+
return input.customStreamProvider({
|
|
965
|
+
system,
|
|
966
|
+
messages: input.messages,
|
|
967
|
+
abortSignal: input.abort,
|
|
968
|
+
maxSteps: input.maxSteps
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
function getModelInfo(input) {
|
|
972
|
+
return {
|
|
973
|
+
provider: typeof input.model === "object" && "provider" in input.model ? String(input.model.provider) : void 0,
|
|
974
|
+
model: typeof input.model === "object" && "modelId" in input.model ? String(input.model.modelId) : String(input.model)
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async function callStreamTextWithOtelContext(options) {
|
|
978
|
+
const { input, allTools, system, providerOptions } = options;
|
|
979
|
+
const callStreamText = () => streamText({
|
|
980
|
+
model: input.model,
|
|
981
|
+
system,
|
|
982
|
+
messages: input.messages,
|
|
983
|
+
tools: allTools,
|
|
984
|
+
stopWhen: stepCountIs(input.maxSteps ?? 50),
|
|
985
|
+
maxOutputTokens: input.maxOutputTokens ?? OUTPUT_TOKEN_MAX,
|
|
986
|
+
temperature: input.temperature,
|
|
987
|
+
topP: input.topP,
|
|
988
|
+
abortSignal: input.abort,
|
|
989
|
+
providerOptions,
|
|
990
|
+
experimental_telemetry: input.telemetry,
|
|
991
|
+
prepareStep: input.intervention ? async ({ messages }) => {
|
|
992
|
+
const pending = input.intervention.drainImmediate();
|
|
993
|
+
if (pending.length === 0) {
|
|
994
|
+
return void 0;
|
|
995
|
+
}
|
|
996
|
+
const injected = pending.map((item) => ({
|
|
997
|
+
role: "user",
|
|
998
|
+
content: item.message
|
|
999
|
+
}));
|
|
1000
|
+
for (const item of pending) {
|
|
1001
|
+
input.intervention.onApplied?.(item);
|
|
1002
|
+
}
|
|
1003
|
+
return { messages: [...messages, ...injected] };
|
|
1004
|
+
} : void 0,
|
|
1005
|
+
onStepFinish: async (step) => {
|
|
1006
|
+
if (!input.onStepFinish) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
await input.onStepFinish({
|
|
1010
|
+
toolResults: step.toolResults?.map((toolResult) => ({
|
|
1011
|
+
toolName: toolResult.toolName,
|
|
1012
|
+
toolCallId: toolResult.toolCallId,
|
|
1013
|
+
output: toolResult.output
|
|
1014
|
+
})),
|
|
1015
|
+
usage: step.usage,
|
|
1016
|
+
finishReason: step.finishReason
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
const otelCtx = input.middleware?.getOtelContext(input.sessionID);
|
|
1021
|
+
if (!otelCtx) {
|
|
1022
|
+
return callStreamText();
|
|
1023
|
+
}
|
|
1024
|
+
try {
|
|
1025
|
+
const otelApi = await import("@opentelemetry/api");
|
|
1026
|
+
return otelApi.context.with(
|
|
1027
|
+
otelCtx,
|
|
1028
|
+
callStreamText
|
|
1029
|
+
);
|
|
1030
|
+
} catch {
|
|
1031
|
+
return callStreamText();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
async function stream(input) {
|
|
1035
|
+
const messageID = crypto.randomUUID();
|
|
1036
|
+
const system = input.system.filter(Boolean).join("\n");
|
|
1037
|
+
if (input.customStreamProvider) {
|
|
1038
|
+
const runCustomStream = async () => await createCustomStream(input);
|
|
1039
|
+
if (!input.retry || input.retry.maxAttempts === 0) {
|
|
1040
|
+
return await runCustomStream();
|
|
1041
|
+
}
|
|
1042
|
+
return await withRetry(
|
|
1043
|
+
async () => await runCustomStream(),
|
|
1044
|
+
input.retry,
|
|
1045
|
+
input.abort
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
const toolSet = await buildToolSet({
|
|
1049
|
+
tools: input.tools,
|
|
1050
|
+
cwd: input.cwd,
|
|
1051
|
+
sessionID: input.sessionID,
|
|
1052
|
+
messageID,
|
|
1053
|
+
abort: input.abort,
|
|
1054
|
+
turnTracker: input.turnTracker,
|
|
1055
|
+
host: input.host,
|
|
1056
|
+
middleware: input.middleware,
|
|
1057
|
+
executionMode: input.toolExecutionMode
|
|
1058
|
+
});
|
|
1059
|
+
const allTools = {
|
|
1060
|
+
...toolSet,
|
|
1061
|
+
...input.mcpTools ?? {}
|
|
1062
|
+
};
|
|
1063
|
+
const providerOptions = input.reasoningLevel ? buildReasoningOptionsSync(input.model, input.reasoningLevel) : void 0;
|
|
1064
|
+
const modelInfo = getModelInfo(input);
|
|
1065
|
+
const createStream = async () => {
|
|
1066
|
+
try {
|
|
1067
|
+
return await callStreamTextWithOtelContext({
|
|
1068
|
+
input,
|
|
1069
|
+
allTools,
|
|
1070
|
+
system,
|
|
1071
|
+
providerOptions
|
|
1072
|
+
});
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
throw LLMError.from(error, modelInfo);
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
if (!input.retry || input.retry.maxAttempts === 0) {
|
|
1078
|
+
return await createStream();
|
|
1079
|
+
}
|
|
1080
|
+
return await withRetry(
|
|
1081
|
+
async () => await createStream(),
|
|
1082
|
+
input.retry,
|
|
1083
|
+
input.abort
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
async function streamOnce(input) {
|
|
1087
|
+
return await stream({ ...input, retry: void 0 });
|
|
1088
|
+
}
|
|
1089
|
+
async function streamStep(input) {
|
|
1090
|
+
return await stream({
|
|
1091
|
+
...input,
|
|
1092
|
+
maxSteps: 1
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/execution/llm/index.ts
|
|
1097
|
+
var LLM = {
|
|
1098
|
+
buildToolSet,
|
|
1099
|
+
stream,
|
|
1100
|
+
streamOnce,
|
|
1101
|
+
streamStep
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
// src/execution/processor/doom-loop.ts
|
|
1105
|
+
var DEFAULT_DOOM_LOOP_THRESHOLD = 3;
|
|
1106
|
+
var DoomLoopError = class extends Error {
|
|
1107
|
+
toolName;
|
|
1108
|
+
repeatCount;
|
|
1109
|
+
input;
|
|
1110
|
+
constructor(toolName, repeatCount, input) {
|
|
1111
|
+
super(
|
|
1112
|
+
`Doom loop detected: "${toolName}" called ${repeatCount} times with identical input`
|
|
1113
|
+
);
|
|
1114
|
+
this.name = "DoomLoopError";
|
|
1115
|
+
this.toolName = toolName;
|
|
1116
|
+
this.repeatCount = repeatCount;
|
|
1117
|
+
this.input = input;
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
function hashInput(input) {
|
|
1121
|
+
return JSON.stringify(input);
|
|
1122
|
+
}
|
|
1123
|
+
async function recordToolCallAndCheckDoomLoop(options) {
|
|
1124
|
+
const {
|
|
1125
|
+
recentToolCalls,
|
|
1126
|
+
toolName,
|
|
1127
|
+
input,
|
|
1128
|
+
processorOptions,
|
|
1129
|
+
threshold,
|
|
1130
|
+
emitEvent
|
|
1131
|
+
} = options;
|
|
1132
|
+
recentToolCalls.push({
|
|
1133
|
+
toolName,
|
|
1134
|
+
inputHash: hashInput(input)
|
|
1135
|
+
});
|
|
1136
|
+
if (recentToolCalls.length > threshold) {
|
|
1137
|
+
recentToolCalls.shift();
|
|
1138
|
+
}
|
|
1139
|
+
if (recentToolCalls.length !== threshold) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
const first = recentToolCalls[0];
|
|
1143
|
+
const isLoop = recentToolCalls.every(
|
|
1144
|
+
(call) => call.toolName === first.toolName && call.inputHash === first.inputHash
|
|
1145
|
+
);
|
|
1146
|
+
if (!isLoop) {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
if (processorOptions.rememberedDoomLoopTools?.has(toolName)) {
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const doomError = new DoomLoopError(toolName, threshold, input);
|
|
1153
|
+
await emitEvent({
|
|
1154
|
+
type: "doom-loop",
|
|
1155
|
+
toolName,
|
|
1156
|
+
repeatCount: threshold
|
|
1157
|
+
});
|
|
1158
|
+
if (processorOptions.onDoomLoop) {
|
|
1159
|
+
const action = await processorOptions.onDoomLoop({
|
|
1160
|
+
tool: toolName,
|
|
1161
|
+
repeatCount: threshold,
|
|
1162
|
+
input,
|
|
1163
|
+
sessionId: processorOptions.sessionID ?? "unknown"
|
|
1164
|
+
});
|
|
1165
|
+
switch (action) {
|
|
1166
|
+
case "allow":
|
|
1167
|
+
return;
|
|
1168
|
+
case "remember":
|
|
1169
|
+
processorOptions.rememberedDoomLoopTools?.add(toolName);
|
|
1170
|
+
return;
|
|
1171
|
+
case "deny":
|
|
1172
|
+
throw doomError;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
if (processorOptions.enforceDoomLoop ?? true) {
|
|
1176
|
+
throw doomError;
|
|
1177
|
+
}
|
|
1178
|
+
options.warn?.(`[Processor] ${doomError.message}`);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/execution/processor/overflow.ts
|
|
1182
|
+
var ContextOverflowError = class extends Error {
|
|
1183
|
+
inputTokens;
|
|
1184
|
+
limit;
|
|
1185
|
+
constructor(inputTokens, limit) {
|
|
1186
|
+
super(`Context overflow: ${inputTokens} tokens exceeds limit of ${limit}`);
|
|
1187
|
+
this.name = "ContextOverflowError";
|
|
1188
|
+
this.inputTokens = inputTokens;
|
|
1189
|
+
this.limit = limit;
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
async function handleContextOverflow(options) {
|
|
1193
|
+
const limit = options.processorOptions.contextTokenLimit;
|
|
1194
|
+
const inputTokens = options.usage?.inputTokens;
|
|
1195
|
+
if (!limit || !inputTokens || inputTokens <= limit) {
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
await options.emitEvent({
|
|
1199
|
+
type: "context-overflow",
|
|
1200
|
+
inputTokens,
|
|
1201
|
+
limit
|
|
1202
|
+
});
|
|
1203
|
+
if (options.processorOptions.onContextOverflow) {
|
|
1204
|
+
await options.processorOptions.onContextOverflow(inputTokens, limit);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/execution/processor/process.ts
|
|
1209
|
+
async function processStream(stream2, options) {
|
|
1210
|
+
const { abort, onEvent } = options;
|
|
1211
|
+
const doomLoopThreshold = options.doomLoopThreshold ?? DEFAULT_DOOM_LOOP_THRESHOLD;
|
|
1212
|
+
const maxSteps = options.maxSteps ?? 50;
|
|
1213
|
+
let stepCount = options.currentStep ?? 1;
|
|
1214
|
+
let sawStartStep = false;
|
|
1215
|
+
let text = "";
|
|
1216
|
+
const toolResults = [];
|
|
1217
|
+
const recentToolCalls = [];
|
|
1218
|
+
let usage;
|
|
1219
|
+
let finishReason;
|
|
1220
|
+
let error;
|
|
1221
|
+
let hasToolCalls = false;
|
|
1222
|
+
await onEvent({ type: "step-start", step: stepCount, maxSteps });
|
|
1223
|
+
await onEvent({ type: "status", status: "processing" });
|
|
1224
|
+
try {
|
|
1225
|
+
for await (const rawChunk of stream2.fullStream) {
|
|
1226
|
+
const chunk = rawChunk;
|
|
1227
|
+
if (process.env.DEBUG_PROCESSOR) {
|
|
1228
|
+
process.stderr.write(`[processor] Chunk received: ${chunk.type}
|
|
1229
|
+
`);
|
|
1230
|
+
}
|
|
1231
|
+
abort.throwIfAborted();
|
|
1232
|
+
switch (chunk.type) {
|
|
1233
|
+
case "start-step":
|
|
1234
|
+
if (!sawStartStep) {
|
|
1235
|
+
sawStartStep = true;
|
|
1236
|
+
break;
|
|
1237
|
+
}
|
|
1238
|
+
stepCount++;
|
|
1239
|
+
await onEvent({ type: "step-start", step: stepCount, maxSteps });
|
|
1240
|
+
await onEvent({ type: "status", status: "processing" });
|
|
1241
|
+
break;
|
|
1242
|
+
case "text-start":
|
|
1243
|
+
await onEvent({ type: "text-start" });
|
|
1244
|
+
break;
|
|
1245
|
+
case "text-delta":
|
|
1246
|
+
text += chunk.text;
|
|
1247
|
+
await onEvent({ type: "text-delta", text: chunk.text });
|
|
1248
|
+
break;
|
|
1249
|
+
case "text-end":
|
|
1250
|
+
await onEvent({ type: "text-end" });
|
|
1251
|
+
break;
|
|
1252
|
+
case "reasoning-start":
|
|
1253
|
+
await onEvent({ type: "status", status: "reasoning" });
|
|
1254
|
+
await onEvent({ type: "reasoning-start", id: chunk.id });
|
|
1255
|
+
break;
|
|
1256
|
+
case "reasoning-delta":
|
|
1257
|
+
await onEvent({
|
|
1258
|
+
type: "reasoning-delta",
|
|
1259
|
+
id: chunk.id,
|
|
1260
|
+
text: chunk.text
|
|
1261
|
+
});
|
|
1262
|
+
break;
|
|
1263
|
+
case "reasoning-end":
|
|
1264
|
+
await onEvent({ type: "reasoning-end", id: chunk.id });
|
|
1265
|
+
break;
|
|
1266
|
+
case "tool-call":
|
|
1267
|
+
hasToolCalls = true;
|
|
1268
|
+
await onEvent({ type: "status", status: "calling-tool" });
|
|
1269
|
+
await onEvent({
|
|
1270
|
+
type: "tool-start",
|
|
1271
|
+
toolName: chunk.toolName,
|
|
1272
|
+
toolCallId: chunk.toolCallId,
|
|
1273
|
+
input: chunk.input
|
|
1274
|
+
});
|
|
1275
|
+
await recordToolCallAndCheckDoomLoop({
|
|
1276
|
+
recentToolCalls,
|
|
1277
|
+
toolName: chunk.toolName,
|
|
1278
|
+
input: chunk.input,
|
|
1279
|
+
processorOptions: options,
|
|
1280
|
+
threshold: doomLoopThreshold,
|
|
1281
|
+
emitEvent: onEvent,
|
|
1282
|
+
warn: (message) => process.stderr.write(`${message}
|
|
1283
|
+
`)
|
|
1284
|
+
});
|
|
1285
|
+
break;
|
|
1286
|
+
case "tool-result":
|
|
1287
|
+
toolResults.push({
|
|
1288
|
+
toolName: chunk.toolName,
|
|
1289
|
+
toolCallId: chunk.toolCallId,
|
|
1290
|
+
result: chunk.output
|
|
1291
|
+
});
|
|
1292
|
+
await onEvent({
|
|
1293
|
+
type: "tool-result",
|
|
1294
|
+
toolName: chunk.toolName,
|
|
1295
|
+
toolCallId: chunk.toolCallId,
|
|
1296
|
+
result: chunk.output
|
|
1297
|
+
});
|
|
1298
|
+
break;
|
|
1299
|
+
case "tool-error":
|
|
1300
|
+
await onEvent({
|
|
1301
|
+
type: "tool-error",
|
|
1302
|
+
toolName: chunk.toolName,
|
|
1303
|
+
toolCallId: chunk.toolCallId,
|
|
1304
|
+
error: String(chunk.error)
|
|
1305
|
+
});
|
|
1306
|
+
break;
|
|
1307
|
+
case "computer-call":
|
|
1308
|
+
hasToolCalls = true;
|
|
1309
|
+
await onEvent({ type: "status", status: "calling-tool" });
|
|
1310
|
+
await onEvent({
|
|
1311
|
+
type: "computer-call",
|
|
1312
|
+
callId: chunk.callId,
|
|
1313
|
+
action: chunk.action,
|
|
1314
|
+
pendingSafetyChecks: chunk.pendingSafetyChecks
|
|
1315
|
+
});
|
|
1316
|
+
break;
|
|
1317
|
+
case "finish-step":
|
|
1318
|
+
finishReason = chunk.finishReason;
|
|
1319
|
+
if (chunk.usage) {
|
|
1320
|
+
usage = {
|
|
1321
|
+
inputTokens: chunk.usage.inputTokens,
|
|
1322
|
+
outputTokens: chunk.usage.outputTokens,
|
|
1323
|
+
totalTokens: chunk.usage.totalTokens
|
|
1324
|
+
};
|
|
1325
|
+
await handleContextOverflow({
|
|
1326
|
+
usage,
|
|
1327
|
+
processorOptions: options,
|
|
1328
|
+
emitEvent: onEvent
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
await onEvent({
|
|
1332
|
+
type: "step-finish",
|
|
1333
|
+
step: stepCount,
|
|
1334
|
+
usage,
|
|
1335
|
+
finishReason
|
|
1336
|
+
});
|
|
1337
|
+
if (!sawStartStep) {
|
|
1338
|
+
stepCount++;
|
|
1339
|
+
}
|
|
1340
|
+
break;
|
|
1341
|
+
case "finish":
|
|
1342
|
+
if (chunk.totalUsage) {
|
|
1343
|
+
usage = {
|
|
1344
|
+
inputTokens: chunk.totalUsage.inputTokens,
|
|
1345
|
+
outputTokens: chunk.totalUsage.outputTokens,
|
|
1346
|
+
totalTokens: chunk.totalUsage.totalTokens
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
break;
|
|
1350
|
+
case "step-usage":
|
|
1351
|
+
usage = {
|
|
1352
|
+
inputTokens: chunk.usage.inputTokens,
|
|
1353
|
+
outputTokens: chunk.usage.outputTokens,
|
|
1354
|
+
totalTokens: chunk.usage.totalTokens
|
|
1355
|
+
};
|
|
1356
|
+
break;
|
|
1357
|
+
case "error":
|
|
1358
|
+
throw chunk.error;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
} catch (caught) {
|
|
1362
|
+
error = caught instanceof Error ? caught : new Error(String(caught));
|
|
1363
|
+
await onEvent({ type: "status", status: "error" });
|
|
1364
|
+
await onEvent({ type: "error", error });
|
|
1365
|
+
}
|
|
1366
|
+
if (!error) {
|
|
1367
|
+
await onEvent({ type: "status", status: "idle" });
|
|
1368
|
+
}
|
|
1369
|
+
let result = "continue";
|
|
1370
|
+
if (error || !hasToolCalls) {
|
|
1371
|
+
result = "stop";
|
|
1372
|
+
}
|
|
1373
|
+
return {
|
|
1374
|
+
result,
|
|
1375
|
+
text,
|
|
1376
|
+
toolResults,
|
|
1377
|
+
usage,
|
|
1378
|
+
finishReason,
|
|
1379
|
+
error
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// src/runtime/turn-runner/stream-step.ts
|
|
1384
|
+
async function* runModelStep(options) {
|
|
1385
|
+
const { preparedStep, turnEngine, applyCommitBatch } = options;
|
|
1386
|
+
const stream2 = await LLM.streamStep(preparedStep.llmInput);
|
|
1387
|
+
const eventQueue = [];
|
|
1388
|
+
let resolveNext = null;
|
|
1389
|
+
let streamDone = false;
|
|
1390
|
+
let streamError;
|
|
1391
|
+
const intervention = preparedStep.llmInput.intervention;
|
|
1392
|
+
const middleware = preparedStep.llmInput.middleware;
|
|
1393
|
+
if (intervention) {
|
|
1394
|
+
intervention.onApplied = (item) => {
|
|
1395
|
+
eventQueue.push({
|
|
1396
|
+
type: "intervention-applied",
|
|
1397
|
+
id: item.id,
|
|
1398
|
+
message: item.message
|
|
1399
|
+
});
|
|
1400
|
+
if (resolveNext) {
|
|
1401
|
+
resolveNext();
|
|
1402
|
+
resolveNext = null;
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
const processPromise = processStream(stream2, {
|
|
1407
|
+
sessionID: preparedStep.llmInput.sessionID,
|
|
1408
|
+
abort: preparedStep.llmInput.abort,
|
|
1409
|
+
currentStep: preparedStep.step,
|
|
1410
|
+
maxSteps: preparedStep.processor.maxSteps,
|
|
1411
|
+
doomLoopThreshold: preparedStep.processor.doomLoopThreshold ?? 3,
|
|
1412
|
+
enforceDoomLoop: preparedStep.processor.enforceDoomLoop ?? true,
|
|
1413
|
+
onDoomLoop: preparedStep.processor.onDoomLoop,
|
|
1414
|
+
rememberedDoomLoopTools: options.rememberedDoomLoopTools,
|
|
1415
|
+
contextTokenLimit: preparedStep.processor.contextTokenLimit,
|
|
1416
|
+
onContextOverflow: async () => {
|
|
1417
|
+
},
|
|
1418
|
+
onEvent: async (event) => {
|
|
1419
|
+
middleware?.emitEvent(event);
|
|
1420
|
+
eventQueue.push(event);
|
|
1421
|
+
if (resolveNext) {
|
|
1422
|
+
resolveNext();
|
|
1423
|
+
resolveNext = null;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}).then((result2) => {
|
|
1427
|
+
streamDone = true;
|
|
1428
|
+
if (resolveNext) {
|
|
1429
|
+
resolveNext();
|
|
1430
|
+
resolveNext = null;
|
|
1431
|
+
}
|
|
1432
|
+
return result2;
|
|
1433
|
+
}).catch((error) => {
|
|
1434
|
+
streamError = error instanceof Error ? error : new Error(String(error));
|
|
1435
|
+
streamDone = true;
|
|
1436
|
+
if (resolveNext) {
|
|
1437
|
+
resolveNext();
|
|
1438
|
+
resolveNext = null;
|
|
1439
|
+
}
|
|
1440
|
+
return null;
|
|
1441
|
+
});
|
|
1442
|
+
while (!streamDone || eventQueue.length > 0) {
|
|
1443
|
+
while (eventQueue.length > 0) {
|
|
1444
|
+
const event = eventQueue.shift();
|
|
1445
|
+
turnEngine.recordEvent(event, (/* @__PURE__ */ new Date()).toISOString());
|
|
1446
|
+
yield event;
|
|
1447
|
+
if (event.type === "intervention-applied") {
|
|
1448
|
+
yield* applyCommitBatch(
|
|
1449
|
+
turnEngine.createInterventionCommit({
|
|
1450
|
+
id: event.id,
|
|
1451
|
+
content: event.message
|
|
1452
|
+
})
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (!streamDone) {
|
|
1457
|
+
await new Promise((resolve) => {
|
|
1458
|
+
resolveNext = resolve;
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
if (streamError) {
|
|
1463
|
+
throw streamError;
|
|
1464
|
+
}
|
|
1465
|
+
const result = await processPromise;
|
|
1466
|
+
if (!result) {
|
|
1467
|
+
throw new Error(
|
|
1468
|
+
`Agent step ${preparedStep.step} produced no processor result`
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
return result;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// src/runtime/turn-runner/tool-batch.ts
|
|
1475
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1476
|
+
function cloneToolResultRecord(options) {
|
|
1477
|
+
return new Map(
|
|
1478
|
+
options.snapshot.toolResults.map((toolResult) => [
|
|
1479
|
+
toolResult.toolCallId,
|
|
1480
|
+
structuredClone(toolResult)
|
|
1481
|
+
])
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
function createToolBatchErrorResult(options) {
|
|
1485
|
+
return {
|
|
1486
|
+
type: "tool-error",
|
|
1487
|
+
toolName: options.toolName,
|
|
1488
|
+
toolCallId: options.toolCallId,
|
|
1489
|
+
error: options.errorMessage
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function createToolBatchResultEvent(options) {
|
|
1493
|
+
return {
|
|
1494
|
+
type: "tool-result",
|
|
1495
|
+
toolName: options.toolName,
|
|
1496
|
+
toolCallId: options.toolCallId,
|
|
1497
|
+
result: options.result
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
async function runToolBatch(options) {
|
|
1501
|
+
const toolResultsByCallId = cloneToolResultRecord(options);
|
|
1502
|
+
const events = [];
|
|
1503
|
+
let turnState = options.turnState ? structuredClone(options.turnState) : void 0;
|
|
1504
|
+
for (const toolCall of options.snapshot.toolCalls) {
|
|
1505
|
+
if (toolResultsByCallId.has(toolCall.toolCallId)) {
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
const tool2 = options.tools[toolCall.toolName];
|
|
1509
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1510
|
+
if (!tool2) {
|
|
1511
|
+
const errorMessage = `Tool '${toolCall.toolName}' is not registered`;
|
|
1512
|
+
const event = createToolBatchErrorResult({
|
|
1513
|
+
toolCallId: toolCall.toolCallId,
|
|
1514
|
+
toolName: toolCall.toolName,
|
|
1515
|
+
errorMessage,
|
|
1516
|
+
replayPolicy: toolCall.replayPolicy
|
|
1517
|
+
});
|
|
1518
|
+
events.push(event);
|
|
1519
|
+
toolResultsByCallId.set(toolCall.toolCallId, {
|
|
1520
|
+
toolCallId: toolCall.toolCallId,
|
|
1521
|
+
toolName: toolCall.toolName,
|
|
1522
|
+
result: `Error: ${errorMessage}`,
|
|
1523
|
+
...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
|
|
1524
|
+
});
|
|
1525
|
+
if (turnState) {
|
|
1526
|
+
turnState = advanceAgentTurnState(turnState, event, updatedAt, {
|
|
1527
|
+
...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
const executed = await executeAgentToolCall({
|
|
1534
|
+
toolName: toolCall.toolName,
|
|
1535
|
+
tool: tool2,
|
|
1536
|
+
params: toolCall.args,
|
|
1537
|
+
cwd: options.cwd,
|
|
1538
|
+
abort: options.abort,
|
|
1539
|
+
sessionID: options.sessionId,
|
|
1540
|
+
messageID: randomUUID3(),
|
|
1541
|
+
...options.host ? { host: options.host } : {},
|
|
1542
|
+
...options.turnTracker ? { turnTracker: options.turnTracker } : {},
|
|
1543
|
+
...options.middleware ? { middleware: options.middleware } : {}
|
|
1544
|
+
});
|
|
1545
|
+
const event = createToolBatchResultEvent({
|
|
1546
|
+
toolCallId: toolCall.toolCallId,
|
|
1547
|
+
toolName: toolCall.toolName,
|
|
1548
|
+
result: executed.output
|
|
1549
|
+
});
|
|
1550
|
+
events.push(event);
|
|
1551
|
+
toolResultsByCallId.set(toolCall.toolCallId, {
|
|
1552
|
+
toolCallId: toolCall.toolCallId,
|
|
1553
|
+
toolName: toolCall.toolName,
|
|
1554
|
+
result: executed.output,
|
|
1555
|
+
...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
|
|
1556
|
+
});
|
|
1557
|
+
if (turnState) {
|
|
1558
|
+
turnState = advanceAgentTurnState(turnState, event, updatedAt, {
|
|
1559
|
+
...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
} catch (error) {
|
|
1563
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1564
|
+
const event = createToolBatchErrorResult({
|
|
1565
|
+
toolCallId: toolCall.toolCallId,
|
|
1566
|
+
toolName: toolCall.toolName,
|
|
1567
|
+
errorMessage,
|
|
1568
|
+
replayPolicy: toolCall.replayPolicy
|
|
1569
|
+
});
|
|
1570
|
+
events.push(event);
|
|
1571
|
+
toolResultsByCallId.set(toolCall.toolCallId, {
|
|
1572
|
+
toolCallId: toolCall.toolCallId,
|
|
1573
|
+
toolName: toolCall.toolName,
|
|
1574
|
+
result: `Error: ${errorMessage}`,
|
|
1575
|
+
...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
|
|
1576
|
+
});
|
|
1577
|
+
if (turnState) {
|
|
1578
|
+
turnState = advanceAgentTurnState(turnState, event, updatedAt, {
|
|
1579
|
+
...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return {
|
|
1585
|
+
snapshot: {
|
|
1586
|
+
toolCalls: options.snapshot.toolCalls.map(
|
|
1587
|
+
(toolCall) => structuredClone(toolCall)
|
|
1588
|
+
),
|
|
1589
|
+
toolResults: options.snapshot.toolCalls.map((toolCall) => toolResultsByCallId.get(toolCall.toolCallId)).filter((toolResult) => toolResult !== void 0)
|
|
1590
|
+
},
|
|
1591
|
+
...turnState ? { turnState } : {},
|
|
1592
|
+
events
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// src/runtime/turn-runner/commit.ts
|
|
1597
|
+
async function* commitStep(options) {
|
|
1598
|
+
if (options.finishReason !== "tool-calls") {
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
const batch = options.turnEngine.consumeStepCommit(options.step);
|
|
1602
|
+
if (!batch) {
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
yield* options.applyCommitBatch(batch);
|
|
1606
|
+
return true;
|
|
1607
|
+
}
|
|
1608
|
+
async function* commitOutput(options) {
|
|
1609
|
+
const batch = options.turnEngine.createOutputCommit({
|
|
1610
|
+
text: options.text,
|
|
1611
|
+
usage: options.usage
|
|
1612
|
+
});
|
|
1613
|
+
if (!batch) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
yield* options.applyCommitBatch(batch, { emitMessages: true });
|
|
1617
|
+
return true;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// src/runtime/workflow-state.ts
|
|
1621
|
+
function cloneUsage2(usage) {
|
|
1622
|
+
return usage ? { ...usage } : void 0;
|
|
1623
|
+
}
|
|
1624
|
+
function cloneMessageSnapshot(message) {
|
|
1625
|
+
return {
|
|
1626
|
+
...message,
|
|
1627
|
+
...message.role === "assistant" && message.toolCalls ? {
|
|
1628
|
+
toolCalls: message.toolCalls.map((toolCall) => ({ ...toolCall }))
|
|
1629
|
+
} : {},
|
|
1630
|
+
...message.role === "assistant" && message.tokens ? {
|
|
1631
|
+
tokens: { ...message.tokens }
|
|
1632
|
+
} : {},
|
|
1633
|
+
...message.role === "assistant" && message.error ? {
|
|
1634
|
+
error: { ...message.error }
|
|
1635
|
+
} : {}
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
function snapshotAgentWorkflowMessage(message) {
|
|
1639
|
+
switch (message.role) {
|
|
1640
|
+
case "user":
|
|
1641
|
+
return {
|
|
1642
|
+
id: message.id,
|
|
1643
|
+
role: "user",
|
|
1644
|
+
createdAt: message.createdAt.toISOString(),
|
|
1645
|
+
content: message.content,
|
|
1646
|
+
...message.system ? { system: message.system } : {}
|
|
1647
|
+
};
|
|
1648
|
+
case "assistant":
|
|
1649
|
+
return {
|
|
1650
|
+
id: message.id,
|
|
1651
|
+
role: "assistant",
|
|
1652
|
+
createdAt: message.createdAt.toISOString(),
|
|
1653
|
+
content: message.content,
|
|
1654
|
+
...message.finish ? { finish: message.finish } : {},
|
|
1655
|
+
...message.tokens ? { tokens: { ...message.tokens } } : {},
|
|
1656
|
+
...message.cost !== void 0 ? { cost: message.cost } : {},
|
|
1657
|
+
...message.error ? { error: { ...message.error } } : {},
|
|
1658
|
+
...message.toolCalls ? {
|
|
1659
|
+
toolCalls: message.toolCalls.map((toolCall) => ({
|
|
1660
|
+
toolCallId: toolCall.toolCallId,
|
|
1661
|
+
toolName: toolCall.toolName,
|
|
1662
|
+
args: toolCall.args
|
|
1663
|
+
}))
|
|
1664
|
+
} : {}
|
|
1665
|
+
};
|
|
1666
|
+
case "tool":
|
|
1667
|
+
return {
|
|
1668
|
+
id: message.id,
|
|
1669
|
+
role: "tool",
|
|
1670
|
+
createdAt: message.createdAt.toISOString(),
|
|
1671
|
+
content: message.content,
|
|
1672
|
+
toolCallId: message.toolCallId,
|
|
1673
|
+
toolName: message.toolName,
|
|
1674
|
+
result: message.result,
|
|
1675
|
+
...message.compactedAt !== void 0 ? { compactedAt: message.compactedAt } : {}
|
|
1676
|
+
};
|
|
1677
|
+
case "system":
|
|
1678
|
+
return {
|
|
1679
|
+
id: message.id,
|
|
1680
|
+
role: "system",
|
|
1681
|
+
createdAt: message.createdAt.toISOString(),
|
|
1682
|
+
content: message.content
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
function snapshotAgentWorkflowMessages(messages) {
|
|
1687
|
+
return messages.map(snapshotAgentWorkflowMessage);
|
|
1688
|
+
}
|
|
1689
|
+
function restoreAgentWorkflowMessage(snapshot) {
|
|
1690
|
+
const createdAt = new Date(snapshot.createdAt);
|
|
1691
|
+
switch (snapshot.role) {
|
|
1692
|
+
case "user":
|
|
1693
|
+
return {
|
|
1694
|
+
id: snapshot.id,
|
|
1695
|
+
role: "user",
|
|
1696
|
+
createdAt,
|
|
1697
|
+
content: snapshot.content,
|
|
1698
|
+
...snapshot.system ? { system: snapshot.system } : {}
|
|
1699
|
+
};
|
|
1700
|
+
case "assistant":
|
|
1701
|
+
return {
|
|
1702
|
+
id: snapshot.id,
|
|
1703
|
+
role: "assistant",
|
|
1704
|
+
createdAt,
|
|
1705
|
+
content: snapshot.content,
|
|
1706
|
+
...snapshot.finish ? { finish: snapshot.finish } : {},
|
|
1707
|
+
...snapshot.tokens ? { tokens: { ...snapshot.tokens } } : {},
|
|
1708
|
+
...snapshot.cost !== void 0 ? { cost: snapshot.cost } : {},
|
|
1709
|
+
...snapshot.error ? { error: { ...snapshot.error } } : {},
|
|
1710
|
+
...snapshot.toolCalls ? {
|
|
1711
|
+
toolCalls: snapshot.toolCalls.map((toolCall) => ({
|
|
1712
|
+
toolCallId: toolCall.toolCallId,
|
|
1713
|
+
toolName: toolCall.toolName,
|
|
1714
|
+
args: toolCall.args
|
|
1715
|
+
}))
|
|
1716
|
+
} : {}
|
|
1717
|
+
};
|
|
1718
|
+
case "tool":
|
|
1719
|
+
return {
|
|
1720
|
+
id: snapshot.id,
|
|
1721
|
+
role: "tool",
|
|
1722
|
+
createdAt,
|
|
1723
|
+
content: snapshot.content,
|
|
1724
|
+
toolCallId: snapshot.toolCallId,
|
|
1725
|
+
toolName: snapshot.toolName,
|
|
1726
|
+
result: snapshot.result,
|
|
1727
|
+
...snapshot.compactedAt !== void 0 ? { compactedAt: snapshot.compactedAt } : {}
|
|
1728
|
+
};
|
|
1729
|
+
case "system":
|
|
1730
|
+
return {
|
|
1731
|
+
id: snapshot.id,
|
|
1732
|
+
role: "system",
|
|
1733
|
+
createdAt,
|
|
1734
|
+
content: snapshot.content
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
function restoreAgentWorkflowMessages(snapshots) {
|
|
1739
|
+
return snapshots.map(restoreAgentWorkflowMessage);
|
|
1740
|
+
}
|
|
1741
|
+
function createAgentWorkflowTurnState(options) {
|
|
1742
|
+
return {
|
|
1743
|
+
sessionId: options.sessionId,
|
|
1744
|
+
systemPrompts: [...options.systemPrompts ?? []],
|
|
1745
|
+
messages: (options.initialMessages ?? []).map(cloneMessageSnapshot),
|
|
1746
|
+
phase: "model-step",
|
|
1747
|
+
step: options.initialStep ?? 1,
|
|
1748
|
+
maxSteps: options.maxSteps,
|
|
1749
|
+
replayDecisions: {},
|
|
1750
|
+
startedAt: options.startedAt,
|
|
1751
|
+
updatedAt: options.startedAt
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function cloneAgentWorkflowTurnState(state) {
|
|
1755
|
+
return {
|
|
1756
|
+
...state,
|
|
1757
|
+
systemPrompts: [...state.systemPrompts],
|
|
1758
|
+
messages: state.messages.map(cloneMessageSnapshot),
|
|
1759
|
+
...state.usage ? { usage: cloneUsage2(state.usage) } : {},
|
|
1760
|
+
...state.turnState ? { turnState: structuredClone(state.turnState) } : {},
|
|
1761
|
+
...state.lastModelStep ? { lastModelStep: structuredClone(state.lastModelStep) } : {},
|
|
1762
|
+
replayDecisions: Object.fromEntries(
|
|
1763
|
+
Object.entries(state.replayDecisions).map(([toolCallId, decision]) => [
|
|
1764
|
+
toolCallId,
|
|
1765
|
+
{ ...decision }
|
|
1766
|
+
])
|
|
1767
|
+
)
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// src/runtime/workflow-planner/helpers.ts
|
|
1772
|
+
function accumulateWorkflowUsage(current, next) {
|
|
1773
|
+
if (!next) {
|
|
1774
|
+
return current ? { ...current } : void 0;
|
|
1775
|
+
}
|
|
1776
|
+
if (!current) {
|
|
1777
|
+
return { ...next };
|
|
1778
|
+
}
|
|
1779
|
+
return {
|
|
1780
|
+
inputTokens: (current.inputTokens ?? 0) + (next.inputTokens ?? 0),
|
|
1781
|
+
outputTokens: (current.outputTokens ?? 0) + (next.outputTokens ?? 0),
|
|
1782
|
+
totalTokens: (current.totalTokens ?? 0) + (next.totalTokens ?? 0),
|
|
1783
|
+
cacheReadTokens: (current.cacheReadTokens ?? 0) + (next.cacheReadTokens ?? 0),
|
|
1784
|
+
cacheWriteTokens: (current.cacheWriteTokens ?? 0) + (next.cacheWriteTokens ?? 0)
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function cloneRelevantReplayDecisions(state, snapshot) {
|
|
1788
|
+
return Object.fromEntries(
|
|
1789
|
+
snapshot.toolCalls.flatMap((toolCall) => {
|
|
1790
|
+
const decision = state.replayDecisions[toolCall.toolCallId];
|
|
1791
|
+
return decision ? [[toolCall.toolCallId, { ...decision }]] : [];
|
|
1792
|
+
})
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
function findNextUnresolvedToolCall(snapshot) {
|
|
1796
|
+
const resolvedIds = new Set(
|
|
1797
|
+
snapshot.toolResults.map((toolResult) => toolResult.toolCallId)
|
|
1798
|
+
);
|
|
1799
|
+
return snapshot.toolCalls.find(
|
|
1800
|
+
(toolCall) => !resolvedIds.has(toolCall.toolCallId)
|
|
1801
|
+
);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/runtime/workflow-planner/plan.ts
|
|
1805
|
+
function planNextAgentWorkflowOperation(state) {
|
|
1806
|
+
switch (state.phase) {
|
|
1807
|
+
case "model-step":
|
|
1808
|
+
return {
|
|
1809
|
+
kind: "model-step",
|
|
1810
|
+
sessionId: state.sessionId,
|
|
1811
|
+
step: state.step,
|
|
1812
|
+
maxSteps: state.maxSteps,
|
|
1813
|
+
systemPrompts: [...state.systemPrompts],
|
|
1814
|
+
messages: state.messages.map((message) => structuredClone(message))
|
|
1815
|
+
};
|
|
1816
|
+
case "tool-batch": {
|
|
1817
|
+
if (!state.lastModelStep?.stepCommit) {
|
|
1818
|
+
throw new Error(
|
|
1819
|
+
`Workflow state for session '${state.sessionId}' is in tool-batch phase without a stepCommit snapshot`
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
const nextToolCall = findNextUnresolvedToolCall(
|
|
1823
|
+
state.lastModelStep.stepCommit
|
|
1824
|
+
);
|
|
1825
|
+
if (!nextToolCall) {
|
|
1826
|
+
throw new Error(
|
|
1827
|
+
`Workflow state for session '${state.sessionId}' is in tool-batch phase without unresolved tool calls`
|
|
1828
|
+
);
|
|
1829
|
+
}
|
|
1830
|
+
return {
|
|
1831
|
+
kind: "tool-call",
|
|
1832
|
+
step: state.step,
|
|
1833
|
+
sessionId: state.sessionId,
|
|
1834
|
+
toolCall: structuredClone(nextToolCall),
|
|
1835
|
+
turnState: state.turnState ? structuredClone(state.turnState) : void 0,
|
|
1836
|
+
replayDecision: cloneRelevantReplayDecisions(
|
|
1837
|
+
state,
|
|
1838
|
+
state.lastModelStep.stepCommit
|
|
1839
|
+
)[nextToolCall.toolCallId]
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
case "step-commit":
|
|
1843
|
+
if (!state.lastModelStep?.stepCommit) {
|
|
1844
|
+
throw new Error(
|
|
1845
|
+
`Workflow state for session '${state.sessionId}' is in step-commit phase without a stepCommit snapshot`
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
kind: "step-commit",
|
|
1850
|
+
sessionId: state.sessionId,
|
|
1851
|
+
step: state.step,
|
|
1852
|
+
snapshot: structuredClone(state.lastModelStep.stepCommit)
|
|
1853
|
+
};
|
|
1854
|
+
case "output-commit":
|
|
1855
|
+
if (!state.lastModelStep) {
|
|
1856
|
+
throw new Error(
|
|
1857
|
+
`Workflow state for session '${state.sessionId}' is in output-commit phase without a last model step`
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
return {
|
|
1861
|
+
kind: "output-commit",
|
|
1862
|
+
sessionId: state.sessionId,
|
|
1863
|
+
text: state.lastModelStep.text,
|
|
1864
|
+
...state.lastModelStep.usage ? { usage: { ...state.lastModelStep.usage } } : {}
|
|
1865
|
+
};
|
|
1866
|
+
case "completed":
|
|
1867
|
+
case "failed":
|
|
1868
|
+
return void 0;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// src/runtime/workflow-planner/apply.ts
|
|
1873
|
+
function applyAgentWorkflowModelStepResult(state, result, updatedAt) {
|
|
1874
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1875
|
+
next.lastModelStep = structuredClone(result);
|
|
1876
|
+
next.turnState = structuredClone(result.turnState);
|
|
1877
|
+
next.usage = accumulateWorkflowUsage(next.usage, result.usage);
|
|
1878
|
+
next.updatedAt = updatedAt;
|
|
1879
|
+
next.phase = result.finishReason === "tool-calls" ? "tool-batch" : "output-commit";
|
|
1880
|
+
return next;
|
|
1881
|
+
}
|
|
1882
|
+
function applyAgentWorkflowToolBatchResult(state, result, updatedAt) {
|
|
1883
|
+
if (state.phase !== "tool-batch") {
|
|
1884
|
+
throw new Error(
|
|
1885
|
+
`Cannot apply a tool-batch result while workflow state is in '${state.phase}' phase`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1889
|
+
next.updatedAt = updatedAt;
|
|
1890
|
+
next.phase = "step-commit";
|
|
1891
|
+
if (!next.lastModelStep) {
|
|
1892
|
+
throw new Error(
|
|
1893
|
+
`Workflow state for session '${state.sessionId}' has no model step to update after tool-batch`
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
next.lastModelStep.stepCommit = structuredClone(result.stepCommit);
|
|
1897
|
+
if (result.turnState) {
|
|
1898
|
+
next.turnState = structuredClone(result.turnState);
|
|
1899
|
+
}
|
|
1900
|
+
return next;
|
|
1901
|
+
}
|
|
1902
|
+
function applyAgentWorkflowToolCallResult(state, result, updatedAt) {
|
|
1903
|
+
if (state.phase !== "tool-batch") {
|
|
1904
|
+
throw new Error(
|
|
1905
|
+
`Cannot apply a tool-call result while workflow state is in '${state.phase}' phase`
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
if (!state.lastModelStep?.stepCommit) {
|
|
1909
|
+
throw new Error(
|
|
1910
|
+
`Workflow state for session '${state.sessionId}' has no stepCommit snapshot in tool-batch phase`
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1914
|
+
const snapshot = structuredClone(state.lastModelStep.stepCommit);
|
|
1915
|
+
const existingResultIndex = snapshot.toolResults.findIndex(
|
|
1916
|
+
(toolResult) => toolResult.toolCallId === result.toolResult.toolCallId
|
|
1917
|
+
);
|
|
1918
|
+
if (existingResultIndex >= 0) {
|
|
1919
|
+
snapshot.toolResults[existingResultIndex] = structuredClone(
|
|
1920
|
+
result.toolResult
|
|
1921
|
+
);
|
|
1922
|
+
} else {
|
|
1923
|
+
snapshot.toolResults.push(structuredClone(result.toolResult));
|
|
1924
|
+
}
|
|
1925
|
+
next.lastModelStep = {
|
|
1926
|
+
...next.lastModelStep,
|
|
1927
|
+
stepCommit: snapshot
|
|
1928
|
+
};
|
|
1929
|
+
next.updatedAt = updatedAt;
|
|
1930
|
+
if (result.turnState) {
|
|
1931
|
+
next.turnState = structuredClone(result.turnState);
|
|
1932
|
+
}
|
|
1933
|
+
next.phase = snapshot.toolResults.length >= snapshot.toolCalls.length ? "step-commit" : "tool-batch";
|
|
1934
|
+
return next;
|
|
1935
|
+
}
|
|
1936
|
+
function applyAgentWorkflowCommitResult(state, result, updatedAt) {
|
|
1937
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1938
|
+
next.messages = [
|
|
1939
|
+
...next.messages,
|
|
1940
|
+
...result.messages.map((message) => structuredClone(message))
|
|
1941
|
+
];
|
|
1942
|
+
next.updatedAt = updatedAt;
|
|
1943
|
+
switch (state.phase) {
|
|
1944
|
+
case "step-commit":
|
|
1945
|
+
next.step = state.step + 1;
|
|
1946
|
+
next.phase = "model-step";
|
|
1947
|
+
delete next.lastModelStep;
|
|
1948
|
+
return next;
|
|
1949
|
+
case "output-commit":
|
|
1950
|
+
next.phase = "completed";
|
|
1951
|
+
next.finalResponse = state.lastModelStep?.text ?? state.finalResponse;
|
|
1952
|
+
delete next.lastModelStep;
|
|
1953
|
+
return next;
|
|
1954
|
+
default:
|
|
1955
|
+
throw new Error(
|
|
1956
|
+
`Cannot apply a commit result while workflow state is in '${state.phase}' phase`
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
function recordAgentWorkflowReplayDecision(state, decision, updatedAt) {
|
|
1961
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1962
|
+
next.replayDecisions[decision.toolCallId] = { ...decision };
|
|
1963
|
+
next.updatedAt = updatedAt;
|
|
1964
|
+
return next;
|
|
1965
|
+
}
|
|
1966
|
+
function failAgentWorkflowTurnState(state, error, updatedAt) {
|
|
1967
|
+
const next = cloneAgentWorkflowTurnState(state);
|
|
1968
|
+
next.phase = "failed";
|
|
1969
|
+
next.error = error.message;
|
|
1970
|
+
next.updatedAt = updatedAt;
|
|
1971
|
+
return next;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
export {
|
|
1975
|
+
convertAgentMessagesToModelMessages,
|
|
1976
|
+
createAgentTurnStepCommitBatch,
|
|
1977
|
+
createAgentTurnState,
|
|
1978
|
+
advanceAgentTurnState,
|
|
1979
|
+
failAgentTurnState,
|
|
1980
|
+
AgentTurnEngine,
|
|
1981
|
+
createAgentTurnEngine,
|
|
1982
|
+
prepareModelStep,
|
|
1983
|
+
isRetryableCategory,
|
|
1984
|
+
LLMError,
|
|
1985
|
+
isRetryable,
|
|
1986
|
+
getRetryDelay,
|
|
1987
|
+
getErrorCategory,
|
|
1988
|
+
DEFAULT_RETRY_CONFIG,
|
|
1989
|
+
createRetryState,
|
|
1990
|
+
calculateDelay,
|
|
1991
|
+
sleep,
|
|
1992
|
+
withRetry,
|
|
1993
|
+
createRetryHandler,
|
|
1994
|
+
shouldRetry,
|
|
1995
|
+
OUTPUT_TOKEN_MAX,
|
|
1996
|
+
LLM,
|
|
1997
|
+
DoomLoopError,
|
|
1998
|
+
ContextOverflowError,
|
|
1999
|
+
processStream,
|
|
2000
|
+
runModelStep,
|
|
2001
|
+
runToolBatch,
|
|
2002
|
+
commitStep,
|
|
2003
|
+
commitOutput,
|
|
2004
|
+
defaultAgentTaskCheckpointStrategy,
|
|
2005
|
+
createAgentTaskRunner,
|
|
2006
|
+
snapshotAgentWorkflowMessage,
|
|
2007
|
+
snapshotAgentWorkflowMessages,
|
|
2008
|
+
restoreAgentWorkflowMessage,
|
|
2009
|
+
restoreAgentWorkflowMessages,
|
|
2010
|
+
createAgentWorkflowTurnState,
|
|
2011
|
+
cloneAgentWorkflowTurnState,
|
|
2012
|
+
planNextAgentWorkflowOperation,
|
|
2013
|
+
applyAgentWorkflowModelStepResult,
|
|
2014
|
+
applyAgentWorkflowToolBatchResult,
|
|
2015
|
+
applyAgentWorkflowToolCallResult,
|
|
2016
|
+
applyAgentWorkflowCommitResult,
|
|
2017
|
+
recordAgentWorkflowReplayDecision,
|
|
2018
|
+
failAgentWorkflowTurnState
|
|
2019
|
+
};
|