@n8n/agents 0.26.3 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.tsbuildinfo +1 -1
- package/dist/runtime/loop/agent-runtime.d.ts +24 -51
- package/dist/runtime/loop/agent-runtime.js +321 -318
- package/dist/runtime/loop/agent-runtime.js.map +1 -1
- package/dist/runtime/loop/checkpoint-data.d.ts +12 -0
- package/dist/runtime/loop/checkpoint-data.js +65 -0
- package/dist/runtime/loop/checkpoint-data.js.map +1 -0
- package/dist/runtime/loop/execution-counter.js +1 -5
- package/dist/runtime/loop/execution-counter.js.map +1 -1
- package/dist/runtime/loop/generate-sink.d.ts +2 -2
- package/dist/runtime/loop/generate-sink.js +3 -5
- package/dist/runtime/loop/generate-sink.js.map +1 -1
- package/dist/runtime/loop/run-output-sink.d.ts +3 -78
- package/dist/runtime/loop/run-output-sink.js +7 -0
- package/dist/runtime/loop/run-output-sink.js.map +1 -1
- package/dist/runtime/loop/runtime-context.d.ts +17 -1
- package/dist/runtime/loop/runtime-context.js +49 -20
- package/dist/runtime/loop/runtime-context.js.map +1 -1
- package/dist/runtime/loop/runtime-helpers.d.ts +1 -1
- package/dist/runtime/loop/stream-sink.d.ts +9 -2
- package/dist/runtime/loop/stream-sink.js +78 -56
- package/dist/runtime/loop/stream-sink.js.map +1 -1
- package/dist/runtime/memory/memory-orchestrator.d.ts +1 -1
- package/dist/runtime/memory/memory-orchestrator.js +8 -8
- package/dist/runtime/memory/memory-orchestrator.js.map +1 -1
- package/dist/runtime/memory/memory-store.d.ts +1 -1
- package/dist/runtime/memory/memory-store.js +7 -2
- package/dist/runtime/memory/memory-store.js.map +1 -1
- package/dist/runtime/memory/tool-result-memory.d.ts +7 -0
- package/dist/runtime/memory/tool-result-memory.js +65 -0
- package/dist/runtime/memory/tool-result-memory.js.map +1 -0
- package/dist/runtime/model/messages.js +20 -9
- package/dist/runtime/model/messages.js.map +1 -1
- package/dist/runtime/model/raw-error.d.ts +1 -1
- package/dist/runtime/state/run-state.d.ts +2 -2
- package/dist/runtime/state/run-state.js +4 -4
- package/dist/runtime/state/run-state.js.map +1 -1
- package/dist/runtime/telemetry/runtime-telemetry.d.ts +1 -1
- package/dist/runtime/tools/delegate-sub-agent-runner.d.ts +13 -0
- package/dist/runtime/tools/delegate-sub-agent-runner.js +361 -0
- package/dist/runtime/tools/delegate-sub-agent-runner.js.map +1 -0
- package/dist/runtime/tools/delegate-sub-agent-tool.d.ts +5 -241
- package/dist/runtime/tools/delegate-sub-agent-tool.js +94 -487
- package/dist/runtime/tools/delegate-sub-agent-tool.js.map +1 -1
- package/dist/runtime/tools/tool-adapter.js +28 -28
- package/dist/runtime/tools/tool-adapter.js.map +1 -1
- package/dist/runtime/tools/tool-call-executor.d.ts +19 -86
- package/dist/runtime/tools/tool-call-executor.js +236 -579
- package/dist/runtime/tools/tool-call-executor.js.map +1 -1
- package/dist/runtime/tools/tool-call-runner.d.ts +25 -0
- package/dist/runtime/tools/tool-call-runner.js +328 -0
- package/dist/runtime/tools/tool-call-runner.js.map +1 -0
- package/dist/runtime/tools/tool-result-guard.d.ts +2 -7
- package/dist/runtime/tools/tool-result-guard.js +13 -70
- package/dist/runtime/tools/tool-result-guard.js.map +1 -1
- package/dist/types/runtime/agent-loop.d.ts +73 -0
- package/dist/types/runtime/agent-loop.js +3 -0
- package/dist/types/runtime/agent-loop.js.map +1 -0
- package/dist/types/runtime/agent-runtime.d.ts +51 -0
- package/dist/types/runtime/agent-runtime.js +3 -0
- package/dist/types/runtime/agent-runtime.js.map +1 -0
- package/dist/types/runtime/delegation.d.ts +234 -0
- package/dist/types/runtime/delegation.js +82 -0
- package/dist/types/runtime/delegation.js.map +1 -0
- package/dist/types/runtime/tool-execution.d.ts +103 -0
- package/dist/types/runtime/tool-execution.js +3 -0
- package/dist/types/runtime/tool-execution.js.map +1 -0
- package/dist/types/sdk/agent-builder.d.ts +1 -1
- package/dist/types/sdk/memory.d.ts +2 -1
- package/package.json +6 -6
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgentRuntime = void 0;
|
|
4
|
+
const checkpoint_data_1 = require("./checkpoint-data");
|
|
4
5
|
const execution_counter_1 = require("./execution-counter");
|
|
5
6
|
const generate_sink_1 = require("./generate-sink");
|
|
6
7
|
const hydrate_file_parts_1 = require("./hydrate-file-parts");
|
|
7
8
|
const runtime_context_1 = require("./runtime-context");
|
|
8
9
|
const runtime_helpers_1 = require("./runtime-helpers");
|
|
9
10
|
const stream_sink_1 = require("./stream-sink");
|
|
10
|
-
const cancellation_1 = require("../../sdk/cancellation");
|
|
11
11
|
const catalog_1 = require("../../sdk/catalog");
|
|
12
12
|
const model_1 = require("../../utils/model");
|
|
13
|
-
const parse_1 = require("../../utils/parse");
|
|
14
13
|
const workspace_1 = require("../../workspace");
|
|
15
14
|
const logger_1 = require("../logger");
|
|
16
15
|
const memory_orchestrator_1 = require("../memory/memory-orchestrator");
|
|
17
16
|
const title_generation_1 = require("../memory/title-generation");
|
|
18
17
|
const message_list_1 = require("../model/message-list");
|
|
19
|
-
const model_factory_1 = require("../model/model-factory");
|
|
20
18
|
const model_token_counter_1 = require("../model/model-token-counter");
|
|
21
19
|
const prompt_cache_1 = require("../model/prompt-cache");
|
|
22
20
|
const active_skills_1 = require("../skills/active-skills");
|
|
@@ -28,7 +26,7 @@ const runtime_telemetry_1 = require("../telemetry/runtime-telemetry");
|
|
|
28
26
|
const deferred_tool_manager_1 = require("../tools/deferred-tool-manager");
|
|
29
27
|
const fix_tool_call_1 = require("../tools/fix-tool-call");
|
|
30
28
|
const tool_call_executor_1 = require("../tools/tool-call-executor");
|
|
31
|
-
const MAX_LOOP_ITERATIONS =
|
|
29
|
+
const MAX_LOOP_ITERATIONS = 100;
|
|
32
30
|
const MAX_EMPTY_TURN_RETRIES = 2;
|
|
33
31
|
const logger = (0, logger_1.createFilteredLogger)();
|
|
34
32
|
const EMPTY_MESSAGE_LIST = {
|
|
@@ -135,70 +133,22 @@ class AgentRuntime {
|
|
|
135
133
|
}
|
|
136
134
|
async resume(method, data, options) {
|
|
137
135
|
this.runId = options.runId;
|
|
138
|
-
const state = await this.
|
|
139
|
-
if (!state) {
|
|
140
|
-
throw new run_state_1.StaleResumeError(`No suspended run found for runId: ${this.runId}`);
|
|
141
|
-
}
|
|
142
|
-
const toolCall = state.pendingToolCalls[options.toolCallId];
|
|
143
|
-
if (!toolCall) {
|
|
144
|
-
throw new run_state_1.StaleResumeError(`No tool call found for toolCallId: ${options.toolCallId}`);
|
|
145
|
-
}
|
|
146
|
-
if (options.hostMetadata !== undefined && !state.persistence) {
|
|
147
|
-
throw new Error('Cannot update host metadata without persistence');
|
|
148
|
-
}
|
|
149
|
-
const list = message_list_1.AgentMessageList.deserialize(state.messageList);
|
|
150
|
-
this.context.hydrateDeferredToolsFromList(list);
|
|
151
|
-
await (0, hydrate_file_parts_1.hydrateFileParts)(list.messages(), this.config.fileStore, {
|
|
152
|
-
threadId: state.persistence?.threadId,
|
|
153
|
-
});
|
|
154
|
-
const tool = this.context
|
|
155
|
-
.getCurrentTools(state.persistence)
|
|
156
|
-
.find((t) => t.name === toolCall.toolName);
|
|
157
|
-
if (!tool)
|
|
158
|
-
throw new Error(`Tool ${toolCall.toolName} not found`);
|
|
159
|
-
let resumeData = data;
|
|
136
|
+
const { state, list, resumeData } = await this.prepareToolResume(data, options);
|
|
160
137
|
let abortScope;
|
|
161
138
|
let resumeClaimed = false;
|
|
162
|
-
const resumeSchema = toolCall.suspended ? toolCall.resumeSchema : tool.resumeSchema;
|
|
163
|
-
if (!(0, cancellation_1.isCancellation)(resumeData) && resumeSchema) {
|
|
164
|
-
const parseResult = await (0, parse_1.parseWithSchema)(resumeSchema, data, { stripUnknown: true });
|
|
165
|
-
if (!parseResult.success) {
|
|
166
|
-
throw new Error(`Invalid resume payload: ${parseResult.error}`);
|
|
167
|
-
}
|
|
168
|
-
resumeData = parseResult.data;
|
|
169
|
-
}
|
|
170
139
|
try {
|
|
171
140
|
const { runId: _rid, toolCallId: _tcid, onResumeClaimed: _onResumeClaimed, hostMetadata, ...callerExecOptions } = options;
|
|
172
|
-
const
|
|
173
|
-
const persistedMaxIterations = persisted.maxIterations;
|
|
174
|
-
const callerMaxIterations = callerExecOptions.maxIterations;
|
|
175
|
-
if (callerMaxIterations !== undefined &&
|
|
176
|
-
persistedMaxIterations !== undefined &&
|
|
177
|
-
callerMaxIterations < persistedMaxIterations) {
|
|
178
|
-
throw new Error(`Cannot decrease maxIterations when resuming a run. Expected >= ${persistedMaxIterations}, received ${callerMaxIterations}.`);
|
|
179
|
-
}
|
|
180
|
-
const mergedMaxIterations = callerMaxIterations ?? persistedMaxIterations;
|
|
181
|
-
const mergedExecOptions = {
|
|
182
|
-
...callerExecOptions,
|
|
183
|
-
...(mergedMaxIterations !== undefined ? { maxIterations: mergedMaxIterations } : {}),
|
|
184
|
-
...(state.iterationCount !== undefined ? { iterationCount: state.iterationCount } : {}),
|
|
185
|
-
};
|
|
141
|
+
const mergedExecOptions = (0, checkpoint_data_1.mergeResumeExecutionOptions)(state, callerExecOptions);
|
|
186
142
|
const claimed = await this.runState.claimResume(this.runId, state);
|
|
187
143
|
if (!claimed) {
|
|
188
144
|
throw new run_state_1.StaleResumeError(`Run ${this.runId} is not suspended. Cannot resume.`);
|
|
189
145
|
}
|
|
190
146
|
resumeClaimed = true;
|
|
191
147
|
const resumeOptions = {
|
|
192
|
-
persistence: state.persistence
|
|
193
|
-
? {
|
|
194
|
-
...state.persistence,
|
|
195
|
-
...(state.persistence.hostMetadata || hostMetadata
|
|
196
|
-
? { hostMetadata: { ...state.persistence.hostMetadata, ...hostMetadata } }
|
|
197
|
-
: {}),
|
|
198
|
-
}
|
|
199
|
-
: undefined,
|
|
148
|
+
persistence: (0, checkpoint_data_1.mergeResumePersistence)(state.persistence, hostMetadata),
|
|
200
149
|
...mergedExecOptions,
|
|
201
150
|
};
|
|
151
|
+
this.updateState({ persistence: resumeOptions.persistence });
|
|
202
152
|
await options.onResumeClaimed?.();
|
|
203
153
|
abortScope = this.eventBus.createAbortScope(resumeOptions.abortSignal);
|
|
204
154
|
const activeAbortScope = abortScope;
|
|
@@ -207,109 +157,43 @@ class AgentRuntime {
|
|
|
207
157
|
resumeToolCallId: options.toolCallId,
|
|
208
158
|
resumeData,
|
|
209
159
|
};
|
|
210
|
-
await this.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
list,
|
|
217
|
-
options: resumeOptions,
|
|
218
|
-
abortScope: activeAbortScope,
|
|
219
|
-
pendingResume,
|
|
220
|
-
}, sink));
|
|
221
|
-
try {
|
|
222
|
-
return this.finalizeGenerate(rawResult, list);
|
|
223
|
-
}
|
|
224
|
-
finally {
|
|
225
|
-
abortScope.dispose();
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
runId: this.runId,
|
|
230
|
-
stream: this.startStream({
|
|
231
|
-
list,
|
|
232
|
-
options: resumeOptions,
|
|
233
|
-
abortScope: activeAbortScope,
|
|
234
|
-
pendingResume,
|
|
235
|
-
}),
|
|
236
|
-
getState: () => this.getState(),
|
|
160
|
+
await this.prepareResumeMemory(list, state.persistence);
|
|
161
|
+
const ctx = {
|
|
162
|
+
list,
|
|
163
|
+
options: resumeOptions,
|
|
164
|
+
abortScope: activeAbortScope,
|
|
165
|
+
pendingResume,
|
|
237
166
|
};
|
|
167
|
+
if (method === 'generate')
|
|
168
|
+
return await this.generateResumedRun(ctx);
|
|
169
|
+
return this.createResumedStream(ctx);
|
|
238
170
|
}
|
|
239
171
|
catch (error) {
|
|
240
|
-
|
|
241
|
-
abortScope?.dispose();
|
|
242
|
-
if (error instanceof run_state_1.StaleResumeError)
|
|
243
|
-
throw error;
|
|
244
|
-
this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
|
|
245
|
-
if (!isAbort) {
|
|
246
|
-
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
247
|
-
}
|
|
248
|
-
if (resumeClaimed)
|
|
249
|
-
await this.cleanupRun();
|
|
250
|
-
if (method === 'generate') {
|
|
251
|
-
return {
|
|
252
|
-
runId: this.runId,
|
|
253
|
-
messages: [],
|
|
254
|
-
finishReason: 'error',
|
|
255
|
-
error,
|
|
256
|
-
getState: () => this.getState(),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
return { runId: this.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error), getState: () => this.getState() };
|
|
172
|
+
return await this.handleResumeFailure(method, error, abortScope, resumeClaimed);
|
|
260
173
|
}
|
|
261
174
|
}
|
|
262
175
|
async crashResume(options) {
|
|
263
176
|
this.runId = options.runId;
|
|
264
|
-
const state = await this.
|
|
265
|
-
|
|
266
|
-
throw new Error(`No checkpoint found for runId: ${this.runId}`);
|
|
267
|
-
if (state.status !== 'running') {
|
|
268
|
-
throw new Error(`Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs`);
|
|
269
|
-
}
|
|
270
|
-
if (Object.keys(state.pendingToolCalls).length > 0) {
|
|
271
|
-
throw new Error(`Checkpoint for runId ${this.runId} has pending tool calls — crashResume only accepts step checkpoints`);
|
|
272
|
-
}
|
|
273
|
-
const list = message_list_1.AgentMessageList.deserialize(state.messageList);
|
|
274
|
-
this.context.hydrateDeferredToolsFromList(list);
|
|
275
|
-
await (0, hydrate_file_parts_1.hydrateFileParts)(list.messages(), this.config.fileStore, {
|
|
276
|
-
threadId: state.persistence?.threadId,
|
|
277
|
-
});
|
|
177
|
+
const state = await this.loadStepCheckpoint();
|
|
178
|
+
const list = await this.restoreCheckpointMessages(state);
|
|
278
179
|
let abortScope;
|
|
279
180
|
try {
|
|
280
181
|
const { runId: _rid, contextNotes, ...callerExecOptions } = options;
|
|
281
|
-
const persisted = state.executionOptions ?? {};
|
|
282
|
-
const persistedMaxIterations = persisted.maxIterations;
|
|
283
|
-
const callerMaxIterations = callerExecOptions.maxIterations;
|
|
284
|
-
if (callerMaxIterations !== undefined &&
|
|
285
|
-
persistedMaxIterations !== undefined &&
|
|
286
|
-
callerMaxIterations < persistedMaxIterations) {
|
|
287
|
-
throw new Error(`Cannot decrease maxIterations when resuming a run. Expected >= ${persistedMaxIterations}, received ${callerMaxIterations}.`);
|
|
288
|
-
}
|
|
289
|
-
const mergedMaxIterations = callerMaxIterations ?? persistedMaxIterations;
|
|
290
182
|
const resumeOptions = {
|
|
291
183
|
persistence: state.persistence,
|
|
292
|
-
...callerExecOptions,
|
|
293
|
-
...(mergedMaxIterations !== undefined ? { maxIterations: mergedMaxIterations } : {}),
|
|
294
|
-
...(state.iterationCount !== undefined ? { iterationCount: state.iterationCount } : {}),
|
|
184
|
+
...(0, checkpoint_data_1.mergeResumeExecutionOptions)(state, callerExecOptions),
|
|
295
185
|
};
|
|
296
186
|
for (const note of contextNotes ?? []) {
|
|
297
187
|
list.addInput([{ role: 'user', content: [{ type: 'text', text: note }] }]);
|
|
298
188
|
}
|
|
299
189
|
abortScope = this.eventBus.createAbortScope(resumeOptions.abortSignal);
|
|
300
190
|
const activeAbortScope = abortScope;
|
|
301
|
-
await this.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
list,
|
|
308
|
-
options: resumeOptions,
|
|
309
|
-
abortScope: activeAbortScope,
|
|
310
|
-
}),
|
|
311
|
-
getState: () => this.getState(),
|
|
312
|
-
};
|
|
191
|
+
await this.prepareResumeMemory(list, state.persistence);
|
|
192
|
+
return this.createResumedStream({
|
|
193
|
+
list,
|
|
194
|
+
options: resumeOptions,
|
|
195
|
+
abortScope: activeAbortScope,
|
|
196
|
+
});
|
|
313
197
|
}
|
|
314
198
|
catch (error) {
|
|
315
199
|
const isAbort = abortScope?.isAborted ?? false;
|
|
@@ -321,6 +205,92 @@ class AgentRuntime {
|
|
|
321
205
|
return { runId: this.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error), getState: () => this.getState() };
|
|
322
206
|
}
|
|
323
207
|
}
|
|
208
|
+
async prepareToolResume(data, options) {
|
|
209
|
+
const state = await this.runState.resume(this.runId);
|
|
210
|
+
if (!state) {
|
|
211
|
+
throw new run_state_1.StaleResumeError(`No suspended run found for runId: ${this.runId}`);
|
|
212
|
+
}
|
|
213
|
+
const toolCall = state.pendingToolCalls[options.toolCallId];
|
|
214
|
+
if (!toolCall) {
|
|
215
|
+
throw new run_state_1.StaleResumeError(`No tool call found for toolCallId: ${options.toolCallId}`);
|
|
216
|
+
}
|
|
217
|
+
if (options.hostMetadata !== undefined && !state.persistence) {
|
|
218
|
+
throw new Error('Cannot update host metadata without persistence');
|
|
219
|
+
}
|
|
220
|
+
const list = await this.restoreCheckpointMessages(state);
|
|
221
|
+
const tool = this.context
|
|
222
|
+
.getCurrentTools(state.persistence)
|
|
223
|
+
.find((t) => t.name === toolCall.toolName);
|
|
224
|
+
if (!tool)
|
|
225
|
+
throw new Error(`Tool ${toolCall.toolName} not found`);
|
|
226
|
+
const resumeSchema = toolCall.suspended ? toolCall.resumeSchema : tool.resumeSchema;
|
|
227
|
+
const resumeData = await (0, checkpoint_data_1.parseResumeData)(data, resumeSchema);
|
|
228
|
+
return { state, list, resumeData };
|
|
229
|
+
}
|
|
230
|
+
async handleResumeFailure(method, error, abortScope, resumeClaimed) {
|
|
231
|
+
const isAbort = abortScope?.isAborted ?? false;
|
|
232
|
+
abortScope?.dispose();
|
|
233
|
+
if (error instanceof run_state_1.StaleResumeError)
|
|
234
|
+
throw error;
|
|
235
|
+
this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
|
|
236
|
+
if (!isAbort) {
|
|
237
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
238
|
+
}
|
|
239
|
+
if (resumeClaimed)
|
|
240
|
+
await this.cleanupRun();
|
|
241
|
+
if (method === 'generate') {
|
|
242
|
+
return {
|
|
243
|
+
runId: this.runId,
|
|
244
|
+
messages: [],
|
|
245
|
+
finishReason: 'error',
|
|
246
|
+
error,
|
|
247
|
+
getState: () => this.getState(),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return { runId: this.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error), getState: () => this.getState() };
|
|
251
|
+
}
|
|
252
|
+
async restoreCheckpointMessages(state) {
|
|
253
|
+
const list = message_list_1.AgentMessageList.deserialize(state.messageList);
|
|
254
|
+
this.context.hydrateDeferredToolsFromList(list);
|
|
255
|
+
await (0, hydrate_file_parts_1.hydrateFileParts)(list.messages(), this.config.fileStore, {
|
|
256
|
+
threadId: state.persistence?.threadId,
|
|
257
|
+
});
|
|
258
|
+
return list;
|
|
259
|
+
}
|
|
260
|
+
async prepareResumeMemory(list, persistence) {
|
|
261
|
+
await this.ensureModelCost();
|
|
262
|
+
await this.memory.setListObservationLogMemory(list, persistence);
|
|
263
|
+
await this.memory.applyObservationMask(list, persistence);
|
|
264
|
+
}
|
|
265
|
+
async generateResumedRun(ctx) {
|
|
266
|
+
const sink = new generate_sink_1.GenerateSink(this.createRunServices());
|
|
267
|
+
const rawResult = await this.telemetry.withRootSpan('generate', ctx.options, this.runId, async () => await this.runAgentLoop(ctx, sink));
|
|
268
|
+
try {
|
|
269
|
+
return this.finalizeGenerate(rawResult, ctx.list);
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
ctx.abortScope.dispose();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
createResumedStream(ctx) {
|
|
276
|
+
return {
|
|
277
|
+
runId: this.runId,
|
|
278
|
+
stream: this.startStream(ctx),
|
|
279
|
+
getState: () => this.getState(),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async loadStepCheckpoint() {
|
|
283
|
+
const state = await this.runState.loadForCrashResume(this.runId);
|
|
284
|
+
if (!state)
|
|
285
|
+
throw new Error(`No checkpoint found for runId: ${this.runId}`);
|
|
286
|
+
if (state.status !== 'running') {
|
|
287
|
+
throw new Error(`Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs`);
|
|
288
|
+
}
|
|
289
|
+
if (Object.keys(state.pendingToolCalls).length > 0) {
|
|
290
|
+
throw new Error(`Checkpoint for runId ${this.runId} has pending tool calls — crashResume only accepts step checkpoints`);
|
|
291
|
+
}
|
|
292
|
+
return state;
|
|
293
|
+
}
|
|
324
294
|
async buildMessageList(input, options) {
|
|
325
295
|
const list = new message_list_1.AgentMessageList();
|
|
326
296
|
await this.memory.loadInto(list, options);
|
|
@@ -407,176 +377,222 @@ class AgentRuntime {
|
|
|
407
377
|
}
|
|
408
378
|
}
|
|
409
379
|
async runAgentLoop(ctx, sink) {
|
|
410
|
-
const {
|
|
380
|
+
const { prepared, state } = await this.prepareLoop(ctx);
|
|
381
|
+
if (prepared.pendingResume) {
|
|
382
|
+
const settlement = await this.resumePendingTools(prepared, sink, state, prepared.pendingResume);
|
|
383
|
+
if (settlement.suspended)
|
|
384
|
+
return settlement.result;
|
|
385
|
+
}
|
|
386
|
+
for (; state.iterationCount < state.maxIterations; state.iterationCount++) {
|
|
387
|
+
const settlement = await this.runLoopIteration(prepared, sink, state);
|
|
388
|
+
if (settlement.suspended)
|
|
389
|
+
return settlement.result;
|
|
390
|
+
if (state.reachedStopCondition)
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
if (!state.reachedStopCondition && state.iterationCount >= state.maxIterations) {
|
|
394
|
+
state.lastFinishReason = 'max-iterations';
|
|
395
|
+
}
|
|
396
|
+
return await sink.finishComplete({
|
|
397
|
+
list: ctx.list,
|
|
398
|
+
options: ctx.options,
|
|
399
|
+
finishReason: state.lastFinishReason,
|
|
400
|
+
usage: state.totalUsage,
|
|
401
|
+
structuredOutput: state.structuredOutput,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async prepareLoop(ctx) {
|
|
405
|
+
const { list, options } = ctx;
|
|
411
406
|
await this.activeSkills?.restore(list, options?.persistence);
|
|
412
407
|
this.context.hydrateDeferredToolsFromList(list);
|
|
413
408
|
list.mcpConnectionNote = (0, runtime_helpers_1.formatMcpConnectionNote)(this.config.mcpConnectionFailures ?? []);
|
|
414
|
-
let totalUsage;
|
|
415
|
-
let lastFinishReason = 'stop';
|
|
416
|
-
let structuredOutput;
|
|
417
409
|
const runTelemetry = this.telemetry.resolve(options);
|
|
418
|
-
const
|
|
410
|
+
const staticContext = this.context.buildStaticLoopContext({
|
|
419
411
|
...options,
|
|
420
412
|
persistence: options?.persistence,
|
|
421
413
|
});
|
|
422
|
-
const instructionProviderOptions =
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
414
|
+
const instructionProviderOptions = this.context.buildInstructionProviderOptions();
|
|
415
|
+
const state = {
|
|
416
|
+
totalUsage: undefined,
|
|
417
|
+
lastFinishReason: 'stop',
|
|
418
|
+
structuredOutput: undefined,
|
|
419
|
+
maxIterations: options?.maxIterations ?? MAX_LOOP_ITERATIONS,
|
|
420
|
+
iterationCount: options?.iterationCount ?? 0,
|
|
421
|
+
reachedStopCondition: false,
|
|
422
|
+
};
|
|
423
|
+
return {
|
|
424
|
+
prepared: {
|
|
425
|
+
...ctx,
|
|
426
|
+
runTelemetry,
|
|
427
|
+
staticContext,
|
|
428
|
+
instructionProviderOptions,
|
|
429
|
+
canDiscardRejectedInput: this.canDiscardRejectedInput(ctx),
|
|
430
|
+
},
|
|
431
|
+
state,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
canDiscardRejectedInput(ctx) {
|
|
435
|
+
const inputMessages = new Set(ctx.list.inputDelta());
|
|
427
436
|
const inputIds = new Set([...inputMessages].map((message) => message.id));
|
|
428
|
-
|
|
437
|
+
return (ctx.isFreshRun === true &&
|
|
429
438
|
[...inputMessages].some((message) => 'role' in message &&
|
|
430
439
|
message.role === 'user' &&
|
|
431
440
|
Array.isArray(message.content) &&
|
|
432
441
|
message.content.some((part) => part.type === 'file' && part.data !== undefined)) &&
|
|
433
|
-
!list
|
|
434
|
-
|
|
442
|
+
!ctx.list
|
|
443
|
+
.messages()
|
|
444
|
+
.some((message) => !inputMessages.has(message) && inputIds.has(message.id)));
|
|
445
|
+
}
|
|
446
|
+
buildToolBatchContext(ctx, toolMap) {
|
|
447
|
+
return {
|
|
435
448
|
toolMap,
|
|
436
|
-
list,
|
|
449
|
+
list: ctx.list,
|
|
437
450
|
runId: this.runId,
|
|
438
|
-
persistence: options?.persistence,
|
|
439
|
-
telemetry: runTelemetry,
|
|
440
|
-
executionCounter: options?.executionCounter,
|
|
441
|
-
abortSignal: abortScope.signal,
|
|
442
|
-
isAborted: () => abortScope.isAborted,
|
|
451
|
+
persistence: ctx.options?.persistence,
|
|
452
|
+
telemetry: ctx.runTelemetry,
|
|
453
|
+
executionCounter: ctx.options?.executionCounter,
|
|
454
|
+
abortSignal: ctx.abortScope.signal,
|
|
455
|
+
isAborted: () => ctx.abortScope.isAborted,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
async resumePendingTools(ctx, sink, state, pendingResume) {
|
|
459
|
+
const { toolMap } = this.context.buildToolLoopContext(ctx.staticContext.aiProviderTools, ctx.options?.persistence, ctx.options?.executionCounter, ctx.list);
|
|
460
|
+
const batch = await this.toolExecutor.iteratePendingToolCallsConcurrent({
|
|
461
|
+
...this.buildToolBatchContext(ctx, toolMap),
|
|
462
|
+
pendingResume,
|
|
443
463
|
});
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
464
|
+
const settlement = await this.finishToolBatch(ctx, sink, state, batch, toolMap, state.iterationCount);
|
|
465
|
+
if (settlement.suspended)
|
|
466
|
+
return settlement;
|
|
467
|
+
await this.memory.maybeObserveMidRun(ctx.list, ctx.options);
|
|
468
|
+
return settlement;
|
|
469
|
+
}
|
|
470
|
+
async runLoopIteration(ctx, sink, state) {
|
|
471
|
+
this.assertNotAborted(ctx.abortScope);
|
|
472
|
+
this.eventBus.emit({ type: "turn_start" });
|
|
473
|
+
const { toolMap, modelCallContext } = await this.prepareModelCall(ctx, state.iterationCount);
|
|
474
|
+
const turn = await this.callModelWithRetries(ctx, sink, state, modelCallContext);
|
|
475
|
+
this.assertNotAborted(ctx.abortScope);
|
|
476
|
+
state.lastFinishReason = turn.finishReason;
|
|
477
|
+
if (!(0, runtime_helpers_1.isReasoningOnlyStop)(turn))
|
|
478
|
+
ctx.list.addResponse(turn.newMessages);
|
|
479
|
+
sink.onTurnFolded?.();
|
|
480
|
+
if (turn.aiFinishReason !== 'tool-calls') {
|
|
481
|
+
if (turn.errorReason)
|
|
482
|
+
throw new Error(turn.errorReason.message);
|
|
483
|
+
state.structuredOutput = turn.structuredOutput;
|
|
484
|
+
this.emitTurnEnd(turn.newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(turn.newMessages));
|
|
485
|
+
state.reachedStopCondition = true;
|
|
486
|
+
return { suspended: false };
|
|
487
|
+
}
|
|
488
|
+
const batch = await this.toolExecutor.iterateToolCallsConcurrent({
|
|
489
|
+
...this.buildToolBatchContext(ctx, toolMap),
|
|
490
|
+
toolCalls: turn.toolCalls,
|
|
491
|
+
});
|
|
492
|
+
const settlement = await this.finishToolBatch(ctx, sink, state, batch, toolMap, state.iterationCount + 1);
|
|
493
|
+
if (settlement.suspended)
|
|
494
|
+
return settlement;
|
|
495
|
+
await this.completeToolTurn(ctx, state, turn);
|
|
496
|
+
return settlement;
|
|
497
|
+
}
|
|
498
|
+
async prepareModelCall(ctx, iterationCount) {
|
|
499
|
+
const { list, options, abortScope, staticContext } = ctx;
|
|
500
|
+
for (const toolName of this.activeSkills?.toolDependencies() ?? []) {
|
|
501
|
+
this.deferredToolManager?.load(toolName);
|
|
502
|
+
}
|
|
503
|
+
const tools = this.context.buildToolLoopContext(staticContext.aiProviderTools, options?.persistence, options?.executionCounter, list);
|
|
504
|
+
const hostVolatileInstructions = await this.resolveVolatileInstructions(options?.persistence);
|
|
505
|
+
const prompt = this.context.buildModelPrompt({
|
|
506
|
+
list,
|
|
507
|
+
tools,
|
|
508
|
+
instructionProviderOptions: ctx.instructionProviderOptions,
|
|
509
|
+
hostVolatileInstructions,
|
|
510
|
+
activeSkills: this.activeSkills,
|
|
511
|
+
});
|
|
512
|
+
const modelCallContext = {
|
|
513
|
+
model: staticContext.model,
|
|
514
|
+
system: prompt.system,
|
|
515
|
+
messages: prompt.messages,
|
|
516
|
+
abortSignal: abortScope.signal,
|
|
517
|
+
hasTools: tools.hasTools,
|
|
518
|
+
aiTools: prompt.aiTools,
|
|
519
|
+
reasoning: staticContext.reasoning,
|
|
520
|
+
providerOptions: staticContext.providerOptions,
|
|
521
|
+
outputSpec: staticContext.outputSpec,
|
|
522
|
+
maxOutputTokens: staticContext.maxOutputTokens,
|
|
523
|
+
aiSdkOptions: this.buildAiSdkOptions(tools.toolMap, options),
|
|
524
|
+
onInputRejected: this.createInputRejectionHandler(ctx, iterationCount),
|
|
477
525
|
};
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
526
|
+
return { toolMap: tools.toolMap, modelCallContext };
|
|
527
|
+
}
|
|
528
|
+
createInputRejectionHandler(ctx, iterationCount) {
|
|
529
|
+
if (!ctx.canDiscardRejectedInput || iterationCount !== 0)
|
|
530
|
+
return undefined;
|
|
531
|
+
return async () => {
|
|
532
|
+
if (ctx.abortScope.isAborted)
|
|
533
|
+
return;
|
|
534
|
+
await this.memory.discardRejectedInput(ctx.list, ctx.options);
|
|
535
|
+
this.updateState({ messageList: ctx.list.serialize() });
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
async callModelWithRetries(ctx, sink, state, modelCallContext) {
|
|
539
|
+
let turn = await sink.callModel(modelCallContext);
|
|
540
|
+
for (let retry = 0; retry < MAX_EMPTY_TURN_RETRIES && (0, runtime_helpers_1.isEmptyModelTurn)(turn); retry++) {
|
|
541
|
+
this.recordTurnUsage(ctx, sink, state, turn);
|
|
542
|
+
this.assertNotAborted(ctx.abortScope);
|
|
543
|
+
turn = await sink.callModel(modelCallContext);
|
|
488
544
|
}
|
|
489
|
-
|
|
545
|
+
this.recordTurnUsage(ctx, sink, state, turn);
|
|
546
|
+
return turn;
|
|
547
|
+
}
|
|
548
|
+
recordTurnUsage(ctx, sink, state, turn) {
|
|
549
|
+
state.totalUsage = (0, runtime_helpers_1.mergeUsage)(state.totalUsage, turn.usage);
|
|
550
|
+
(0, execution_counter_1.incrementTokenCountFromUsage)(ctx.options?.executionCounter, turn.usage);
|
|
551
|
+
sink.reportUsage(state.totalUsage);
|
|
552
|
+
}
|
|
553
|
+
async finishToolBatch(ctx, sink, state, batch, toolMap, nextIteration) {
|
|
554
|
+
const { list, options, abortScope } = ctx;
|
|
555
|
+
const hasPending = Object.keys(batch.pending).length > 0;
|
|
556
|
+
let completed = false;
|
|
557
|
+
try {
|
|
490
558
|
this.assertNotAborted(abortScope);
|
|
491
|
-
|
|
492
|
-
for (const toolName of this.activeSkills?.toolDependencies() ?? []) {
|
|
493
|
-
this.deferredToolManager?.load(toolName);
|
|
494
|
-
}
|
|
495
|
-
const { toolMap, aiTools, hasTools, effectiveInstructions, volatileInstructions, staticToolCacheName, } = this.context.buildToolLoopContext(staticLoopContext.aiProviderTools, options?.persistence, options?.executionCounter, list);
|
|
496
|
-
const hostVolatileInstructions = await this.resolveVolatileInstructions(options?.persistence);
|
|
497
|
-
const combinedVolatileInstructions = [volatileInstructions, hostVolatileInstructions]
|
|
498
|
-
.map((value) => value?.trim())
|
|
499
|
-
.filter((value) => Boolean(value))
|
|
500
|
-
.join('\n\n');
|
|
501
|
-
const { system, messages } = list.forLlm([effectiveInstructions, this.activeSkills?.instructions()]
|
|
502
|
-
.filter(Boolean)
|
|
503
|
-
.join('\n\n'), instructionProviderOptions, combinedVolatileInstructions || undefined, (0, model_factory_1.supportsSplitSystemMessages)(this.config.model));
|
|
504
|
-
const cached = (0, prompt_cache_1.applyRuntimeCacheBreakpoints)({
|
|
505
|
-
system,
|
|
506
|
-
messages: this.activeSkills?.modelMessages(messages, list) ?? messages,
|
|
507
|
-
aiTools,
|
|
508
|
-
promptCaching: this.config.promptCaching,
|
|
509
|
-
modelId: this.modelIdString,
|
|
510
|
-
staticToolCacheName,
|
|
511
|
-
});
|
|
512
|
-
const modelCallContext = {
|
|
513
|
-
model: staticLoopContext.model,
|
|
514
|
-
system,
|
|
515
|
-
messages: cached.messages,
|
|
516
|
-
abortSignal: abortScope.signal,
|
|
517
|
-
hasTools,
|
|
518
|
-
aiTools: cached.aiTools,
|
|
519
|
-
reasoning: staticLoopContext.reasoning,
|
|
520
|
-
providerOptions: staticLoopContext.providerOptions,
|
|
521
|
-
outputSpec: staticLoopContext.outputSpec,
|
|
522
|
-
maxOutputTokens: staticLoopContext.maxOutputTokens,
|
|
523
|
-
aiSdkOptions: this.buildAiSdkOptions(toolMap, options),
|
|
524
|
-
onInputRejected: canDiscardRejectedInput && iterationCount === 0
|
|
525
|
-
? async () => {
|
|
526
|
-
if (abortScope.isAborted)
|
|
527
|
-
return;
|
|
528
|
-
await this.memory.discardRejectedInput(list, options);
|
|
529
|
-
this.updateState({ messageList: list.serialize() });
|
|
530
|
-
}
|
|
531
|
-
: undefined,
|
|
532
|
-
};
|
|
533
|
-
let turn = await sink.callModel(modelCallContext);
|
|
534
|
-
for (let emptyRetry = 0; emptyRetry < MAX_EMPTY_TURN_RETRIES && (0, runtime_helpers_1.isEmptyModelTurn)(turn); emptyRetry++) {
|
|
535
|
-
totalUsage = (0, runtime_helpers_1.mergeUsage)(totalUsage, turn.usage);
|
|
536
|
-
(0, execution_counter_1.incrementTokenCountFromUsage)(options?.executionCounter, turn.usage);
|
|
537
|
-
sink.reportUsage(totalUsage);
|
|
538
|
-
this.assertNotAborted(abortScope);
|
|
539
|
-
turn = await sink.callModel(modelCallContext);
|
|
540
|
-
}
|
|
541
|
-
totalUsage = (0, runtime_helpers_1.mergeUsage)(totalUsage, turn.usage);
|
|
542
|
-
(0, execution_counter_1.incrementTokenCountFromUsage)(options?.executionCounter, turn.usage);
|
|
543
|
-
sink.reportUsage(totalUsage);
|
|
559
|
+
await sink.emitToolBatch(batch);
|
|
544
560
|
this.assertNotAborted(abortScope);
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
sink.onTurnFolded?.();
|
|
549
|
-
if (turn.aiFinishReason !== 'tool-calls') {
|
|
550
|
-
if (turn.errorReason)
|
|
551
|
-
throw new Error(turn.errorReason.message);
|
|
552
|
-
structuredOutput = turn.structuredOutput;
|
|
553
|
-
this.emitTurnEnd(turn.newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(turn.newMessages));
|
|
554
|
-
reachedStopCondition = true;
|
|
555
|
-
break;
|
|
561
|
+
if (!hasPending) {
|
|
562
|
+
completed = true;
|
|
563
|
+
return { suspended: false };
|
|
556
564
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
565
|
+
await this.persistSuspension(batch.pending, options, list, state.totalUsage, state.maxIterations, nextIteration);
|
|
566
|
+
this.assertNotAborted(abortScope);
|
|
567
|
+
const result = await sink.finishSuspended({
|
|
568
|
+
suspendRunId: this.runId,
|
|
569
|
+
list,
|
|
570
|
+
usage: state.totalUsage,
|
|
571
|
+
suspensions: batch.suspensions,
|
|
560
572
|
});
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
this.emitTurnEnd(turn.newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
|
|
565
|
-
await this.memory.maybeObserveMidRun(list, options);
|
|
566
|
-
if (options?.stepCheckpoints) {
|
|
567
|
-
await this.persistStepCheckpoint(list, totalUsage, options, maxIterations, iterationCount + 1);
|
|
568
|
-
}
|
|
573
|
+
this.assertNotAborted(abortScope);
|
|
574
|
+
completed = true;
|
|
575
|
+
return { suspended: true, result };
|
|
569
576
|
}
|
|
570
|
-
|
|
571
|
-
|
|
577
|
+
finally {
|
|
578
|
+
if (!completed && hasPending)
|
|
579
|
+
await this.cleanupFailedSuspension(ctx, batch.pending, toolMap);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async cleanupFailedSuspension(ctx, pending, toolMap) {
|
|
583
|
+
await this.toolExecutor.cleanupPendingToolCalls(pending, this.buildToolBatchContext(ctx, toolMap), ctx.abortScope.isAborted ? 'Run aborted' : 'Parent run failed before suspension');
|
|
584
|
+
try {
|
|
585
|
+
await this.runState.cancel(this.runId, this.getState());
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async completeToolTurn(ctx, state, turn) {
|
|
591
|
+
this.emitTurnEnd(turn.newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(ctx.list.responseDelta()));
|
|
592
|
+
await this.memory.maybeObserveMidRun(ctx.list, ctx.options);
|
|
593
|
+
if (ctx.options?.stepCheckpoints) {
|
|
594
|
+
await this.persistStepCheckpoint(ctx.list, state.totalUsage, ctx.options, state.maxIterations, state.iterationCount + 1);
|
|
572
595
|
}
|
|
573
|
-
return await sink.finishComplete({
|
|
574
|
-
list,
|
|
575
|
-
options,
|
|
576
|
-
finishReason: lastFinishReason,
|
|
577
|
-
usage: totalUsage,
|
|
578
|
-
structuredOutput,
|
|
579
|
-
});
|
|
580
596
|
}
|
|
581
597
|
async resolveVolatileInstructions(persistence) {
|
|
582
598
|
try {
|
|
@@ -597,15 +613,7 @@ class AgentRuntime {
|
|
|
597
613
|
options: ctx.options,
|
|
598
614
|
withRootSpan: async (operation, options, runId, fn) => await this.telemetry.withRootSpan(operation, options, runId, fn),
|
|
599
615
|
runLoop: async (guard) => {
|
|
600
|
-
|
|
601
|
-
void guard.write({
|
|
602
|
-
type: 'warning',
|
|
603
|
-
message: failure.error,
|
|
604
|
-
code: 'mcp_connection_failed',
|
|
605
|
-
source: 'mcp',
|
|
606
|
-
server: failure.server,
|
|
607
|
-
});
|
|
608
|
-
}
|
|
616
|
+
this.writeMcpConnectionWarnings(guard);
|
|
609
617
|
const resolvedList = ctx.list ?? (await this.initRun(ctx.input, ctx.options));
|
|
610
618
|
list = resolvedList;
|
|
611
619
|
sink = new stream_sink_1.StreamSink(guard, this.createRunServices(), ctx.options);
|
|
@@ -632,52 +640,47 @@ class AgentRuntime {
|
|
|
632
640
|
emitError: (error) => this.eventBus.emit({ type: "error", message: String(error), error }),
|
|
633
641
|
});
|
|
634
642
|
}
|
|
635
|
-
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
? pending.suspendPayload
|
|
644
|
-
: undefined;
|
|
645
|
-
list.markToolCallSuspended(pending.toolCallId, {
|
|
646
|
-
...(typeof payload?.message === 'string' ? { message: payload.message } : {}),
|
|
647
|
-
...(typeof payload?.requestId === 'string' ? { requestId: payload.requestId } : {}),
|
|
643
|
+
writeMcpConnectionWarnings(guard) {
|
|
644
|
+
for (const failure of this.config.mcpConnectionFailures ?? []) {
|
|
645
|
+
void guard.write({
|
|
646
|
+
type: 'warning',
|
|
647
|
+
message: failure.error,
|
|
648
|
+
code: 'mcp_connection_failed',
|
|
649
|
+
source: 'mcp',
|
|
650
|
+
server: failure.server,
|
|
648
651
|
});
|
|
649
652
|
}
|
|
653
|
+
}
|
|
654
|
+
async persistSuspension(pendingToolCalls, options, list, totalUsage, maxIterations, iterationCount) {
|
|
655
|
+
const checkpointOptions = (0, checkpoint_data_1.buildCheckpointOptions)(options, maxIterations, iterationCount);
|
|
656
|
+
(0, checkpoint_data_1.markSuspendedToolCalls)(list, pendingToolCalls);
|
|
650
657
|
const state = {
|
|
651
658
|
persistence: options?.persistence,
|
|
652
659
|
status: 'suspended',
|
|
653
660
|
messageList: list.serialize(),
|
|
654
661
|
pendingToolCalls,
|
|
655
662
|
usage: totalUsage,
|
|
656
|
-
|
|
657
|
-
...(resolvedIterationCount !== undefined ? { iterationCount: resolvedIterationCount } : {}),
|
|
663
|
+
...checkpointOptions,
|
|
658
664
|
};
|
|
659
665
|
await this.runState.suspend(this.runId, state);
|
|
660
666
|
this.updateState({ status: 'suspended', pendingToolCalls, messageList: list.serialize() });
|
|
661
667
|
await this.memory.persistTurnDelta(list, options);
|
|
662
668
|
}
|
|
663
669
|
async persistStepCheckpoint(list, totalUsage, options, maxIterations, iterationCount) {
|
|
664
|
-
const
|
|
665
|
-
const resolvedIterationCount = iterationCount ?? options?.iterationCount;
|
|
666
|
-
const executionOptions = resolvedMaxIterations !== undefined ? { maxIterations: resolvedMaxIterations } : undefined;
|
|
670
|
+
const checkpointOptions = (0, checkpoint_data_1.buildCheckpointOptions)(options, maxIterations, iterationCount);
|
|
667
671
|
const state = {
|
|
668
672
|
persistence: options?.persistence,
|
|
669
673
|
status: 'running',
|
|
670
674
|
messageList: list.serialize(),
|
|
671
675
|
pendingToolCalls: {},
|
|
672
676
|
usage: totalUsage,
|
|
673
|
-
|
|
674
|
-
...(resolvedIterationCount !== undefined ? { iterationCount: resolvedIterationCount } : {}),
|
|
677
|
+
...checkpointOptions,
|
|
675
678
|
};
|
|
676
679
|
await this.runState.checkpointStep(this.runId, state);
|
|
677
680
|
}
|
|
678
681
|
async cleanupRun() {
|
|
679
682
|
try {
|
|
680
|
-
await this.runState.complete(this.runId);
|
|
683
|
+
await this.runState.complete(this.runId, this.getState());
|
|
681
684
|
}
|
|
682
685
|
catch (error) {
|
|
683
686
|
logger.warn('Failed to clean up agent run checkpoint', { runId: this.runId, error });
|