@n8n/agents 0.6.0 → 0.8.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/dist/build.tsbuildinfo +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.js +37 -10
- package/dist/index.js.map +1 -1
- package/dist/integrations/langsmith.d.ts +2 -0
- package/dist/integrations/langsmith.js +117 -2
- package/dist/integrations/langsmith.js.map +1 -1
- package/dist/runtime/agent-runtime.d.ts +30 -19
- package/dist/runtime/agent-runtime.js +616 -280
- package/dist/runtime/agent-runtime.js.map +1 -1
- package/dist/runtime/background-task-tracker.d.ts +6 -0
- package/dist/runtime/background-task-tracker.js +26 -0
- package/dist/runtime/background-task-tracker.js.map +1 -0
- package/dist/runtime/deferred-tool-manager.d.ts +28 -0
- package/dist/runtime/deferred-tool-manager.js +205 -0
- package/dist/runtime/deferred-tool-manager.js.map +1 -0
- package/dist/runtime/event-bus.d.ts +1 -0
- package/dist/runtime/event-bus.js +3 -0
- package/dist/runtime/event-bus.js.map +1 -1
- package/dist/runtime/json-value.d.ts +2 -0
- package/dist/runtime/json-value.js +43 -0
- package/dist/runtime/json-value.js.map +1 -0
- package/dist/runtime/memory-store.d.ts +34 -11
- package/dist/runtime/memory-store.js +210 -8
- package/dist/runtime/memory-store.js.map +1 -1
- package/dist/runtime/message-list.d.ts +6 -7
- package/dist/runtime/message-list.js +50 -6
- package/dist/runtime/message-list.js.map +1 -1
- package/dist/runtime/messages.d.ts +0 -2
- package/dist/runtime/messages.js +127 -101
- package/dist/runtime/messages.js.map +1 -1
- package/dist/runtime/model-factory.js +117 -34
- package/dist/runtime/model-factory.js.map +1 -1
- package/dist/runtime/observation-log-defaults.d.ts +20 -0
- package/dist/runtime/observation-log-defaults.js +523 -0
- package/dist/runtime/observation-log-defaults.js.map +1 -0
- package/dist/runtime/observation-log-observer.d.ts +64 -0
- package/dist/runtime/observation-log-observer.js +235 -0
- package/dist/runtime/observation-log-observer.js.map +1 -0
- package/dist/runtime/observation-log-reflector.d.ts +36 -0
- package/dist/runtime/observation-log-reflector.js +292 -0
- package/dist/runtime/observation-log-reflector.js.map +1 -0
- package/dist/runtime/observation-log-renderer.d.ts +6 -0
- package/dist/runtime/observation-log-renderer.js +73 -0
- package/dist/runtime/observation-log-renderer.js.map +1 -0
- package/dist/runtime/observation-log-store.d.ts +3 -0
- package/dist/runtime/observation-log-store.js +26 -0
- package/dist/runtime/observation-log-store.js.map +1 -0
- package/dist/runtime/provider-credentials.d.ts +167 -0
- package/dist/runtime/provider-credentials.js +34 -0
- package/dist/runtime/provider-credentials.js.map +1 -0
- package/dist/runtime/runtime-helpers.d.ts +3 -4
- package/dist/runtime/runtime-helpers.js +6 -32
- package/dist/runtime/runtime-helpers.js.map +1 -1
- package/dist/runtime/scoped-memory-task-runner.d.ts +79 -0
- package/dist/runtime/scoped-memory-task-runner.js +134 -0
- package/dist/runtime/scoped-memory-task-runner.js.map +1 -0
- package/dist/runtime/stream.js +25 -43
- package/dist/runtime/stream.js.map +1 -1
- package/dist/runtime/strip-orphaned-tool-messages.js +1 -25
- package/dist/runtime/strip-orphaned-tool-messages.js.map +1 -1
- package/dist/runtime/title-generation.d.ts +12 -3
- package/dist/runtime/title-generation.js +72 -3
- package/dist/runtime/title-generation.js.map +1 -1
- package/dist/runtime/tool-adapter.d.ts +1 -1
- package/dist/runtime/tool-adapter.js +3 -2
- package/dist/runtime/tool-adapter.js.map +1 -1
- package/dist/sdk/agent.d.ts +35 -22
- package/dist/sdk/agent.js +106 -331
- package/dist/sdk/agent.js.map +1 -1
- package/dist/sdk/catalog.d.ts +1 -0
- package/dist/sdk/catalog.js +1 -0
- package/dist/sdk/catalog.js.map +1 -1
- package/dist/sdk/eval.d.ts +0 -3
- package/dist/sdk/eval.js +4 -9
- package/dist/sdk/eval.js.map +1 -1
- package/dist/sdk/memory.d.ts +12 -12
- package/dist/sdk/memory.js +71 -51
- package/dist/sdk/memory.js.map +1 -1
- package/dist/sdk/provider-capabilities.js +4 -2
- package/dist/sdk/provider-capabilities.js.map +1 -1
- package/dist/sdk/provider-tools.d.ts +17 -2
- package/dist/sdk/provider-tools.js +19 -0
- package/dist/sdk/provider-tools.js.map +1 -1
- package/dist/sdk/telemetry.d.ts +2 -0
- package/dist/sdk/telemetry.js +6 -0
- package/dist/sdk/telemetry.js.map +1 -1
- package/dist/sdk/tool.d.ts +17 -10
- package/dist/sdk/tool.js +34 -0
- package/dist/sdk/tool.js.map +1 -1
- package/dist/sdk/verify.js +1 -4
- package/dist/sdk/verify.js.map +1 -1
- package/dist/storage/base-memory.d.ts +54 -0
- package/dist/storage/base-memory.js +48 -0
- package/dist/storage/base-memory.js.map +1 -0
- package/dist/types/index.d.ts +6 -3
- package/dist/types/index.js +7 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/runtime/event.d.ts +4 -9
- package/dist/types/runtime/event.js.map +1 -1
- package/dist/types/sdk/agent-builder.d.ts +5 -3
- package/dist/types/sdk/agent.d.ts +70 -27
- package/dist/types/sdk/credential-provider.d.ts +2 -3
- package/dist/types/sdk/memory.d.ts +33 -20
- package/dist/types/sdk/message.d.ts +11 -10
- package/dist/types/sdk/observation-log.d.ts +99 -0
- package/dist/types/sdk/observation-log.js +17 -0
- package/dist/types/sdk/observation-log.js.map +1 -0
- package/dist/types/sdk/observation.d.ts +9 -0
- package/dist/types/sdk/{schema.js → observation.js} +1 -1
- package/dist/types/sdk/observation.js.map +1 -0
- package/dist/types/sdk/provider.d.ts +5 -2
- package/dist/types/sdk/tool-descriptor.d.ts +13 -0
- package/dist/types/sdk/tool-descriptor.js +3 -0
- package/dist/types/sdk/tool-descriptor.js.map +1 -0
- package/dist/types/sdk/tool.d.ts +8 -3
- package/dist/types/telemetry.d.ts +1 -0
- package/dist/utils/parse.d.ts +10 -0
- package/dist/utils/parse.js +26 -0
- package/dist/utils/parse.js.map +1 -0
- package/dist/utils/zod.d.ts +1 -1
- package/dist/utils/zod.js.map +1 -1
- package/dist/workspace/tools/batch-str-replace-file.d.ts +3 -0
- package/dist/workspace/tools/batch-str-replace-file.js +67 -0
- package/dist/workspace/tools/batch-str-replace-file.js.map +1 -0
- package/dist/workspace/tools/str-replace-file.d.ts +3 -0
- package/dist/workspace/tools/str-replace-file.js +51 -0
- package/dist/workspace/tools/str-replace-file.js.map +1 -0
- package/dist/workspace/tools/workspace-tools.js +4 -0
- package/dist/workspace/tools/workspace-tools.js.map +1 -1
- package/package.json +16 -11
- package/dist/codegen/generate-agent-code.d.ts +0 -2
- package/dist/codegen/generate-agent-code.js +0 -197
- package/dist/codegen/generate-agent-code.js.map +0 -1
- package/dist/runtime/working-memory.d.ts +0 -14
- package/dist/runtime/working-memory.js +0 -69
- package/dist/runtime/working-memory.js.map +0 -1
- package/dist/sdk/from-schema.d.ts +0 -15
- package/dist/sdk/from-schema.js +0 -218
- package/dist/sdk/from-schema.js.map +0 -1
- package/dist/storage/postgres-memory.d.ts +0 -79
- package/dist/storage/postgres-memory.js +0 -410
- package/dist/storage/postgres-memory.js.map +0 -1
- package/dist/storage/sqlite-memory.d.ts +0 -60
- package/dist/storage/sqlite-memory.js +0 -348
- package/dist/storage/sqlite-memory.js.map +0 -1
- package/dist/types/sdk/schema.d.ts +0 -99
- package/dist/types/sdk/schema.js.map +0 -1
|
@@ -32,44 +32,119 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.AgentRuntime = void 0;
|
|
40
37
|
const ai_1 = require("ai");
|
|
41
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
42
38
|
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
43
39
|
const catalog_1 = require("../sdk/catalog");
|
|
44
40
|
const message_1 = require("../sdk/message");
|
|
41
|
+
const background_task_tracker_1 = require("./background-task-tracker");
|
|
42
|
+
const deferred_tool_manager_1 = require("./deferred-tool-manager");
|
|
45
43
|
const event_bus_1 = require("./event-bus");
|
|
44
|
+
const json_value_1 = require("./json-value");
|
|
45
|
+
const logger_1 = require("./logger");
|
|
46
46
|
const memory_store_1 = require("./memory-store");
|
|
47
47
|
const message_list_1 = require("./message-list");
|
|
48
48
|
const messages_1 = require("./messages");
|
|
49
49
|
const model_factory_1 = require("./model-factory");
|
|
50
|
+
const observation_log_observer_1 = require("./observation-log-observer");
|
|
51
|
+
const observation_log_reflector_1 = require("./observation-log-reflector");
|
|
52
|
+
const observation_log_renderer_1 = require("./observation-log-renderer");
|
|
53
|
+
const observation_log_store_1 = require("./observation-log-store");
|
|
50
54
|
const run_state_1 = require("./run-state");
|
|
51
55
|
const runtime_helpers_1 = require("./runtime-helpers");
|
|
56
|
+
const scoped_memory_task_runner_1 = require("./scoped-memory-task-runner");
|
|
52
57
|
const stream_1 = require("./stream");
|
|
53
58
|
const strip_orphaned_tool_messages_1 = require("./strip-orphaned-tool-messages");
|
|
54
59
|
const title_generation_1 = require("./title-generation");
|
|
55
60
|
const tool_adapter_1 = require("./tool-adapter");
|
|
56
|
-
const
|
|
61
|
+
const observation_log_1 = require("../types/sdk/observation-log");
|
|
62
|
+
const parse_1 = require("../utils/parse");
|
|
57
63
|
const zod_1 = require("../utils/zod");
|
|
64
|
+
function isActiveSpanTracer(value) {
|
|
65
|
+
return (value !== null &&
|
|
66
|
+
typeof value === 'object' &&
|
|
67
|
+
typeof Reflect.get(value, 'startActiveSpan') === 'function');
|
|
68
|
+
}
|
|
69
|
+
function stringifyTelemetryValue(value) {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.stringify(value);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getToolInputSchema(tool) {
|
|
78
|
+
if (!tool.inputSchema) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return (0, zod_1.isZodSchema)(tool.inputSchema) ? (0, zod_to_json_schema_1.zodToJsonSchema)(tool.inputSchema) : tool.inputSchema;
|
|
82
|
+
}
|
|
83
|
+
function summarizeToolForTelemetry(tool) {
|
|
84
|
+
return {
|
|
85
|
+
name: tool.name,
|
|
86
|
+
description: tool.description,
|
|
87
|
+
type: tool.mcpTool ? 'mcp' : 'local',
|
|
88
|
+
...(tool.mcpServerName ? { mcp_server: tool.mcpServerName } : {}),
|
|
89
|
+
...(tool.suspendSchema || tool.resumeSchema || tool.withDefaultApproval
|
|
90
|
+
? { approval: true }
|
|
91
|
+
: {}),
|
|
92
|
+
...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function summarizeProviderToolForTelemetry(tool) {
|
|
96
|
+
const [provider] = tool.name.split('.');
|
|
97
|
+
return {
|
|
98
|
+
name: tool.name,
|
|
99
|
+
provider,
|
|
100
|
+
type: 'provider',
|
|
101
|
+
args: tool.args,
|
|
102
|
+
...(tool.inputSchema ? { input_schema: getToolInputSchema(tool) } : {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function buildAgentRootInputAttributes(config) {
|
|
106
|
+
const localTools = (config.tools ?? []).map(summarizeToolForTelemetry);
|
|
107
|
+
const providerTools = (config.providerTools ?? []).map(summarizeProviderToolForTelemetry);
|
|
108
|
+
const tools = [...localTools, ...providerTools];
|
|
109
|
+
const toolNames = tools
|
|
110
|
+
.map((tool) => (typeof tool.name === 'string' ? tool.name : undefined))
|
|
111
|
+
.filter((name) => name !== undefined);
|
|
112
|
+
const serialized = stringifyTelemetryValue({
|
|
113
|
+
agent: config.name,
|
|
114
|
+
tool_count: tools.length,
|
|
115
|
+
tools,
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
...(toolNames.length > 0 ? { 'langsmith.metadata.available_tools': toolNames } : {}),
|
|
119
|
+
...(serialized ? { 'gen_ai.prompt': serialized } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
58
122
|
const MAX_LOOP_ITERATIONS = 20;
|
|
59
|
-
const
|
|
123
|
+
const logger = (0, logger_1.createFilteredLogger)();
|
|
60
124
|
const EMPTY_MESSAGE_LIST = {
|
|
61
125
|
messages: [],
|
|
62
126
|
historyIds: [],
|
|
63
127
|
inputIds: [],
|
|
64
128
|
responseIds: [],
|
|
65
129
|
};
|
|
130
|
+
function hasFunctionProperty(value, property) {
|
|
131
|
+
return property in value && typeof Reflect.get(value, property) === 'function';
|
|
132
|
+
}
|
|
133
|
+
function hasObservationLogObserverMemory(memory) {
|
|
134
|
+
return ((0, observation_log_store_1.hasObservationLogStore)(memory) &&
|
|
135
|
+
hasFunctionProperty(memory, 'getMessagesForScope') &&
|
|
136
|
+
hasFunctionProperty(memory, 'getCursor') &&
|
|
137
|
+
hasFunctionProperty(memory, 'setCursor'));
|
|
138
|
+
}
|
|
66
139
|
class AgentRuntime {
|
|
67
140
|
constructor(config) {
|
|
68
|
-
this.
|
|
141
|
+
this.backgroundTasks = new background_task_tracker_1.BackgroundTaskTracker();
|
|
69
142
|
this.config = config;
|
|
70
|
-
|
|
143
|
+
if (config.deferredTools && config.deferredTools.length > 0) {
|
|
144
|
+
this.deferredToolManager = new deferred_tool_manager_1.DeferredToolManager(config.deferredTools, config.toolSearch);
|
|
145
|
+
}
|
|
146
|
+
this.runState = new run_state_1.RunStateManager(config.checkpointStorage);
|
|
71
147
|
this.eventBus = config.eventBus ?? new event_bus_1.AgentEventBus();
|
|
72
|
-
this.modelCost = config.modelCost;
|
|
73
148
|
this.currentState = {
|
|
74
149
|
persistence: undefined,
|
|
75
150
|
status: 'idle',
|
|
@@ -77,6 +152,9 @@ class AgentRuntime {
|
|
|
77
152
|
pendingToolCalls: {},
|
|
78
153
|
};
|
|
79
154
|
}
|
|
155
|
+
async dispose() {
|
|
156
|
+
await this.backgroundTasks.flush();
|
|
157
|
+
}
|
|
80
158
|
getState() {
|
|
81
159
|
return { ...this.currentState };
|
|
82
160
|
}
|
|
@@ -84,83 +162,72 @@ class AgentRuntime {
|
|
|
84
162
|
this.eventBus.abort();
|
|
85
163
|
}
|
|
86
164
|
async generate(input, options) {
|
|
87
|
-
|
|
88
|
-
this.executionOptions = options;
|
|
89
|
-
this.updateState({ persistence: options?.persistence });
|
|
165
|
+
const runId = (0, run_state_1.generateRunId)();
|
|
90
166
|
let list = undefined;
|
|
91
167
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
168
|
+
const initializedList = await this.initRun(input, options);
|
|
169
|
+
list = initializedList;
|
|
170
|
+
const rawResult = await this.withTelemetryRootSpan('generate', options, runId, async () => await this.runGenerateLoop({ list: initializedList, options, runId }));
|
|
171
|
+
return this.finalizeGenerate(rawResult, list, runId);
|
|
95
172
|
}
|
|
96
173
|
catch (error) {
|
|
97
|
-
await this.flushTelemetry();
|
|
174
|
+
await this.flushTelemetry(options);
|
|
98
175
|
const isAbort = this.eventBus.isAborted;
|
|
99
176
|
this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
|
|
100
177
|
if (!isAbort) {
|
|
101
|
-
this.
|
|
178
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
102
179
|
}
|
|
103
|
-
return {
|
|
104
|
-
runId: this.runId,
|
|
105
|
-
messages: list?.responseDelta() ?? [],
|
|
106
|
-
finishReason: 'error',
|
|
107
|
-
error,
|
|
108
|
-
getState: () => this.getState(),
|
|
109
|
-
};
|
|
180
|
+
return { runId, messages: list?.responseDelta() ?? [], finishReason: 'error', error };
|
|
110
181
|
}
|
|
111
182
|
}
|
|
112
183
|
async stream(input, options) {
|
|
113
|
-
|
|
114
|
-
this.executionOptions = options;
|
|
115
|
-
this.updateState({ persistence: options?.persistence });
|
|
184
|
+
const runId = (0, run_state_1.generateRunId)();
|
|
116
185
|
let list;
|
|
117
186
|
try {
|
|
118
|
-
list = await this.initRun(input);
|
|
187
|
+
list = await this.initRun(input, options);
|
|
119
188
|
}
|
|
120
189
|
catch (error) {
|
|
121
190
|
const isAbort = this.eventBus.isAborted;
|
|
122
191
|
this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
|
|
123
192
|
if (!isAbort) {
|
|
124
|
-
this.
|
|
193
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
125
194
|
}
|
|
126
|
-
return { runId
|
|
195
|
+
return { runId, stream: (0, runtime_helpers_1.makeErrorStream)(error) };
|
|
127
196
|
}
|
|
128
|
-
return {
|
|
129
|
-
runId: this.runId,
|
|
130
|
-
stream: this.startStreamLoop(list),
|
|
131
|
-
getState: () => this.getState(),
|
|
132
|
-
};
|
|
197
|
+
return { runId, stream: this.startStreamLoop({ list, options, runId }) };
|
|
133
198
|
}
|
|
134
199
|
async resume(method, data, options) {
|
|
135
|
-
|
|
136
|
-
const state = await this.runState.resume(this.runId);
|
|
200
|
+
const state = await this.runState.resume(options.runId);
|
|
137
201
|
if (!state)
|
|
138
|
-
throw new Error(`No suspended run found for runId: ${
|
|
202
|
+
throw new Error(`No suspended run found for runId: ${options.runId}`);
|
|
139
203
|
const toolCall = state.pendingToolCalls[options.toolCallId];
|
|
140
204
|
if (!toolCall)
|
|
141
205
|
throw new Error(`No tool call found for toolCallId: ${options.toolCallId}`);
|
|
142
|
-
const
|
|
206
|
+
const list = message_list_1.AgentMessageList.deserialize(state.messageList);
|
|
207
|
+
this.hydrateDeferredToolsFromList(list);
|
|
208
|
+
const tool = this.getCurrentTools().find((t) => t.name === toolCall.toolName);
|
|
143
209
|
if (!tool)
|
|
144
210
|
throw new Error(`Tool ${toolCall.toolName} not found`);
|
|
145
211
|
let resumeData = data;
|
|
146
212
|
if (tool.resumeSchema) {
|
|
147
|
-
const parseResult = await tool.resumeSchema
|
|
213
|
+
const parseResult = await (0, parse_1.parseWithSchema)(tool.resumeSchema, data);
|
|
148
214
|
if (!parseResult.success) {
|
|
149
|
-
throw new Error(`Invalid resume payload: ${parseResult.error
|
|
215
|
+
throw new Error(`Invalid resume payload: ${parseResult.error}`);
|
|
150
216
|
}
|
|
151
217
|
resumeData = parseResult.data;
|
|
152
218
|
}
|
|
153
219
|
try {
|
|
154
|
-
const list = message_list_1.AgentMessageList.deserialize(state.messageList);
|
|
155
220
|
const { runId: _rid, toolCallId: _tcid, ...callerExecOptions } = options;
|
|
156
221
|
const persisted = state.executionOptions ?? {};
|
|
157
222
|
const mergedExecOptions = {
|
|
158
223
|
...persisted,
|
|
159
224
|
...callerExecOptions,
|
|
160
225
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
226
|
+
const resumeOptions = {
|
|
227
|
+
persistence: state.persistence,
|
|
228
|
+
...mergedExecOptions,
|
|
229
|
+
};
|
|
230
|
+
this.eventBus.resetAbort(resumeOptions.abortSignal);
|
|
164
231
|
const pendingResume = {
|
|
165
232
|
pendingToolCalls: state.pendingToolCalls,
|
|
166
233
|
resumeToolCallId: options.toolCallId,
|
|
@@ -168,53 +235,61 @@ class AgentRuntime {
|
|
|
168
235
|
messages: state.messageList.messages,
|
|
169
236
|
};
|
|
170
237
|
await this.ensureModelCost();
|
|
171
|
-
await this.
|
|
238
|
+
await this.setListObservationLogMemory(list, state.persistence);
|
|
172
239
|
if (method === 'generate') {
|
|
173
|
-
const rawResult = await this.
|
|
240
|
+
const rawResult = await this.withTelemetryRootSpan('generate', resumeOptions, options.runId, async () => await this.runGenerateLoop({
|
|
241
|
+
list,
|
|
242
|
+
options: resumeOptions,
|
|
243
|
+
runId: options.runId,
|
|
244
|
+
pendingResume,
|
|
245
|
+
}));
|
|
174
246
|
if (!rawResult.pendingSuspend) {
|
|
175
|
-
await this.cleanupRun();
|
|
247
|
+
await this.cleanupRun(options.runId);
|
|
176
248
|
}
|
|
177
|
-
return this.finalizeGenerate(rawResult, list);
|
|
249
|
+
return this.finalizeGenerate(rawResult, list, options.runId);
|
|
178
250
|
}
|
|
179
251
|
return {
|
|
180
|
-
runId:
|
|
181
|
-
stream: this.startStreamLoop(
|
|
182
|
-
|
|
252
|
+
runId: options.runId,
|
|
253
|
+
stream: this.startStreamLoop({
|
|
254
|
+
list,
|
|
255
|
+
options: resumeOptions,
|
|
256
|
+
runId: options.runId,
|
|
257
|
+
pendingResume,
|
|
258
|
+
}),
|
|
183
259
|
};
|
|
184
260
|
}
|
|
185
261
|
catch (error) {
|
|
186
262
|
const isAbort = this.eventBus.isAborted;
|
|
187
263
|
this.updateState({ status: isAbort ? 'cancelled' : 'failed' });
|
|
188
264
|
if (!isAbort) {
|
|
189
|
-
this.
|
|
265
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
190
266
|
}
|
|
191
267
|
if (method === 'generate') {
|
|
192
268
|
return {
|
|
193
|
-
runId:
|
|
269
|
+
runId: options.runId,
|
|
194
270
|
messages: [],
|
|
195
271
|
finishReason: 'error',
|
|
196
272
|
error,
|
|
197
|
-
getState: () => this.getState(),
|
|
198
273
|
};
|
|
199
274
|
}
|
|
200
|
-
return { runId:
|
|
275
|
+
return { runId: options.runId, stream: (0, runtime_helpers_1.makeErrorStream)(error) };
|
|
201
276
|
}
|
|
202
277
|
}
|
|
203
|
-
async buildMessageList(input) {
|
|
278
|
+
async buildMessageList(input, options) {
|
|
204
279
|
const list = new message_list_1.AgentMessageList();
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const memMessages = await this.config.memory.getMessages(persistence.threadId, {
|
|
280
|
+
if (this.config.memory && options?.persistence?.threadId) {
|
|
281
|
+
const memMessages = await this.config.memory.getMessages(options.persistence.threadId, {
|
|
208
282
|
limit: this.config.lastMessages ?? 10,
|
|
283
|
+
resourceId: options.persistence.resourceId,
|
|
209
284
|
});
|
|
210
285
|
if (memMessages.length > 0) {
|
|
211
286
|
list.addHistory((0, strip_orphaned_tool_messages_1.stripOrphanedToolMessages)(memMessages));
|
|
212
287
|
}
|
|
213
288
|
}
|
|
214
|
-
if (this.config.semanticRecall && persistence?.threadId) {
|
|
215
|
-
await this.performSemanticRecall(list, input, persistence.threadId, persistence.resourceId);
|
|
289
|
+
if (this.config.semanticRecall && options?.persistence?.threadId) {
|
|
290
|
+
await this.performSemanticRecall(list, input, options.persistence.threadId, options.persistence.resourceId);
|
|
216
291
|
}
|
|
217
|
-
await this.
|
|
292
|
+
await this.setListObservationLogMemory(list, options?.persistence);
|
|
218
293
|
list.addInput(input);
|
|
219
294
|
return list;
|
|
220
295
|
}
|
|
@@ -296,34 +371,38 @@ class AgentRuntime {
|
|
|
296
371
|
return mid && expandedIds.has(mid);
|
|
297
372
|
});
|
|
298
373
|
}
|
|
299
|
-
async initRun(input) {
|
|
300
|
-
this.eventBus.resetAbort(
|
|
301
|
-
this.updateState({
|
|
302
|
-
|
|
374
|
+
async initRun(input, options) {
|
|
375
|
+
this.eventBus.resetAbort(options?.abortSignal);
|
|
376
|
+
this.updateState({
|
|
377
|
+
status: 'running',
|
|
378
|
+
persistence: options?.persistence,
|
|
379
|
+
});
|
|
380
|
+
this.eventBus.emit({ type: "agent_start" });
|
|
303
381
|
await this.ensureModelCost();
|
|
304
382
|
const normalizedInput = (0, runtime_helpers_1.normalizeInput)(input);
|
|
305
|
-
|
|
383
|
+
this.incrementMessageCount(options?.executionCounter);
|
|
384
|
+
return await this.buildMessageList(normalizedInput, options);
|
|
306
385
|
}
|
|
307
|
-
finalizeGenerate(result, list) {
|
|
308
|
-
result.runId =
|
|
386
|
+
finalizeGenerate(result, list, runId) {
|
|
387
|
+
result.runId = runId;
|
|
309
388
|
result.usage = this.applyCost(result.usage);
|
|
310
389
|
result.model = this.modelIdString;
|
|
311
390
|
const finalized = (0, runtime_helpers_1.applySubAgentUsage)(result);
|
|
312
391
|
this.updateState({ status: 'success', messageList: list.serialize() });
|
|
313
|
-
this.
|
|
314
|
-
return
|
|
392
|
+
this.eventBus.emit({ type: "agent_end", messages: finalized.messages });
|
|
393
|
+
return finalized;
|
|
315
394
|
}
|
|
316
|
-
resolveTelemetry() {
|
|
395
|
+
resolveTelemetry(options) {
|
|
317
396
|
if (this.config.telemetry)
|
|
318
397
|
return this.config.telemetry;
|
|
319
|
-
const inherited =
|
|
398
|
+
const inherited = options?.telemetry;
|
|
320
399
|
if (!inherited)
|
|
321
400
|
return undefined;
|
|
322
401
|
return { ...inherited, functionId: this.config.name };
|
|
323
402
|
}
|
|
324
|
-
async flushTelemetry() {
|
|
403
|
+
async flushTelemetry(options) {
|
|
325
404
|
try {
|
|
326
|
-
const resolved = this.resolveTelemetry();
|
|
405
|
+
const resolved = this.resolveTelemetry(options);
|
|
327
406
|
if (resolved?.provider) {
|
|
328
407
|
await resolved.provider.forceFlush();
|
|
329
408
|
}
|
|
@@ -331,8 +410,8 @@ class AgentRuntime {
|
|
|
331
410
|
catch {
|
|
332
411
|
}
|
|
333
412
|
}
|
|
334
|
-
buildTelemetryOptions() {
|
|
335
|
-
const t = this.resolveTelemetry();
|
|
413
|
+
buildTelemetryOptions(options) {
|
|
414
|
+
const t = this.resolveTelemetry(options);
|
|
336
415
|
if (!t?.enabled)
|
|
337
416
|
return {};
|
|
338
417
|
return {
|
|
@@ -347,23 +426,142 @@ class AgentRuntime {
|
|
|
347
426
|
},
|
|
348
427
|
};
|
|
349
428
|
}
|
|
350
|
-
|
|
351
|
-
const
|
|
429
|
+
buildTelemetryRootAttributes(t, spanName, runId) {
|
|
430
|
+
const metadataAttributes = this.buildTelemetryMetadataAttributes(t, 'langsmith.metadata');
|
|
431
|
+
return {
|
|
432
|
+
'langsmith.traceable': 'true',
|
|
433
|
+
'langsmith.trace.name': spanName,
|
|
434
|
+
'langsmith.span.kind': 'chain',
|
|
435
|
+
'langsmith.metadata.agent_name': this.config.name,
|
|
436
|
+
'langsmith.metadata.agent_run_id': runId,
|
|
437
|
+
...metadataAttributes,
|
|
438
|
+
...buildAgentRootInputAttributes(this.config),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
buildTelemetryMetadataAttributes(t, prefix) {
|
|
442
|
+
return Object.fromEntries(Object.entries(t.metadata ?? {}).map(([key, value]) => [`${prefix}.${key}`, value]));
|
|
443
|
+
}
|
|
444
|
+
buildAiSdkOperationAttributes(operationId, t) {
|
|
445
|
+
const functionId = t.functionId ?? this.config.name;
|
|
446
|
+
return {
|
|
447
|
+
'operation.name': `${operationId} ${functionId}`,
|
|
448
|
+
'resource.name': functionId,
|
|
449
|
+
'ai.operationId': operationId,
|
|
450
|
+
'ai.telemetry.functionId': functionId,
|
|
451
|
+
...this.buildTelemetryMetadataAttributes(t, 'ai.telemetry.metadata'),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
recordExecutionCounter(fn) {
|
|
455
|
+
try {
|
|
456
|
+
fn();
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
incrementMessageCount(counter) {
|
|
462
|
+
if (!counter)
|
|
463
|
+
return;
|
|
464
|
+
this.recordExecutionCounter(() => counter.incrementMessageCount());
|
|
465
|
+
}
|
|
466
|
+
incrementToolCallCount(counter) {
|
|
467
|
+
if (!counter)
|
|
468
|
+
return;
|
|
469
|
+
this.recordExecutionCounter(() => counter.incrementToolCallCount());
|
|
470
|
+
}
|
|
471
|
+
incrementTokenCount(counter, usage) {
|
|
472
|
+
if (!counter || !usage)
|
|
473
|
+
return;
|
|
474
|
+
const tokenCount = usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
475
|
+
if (tokenCount <= 0)
|
|
476
|
+
return;
|
|
477
|
+
this.recordExecutionCounter(() => counter.incrementTokenCount(tokenCount));
|
|
478
|
+
}
|
|
479
|
+
async withTelemetryRootSpan(operation, options, runId, fn) {
|
|
480
|
+
const t = this.resolveTelemetry(options);
|
|
481
|
+
if (!t?.enabled || t.runtimeRootSpanEnabled === false || !isActiveSpanTracer(t.tracer)) {
|
|
482
|
+
return await fn();
|
|
483
|
+
}
|
|
484
|
+
const spanName = `${t.functionId ?? this.config.name}.${operation}`;
|
|
485
|
+
return await t.tracer.startActiveSpan(spanName, { attributes: this.buildTelemetryRootAttributes(t, spanName, runId) }, async (span) => {
|
|
486
|
+
try {
|
|
487
|
+
return await fn();
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
span.recordException?.(error);
|
|
491
|
+
span.setStatus?.({ code: 2, message: String(error) });
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
span.end();
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
async withTelemetryToolSpan(toolCallId, toolName, input, t, fn) {
|
|
500
|
+
if (!t?.enabled || !isActiveSpanTracer(t.tracer)) {
|
|
501
|
+
return await fn();
|
|
502
|
+
}
|
|
503
|
+
const shouldRecordInputs = t.recordInputs ?? true;
|
|
504
|
+
const inputValue = shouldRecordInputs ? stringifyTelemetryValue(input) : undefined;
|
|
505
|
+
return await t.tracer.startActiveSpan('ai.toolCall', {
|
|
506
|
+
attributes: {
|
|
507
|
+
...this.buildAiSdkOperationAttributes('ai.toolCall', t),
|
|
508
|
+
'ai.toolCall.name': toolName,
|
|
509
|
+
'ai.toolCall.id': toolCallId,
|
|
510
|
+
...(inputValue !== undefined ? { 'ai.toolCall.args': inputValue } : {}),
|
|
511
|
+
},
|
|
512
|
+
}, async (span) => {
|
|
513
|
+
try {
|
|
514
|
+
const result = await fn();
|
|
515
|
+
const shouldRecordOutputs = t.recordOutputs ?? true;
|
|
516
|
+
const outputValue = shouldRecordOutputs ? stringifyTelemetryValue(result) : undefined;
|
|
517
|
+
if (outputValue !== undefined) {
|
|
518
|
+
span.setAttributes?.({ 'ai.toolCall.result': outputValue });
|
|
519
|
+
}
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
span.recordException?.(error);
|
|
524
|
+
span.setStatus?.({ code: 2, message: String(error) });
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
finally {
|
|
528
|
+
span.end();
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
async runGenerateLoop(ctx) {
|
|
533
|
+
const { list, options, runId, pendingResume } = ctx;
|
|
534
|
+
this.hydrateDeferredToolsFromList(list);
|
|
352
535
|
let totalUsage;
|
|
353
536
|
let lastFinishReason = 'stop';
|
|
354
537
|
let structuredOutput;
|
|
355
538
|
const toolCallSummary = [];
|
|
356
539
|
const collectedSubAgentUsage = [];
|
|
357
|
-
const runTelemetry = this.resolveTelemetry();
|
|
540
|
+
const runTelemetry = this.resolveTelemetry(options);
|
|
541
|
+
const staticLoopContext = this.buildStaticLoopContext({
|
|
542
|
+
...options,
|
|
543
|
+
persistence: options?.persistence,
|
|
544
|
+
});
|
|
545
|
+
const pendingLoopContext = this.buildToolLoopContext(staticLoopContext.aiProviderTools);
|
|
546
|
+
const pendingToolCtx = {
|
|
547
|
+
toolMap: pendingLoopContext.toolMap,
|
|
548
|
+
list,
|
|
549
|
+
runId,
|
|
550
|
+
telemetry: runTelemetry,
|
|
551
|
+
executionCounter: options?.executionCounter,
|
|
552
|
+
};
|
|
358
553
|
if (pendingResume) {
|
|
359
|
-
const batch = await this.iteratePendingToolCallsConcurrent(
|
|
554
|
+
const batch = await this.iteratePendingToolCallsConcurrent({
|
|
555
|
+
...pendingToolCtx,
|
|
556
|
+
pendingResume,
|
|
557
|
+
});
|
|
360
558
|
for (const r of batch.results) {
|
|
361
559
|
toolCallSummary.push(r.toolEntry);
|
|
362
560
|
if (r.subAgentUsage)
|
|
363
561
|
collectedSubAgentUsage.push(...r.subAgentUsage);
|
|
364
562
|
}
|
|
365
563
|
if (Object.keys(batch.pending).length > 0) {
|
|
366
|
-
const suspendRunId = await this.persistSuspension(batch.pending, list, totalUsage);
|
|
564
|
+
const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId);
|
|
367
565
|
return {
|
|
368
566
|
runId: suspendRunId,
|
|
369
567
|
messages: list.responseDelta(),
|
|
@@ -377,49 +575,57 @@ class AgentRuntime {
|
|
|
377
575
|
suspendPayload: s.payload,
|
|
378
576
|
resumeSchema: s.resumeSchema,
|
|
379
577
|
})),
|
|
380
|
-
getState: () => this.getState(),
|
|
381
578
|
};
|
|
382
579
|
}
|
|
383
580
|
}
|
|
384
|
-
const maxIterations =
|
|
581
|
+
const maxIterations = options?.maxIterations ?? MAX_LOOP_ITERATIONS;
|
|
385
582
|
for (let i = 0; i < maxIterations; i++) {
|
|
386
583
|
if (this.eventBus.isAborted) {
|
|
387
584
|
this.updateState({ status: 'cancelled' });
|
|
388
585
|
throw new Error('Agent run was aborted');
|
|
389
586
|
}
|
|
390
|
-
this.
|
|
587
|
+
this.eventBus.emit({ type: "turn_start" });
|
|
588
|
+
const { toolMap, aiTools, hasTools, effectiveInstructions } = this.buildToolLoopContext(staticLoopContext.aiProviderTools);
|
|
391
589
|
const result = await (0, ai_1.generateText)({
|
|
392
|
-
model,
|
|
393
|
-
messages: list.forLlm(
|
|
590
|
+
model: staticLoopContext.model,
|
|
591
|
+
messages: list.forLlm(effectiveInstructions, this.config.instructionProviderOptions),
|
|
394
592
|
abortSignal: this.eventBus.signal,
|
|
395
593
|
...(hasTools ? { tools: aiTools } : {}),
|
|
396
|
-
...(providerOptions
|
|
397
|
-
? { providerOptions: providerOptions }
|
|
594
|
+
...(staticLoopContext.providerOptions
|
|
595
|
+
? { providerOptions: staticLoopContext.providerOptions }
|
|
398
596
|
: {}),
|
|
399
|
-
...(outputSpec ? { output: outputSpec } : {}),
|
|
400
|
-
...this.buildTelemetryOptions(),
|
|
597
|
+
...(staticLoopContext.outputSpec ? { output: staticLoopContext.outputSpec } : {}),
|
|
598
|
+
...this.buildTelemetryOptions(options),
|
|
401
599
|
});
|
|
402
600
|
const aiFinishReason = result.finishReason;
|
|
403
601
|
lastFinishReason = (0, messages_1.fromAiFinishReason)(aiFinishReason);
|
|
404
602
|
totalUsage = (0, runtime_helpers_1.accumulateUsage)(totalUsage, result.usage);
|
|
603
|
+
this.incrementTokenCount(options?.executionCounter, result.usage);
|
|
405
604
|
const responseMessages = result.response.messages;
|
|
406
605
|
const newMessages = (0, messages_1.fromAiMessages)(responseMessages);
|
|
407
606
|
list.addResponse(newMessages);
|
|
408
607
|
if (aiFinishReason !== 'tool-calls') {
|
|
409
|
-
if (outputSpec) {
|
|
608
|
+
if (staticLoopContext.outputSpec) {
|
|
410
609
|
structuredOutput = result.output;
|
|
411
610
|
}
|
|
412
|
-
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.
|
|
611
|
+
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(newMessages));
|
|
413
612
|
break;
|
|
414
613
|
}
|
|
415
|
-
const batch = await this.iterateToolCallsConcurrent(
|
|
614
|
+
const batch = await this.iterateToolCallsConcurrent({
|
|
615
|
+
toolMap,
|
|
616
|
+
list,
|
|
617
|
+
runId,
|
|
618
|
+
telemetry: runTelemetry,
|
|
619
|
+
executionCounter: options?.executionCounter,
|
|
620
|
+
toolCalls: result.toolCalls,
|
|
621
|
+
});
|
|
416
622
|
for (const r of batch.results) {
|
|
417
623
|
toolCallSummary.push(r.toolEntry);
|
|
418
624
|
if (r.subAgentUsage)
|
|
419
625
|
collectedSubAgentUsage.push(...r.subAgentUsage);
|
|
420
626
|
}
|
|
421
627
|
if (Object.keys(batch.pending).length > 0) {
|
|
422
|
-
const suspendRunId = await this.persistSuspension(batch.pending, list, totalUsage);
|
|
628
|
+
const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId);
|
|
423
629
|
return {
|
|
424
630
|
runId: suspendRunId,
|
|
425
631
|
messages: list.responseDelta(),
|
|
@@ -433,44 +639,59 @@ class AgentRuntime {
|
|
|
433
639
|
suspendPayload: s.payload,
|
|
434
640
|
resumeSchema: s.resumeSchema,
|
|
435
641
|
})),
|
|
436
|
-
getState: () => this.getState(),
|
|
437
642
|
};
|
|
438
643
|
}
|
|
439
|
-
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.
|
|
644
|
+
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
|
|
440
645
|
}
|
|
441
646
|
if (lastFinishReason === 'tool-calls') {
|
|
442
647
|
throw new Error(`Agent loop exceeded ${maxIterations} iterations without reaching a stop condition`);
|
|
443
648
|
}
|
|
444
|
-
await this.saveToMemory(list);
|
|
445
|
-
await this.flushTelemetry();
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
void (0, title_generation_1.generateThreadTitle)({
|
|
649
|
+
await this.saveToMemory(list, options);
|
|
650
|
+
await this.flushTelemetry(options);
|
|
651
|
+
if (this.config.titleGeneration && options?.persistence?.threadId && this.config.memory) {
|
|
652
|
+
const titlePromise = (0, title_generation_1.generateThreadTitle)({
|
|
449
653
|
memory: this.config.memory,
|
|
450
|
-
threadId: persistence.threadId,
|
|
451
|
-
resourceId: persistence.resourceId,
|
|
654
|
+
threadId: options.persistence.threadId,
|
|
655
|
+
resourceId: options.persistence.resourceId,
|
|
452
656
|
titleConfig: this.config.titleGeneration,
|
|
453
657
|
agentModel: this.config.model,
|
|
454
658
|
turnDelta: list.turnDelta(),
|
|
455
659
|
});
|
|
660
|
+
this.backgroundTasks.track(titlePromise);
|
|
661
|
+
if (this.config.titleGeneration.sync) {
|
|
662
|
+
await titlePromise;
|
|
663
|
+
}
|
|
456
664
|
}
|
|
457
665
|
return {
|
|
458
|
-
runId:
|
|
666
|
+
runId: runId ?? '',
|
|
459
667
|
messages: list.responseDelta(),
|
|
460
668
|
finishReason: lastFinishReason,
|
|
461
669
|
usage: totalUsage,
|
|
462
670
|
...(structuredOutput !== undefined && { structuredOutput }),
|
|
463
671
|
...(toolCallSummary.length > 0 && { toolCalls: toolCallSummary }),
|
|
464
672
|
...(collectedSubAgentUsage.length > 0 && { subAgentUsage: collectedSubAgentUsage }),
|
|
465
|
-
getState: () => this.getState(),
|
|
466
673
|
};
|
|
467
674
|
}
|
|
468
|
-
startStreamLoop(
|
|
675
|
+
startStreamLoop(ctx) {
|
|
676
|
+
const { options, runId } = ctx;
|
|
469
677
|
const { readable, writable } = new TransformStream();
|
|
470
678
|
const writer = writable.getWriter();
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
679
|
+
const onToolExecutionStart = (data) => {
|
|
680
|
+
if (data.type !== "tool_execution_start")
|
|
681
|
+
return;
|
|
682
|
+
writer
|
|
683
|
+
.write({
|
|
684
|
+
type: 'tool-execution-start',
|
|
685
|
+
toolCallId: data.toolCallId,
|
|
686
|
+
toolName: data.toolName,
|
|
687
|
+
})
|
|
688
|
+
.catch(() => { });
|
|
689
|
+
};
|
|
690
|
+
this.eventBus.on("tool_execution_start", onToolExecutionStart);
|
|
691
|
+
this.withTelemetryRootSpan('stream', options, runId, async () => await this.runStreamLoop({ ...ctx, writer }))
|
|
692
|
+
.catch(async (error) => {
|
|
693
|
+
await this.flushTelemetry(options);
|
|
694
|
+
await this.cleanupRun(runId);
|
|
474
695
|
try {
|
|
475
696
|
await writer.write({ type: 'error', error });
|
|
476
697
|
await writer.write({ type: 'finish', finishReason: 'error' });
|
|
@@ -479,11 +700,15 @@ class AgentRuntime {
|
|
|
479
700
|
catch {
|
|
480
701
|
writer.abort(error).catch(() => { });
|
|
481
702
|
}
|
|
703
|
+
})
|
|
704
|
+
.finally(() => {
|
|
705
|
+
this.eventBus.off("tool_execution_start", onToolExecutionStart);
|
|
482
706
|
});
|
|
483
707
|
return readable;
|
|
484
708
|
}
|
|
485
|
-
async runStreamLoop(
|
|
486
|
-
const {
|
|
709
|
+
async runStreamLoop(ctx) {
|
|
710
|
+
const { list, options, runId, pendingResume, writer } = ctx;
|
|
711
|
+
this.hydrateDeferredToolsFromList(list);
|
|
487
712
|
const writeChunk = async (chunk) => {
|
|
488
713
|
await writer.write(chunk);
|
|
489
714
|
};
|
|
@@ -491,9 +716,9 @@ class AgentRuntime {
|
|
|
491
716
|
let lastFinishReason = 'stop';
|
|
492
717
|
let structuredOutput;
|
|
493
718
|
const collectedSubAgentUsage = [];
|
|
494
|
-
const maxIterations =
|
|
719
|
+
const maxIterations = options?.maxIterations ?? MAX_LOOP_ITERATIONS;
|
|
495
720
|
const closeStreamWithError = async (error, status) => {
|
|
496
|
-
await this.cleanupRun();
|
|
721
|
+
await this.cleanupRun(runId);
|
|
497
722
|
this.updateState({ status });
|
|
498
723
|
await writer.write({ type: 'error', error });
|
|
499
724
|
await writer.write({ type: 'finish', finishReason: 'error' });
|
|
@@ -505,16 +730,33 @@ class AgentRuntime {
|
|
|
505
730
|
await closeStreamWithError(new Error('Agent run was aborted'), 'cancelled');
|
|
506
731
|
return true;
|
|
507
732
|
};
|
|
508
|
-
const runTelemetry = this.resolveTelemetry();
|
|
733
|
+
const runTelemetry = this.resolveTelemetry(options);
|
|
734
|
+
const staticLoopContext = this.buildStaticLoopContext({
|
|
735
|
+
...options,
|
|
736
|
+
persistence: options?.persistence,
|
|
737
|
+
});
|
|
738
|
+
const pendingLoopContext = this.buildToolLoopContext(staticLoopContext.aiProviderTools);
|
|
739
|
+
const pendingToolCtx = {
|
|
740
|
+
toolMap: pendingLoopContext.toolMap,
|
|
741
|
+
list,
|
|
742
|
+
runId,
|
|
743
|
+
telemetry: runTelemetry,
|
|
744
|
+
executionCounter: options?.executionCounter,
|
|
745
|
+
};
|
|
509
746
|
if (pendingResume) {
|
|
510
747
|
try {
|
|
511
|
-
const batch = await this.iteratePendingToolCallsConcurrent(
|
|
748
|
+
const batch = await this.iteratePendingToolCallsConcurrent({
|
|
749
|
+
...pendingToolCtx,
|
|
750
|
+
pendingResume,
|
|
751
|
+
});
|
|
512
752
|
for (const r of batch.results) {
|
|
513
753
|
if (r.subAgentUsage)
|
|
514
754
|
collectedSubAgentUsage.push(...r.subAgentUsage);
|
|
515
755
|
await writer.write({
|
|
516
|
-
type: '
|
|
517
|
-
|
|
756
|
+
type: 'tool-result',
|
|
757
|
+
toolCallId: r.toolCallId,
|
|
758
|
+
toolName: r.toolName,
|
|
759
|
+
output: r.modelOutput,
|
|
518
760
|
});
|
|
519
761
|
if (r.customMessage) {
|
|
520
762
|
await writer.write({ type: 'message', message: r.customMessage });
|
|
@@ -522,12 +764,15 @@ class AgentRuntime {
|
|
|
522
764
|
}
|
|
523
765
|
for (const e of batch.errors) {
|
|
524
766
|
await writer.write({
|
|
525
|
-
type: '
|
|
526
|
-
|
|
767
|
+
type: 'tool-result',
|
|
768
|
+
toolCallId: e.toolCallId,
|
|
769
|
+
toolName: e.toolName,
|
|
770
|
+
output: e.error,
|
|
771
|
+
isError: true,
|
|
527
772
|
});
|
|
528
773
|
}
|
|
529
774
|
if (Object.keys(batch.pending).length > 0) {
|
|
530
|
-
const suspendRunId = await this.persistSuspension(batch.pending, list, totalUsage);
|
|
775
|
+
const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId);
|
|
531
776
|
for (const s of batch.suspensions) {
|
|
532
777
|
await writer.write({
|
|
533
778
|
type: 'tool-call-suspended',
|
|
@@ -545,7 +790,7 @@ class AgentRuntime {
|
|
|
545
790
|
}
|
|
546
791
|
}
|
|
547
792
|
catch (error) {
|
|
548
|
-
this.
|
|
793
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
549
794
|
await closeStreamWithError(error, 'failed');
|
|
550
795
|
return;
|
|
551
796
|
}
|
|
@@ -553,21 +798,23 @@ class AgentRuntime {
|
|
|
553
798
|
for (let i = 0; i < maxIterations; i++) {
|
|
554
799
|
if (await handleAbort())
|
|
555
800
|
return;
|
|
556
|
-
this.
|
|
801
|
+
this.eventBus.emit({ type: "turn_start" });
|
|
802
|
+
const { toolMap, aiTools, hasTools, effectiveInstructions } = this.buildToolLoopContext(staticLoopContext.aiProviderTools);
|
|
803
|
+
const messages = list.forLlm(effectiveInstructions, this.config.instructionProviderOptions);
|
|
557
804
|
const result = (0, ai_1.streamText)({
|
|
558
|
-
model,
|
|
559
|
-
messages
|
|
805
|
+
model: staticLoopContext.model,
|
|
806
|
+
messages,
|
|
560
807
|
abortSignal: this.eventBus.signal,
|
|
561
808
|
...(hasTools ? { tools: aiTools } : {}),
|
|
562
|
-
...(providerOptions
|
|
563
|
-
? { providerOptions: providerOptions }
|
|
809
|
+
...(staticLoopContext.providerOptions
|
|
810
|
+
? { providerOptions: staticLoopContext.providerOptions }
|
|
564
811
|
: {}),
|
|
565
|
-
...(outputSpec ? { output: outputSpec } : {}),
|
|
566
|
-
...this.buildTelemetryOptions(),
|
|
812
|
+
...(staticLoopContext.outputSpec ? { output: staticLoopContext.outputSpec } : {}),
|
|
813
|
+
...this.buildTelemetryOptions(options),
|
|
567
814
|
});
|
|
568
815
|
try {
|
|
569
816
|
for await (const chunk of result.fullStream) {
|
|
570
|
-
if (chunk.type === 'finish'
|
|
817
|
+
if (chunk.type === 'finish')
|
|
571
818
|
continue;
|
|
572
819
|
const converted = (0, stream_1.convertChunk)(chunk);
|
|
573
820
|
if (converted)
|
|
@@ -577,7 +824,7 @@ class AgentRuntime {
|
|
|
577
824
|
catch (streamError) {
|
|
578
825
|
if (await handleAbort())
|
|
579
826
|
return;
|
|
580
|
-
this.
|
|
827
|
+
this.eventBus.emit({
|
|
581
828
|
type: "error",
|
|
582
829
|
message: String(streamError),
|
|
583
830
|
error: streamError,
|
|
@@ -592,27 +839,37 @@ class AgentRuntime {
|
|
|
592
839
|
const response = await result.response;
|
|
593
840
|
lastFinishReason = (0, messages_1.fromAiFinishReason)(aiFinishReason);
|
|
594
841
|
totalUsage = (0, runtime_helpers_1.accumulateUsage)(totalUsage, usage);
|
|
842
|
+
this.incrementTokenCount(options?.executionCounter, usage);
|
|
595
843
|
const responseMessages = response.messages;
|
|
596
844
|
const newMessages = (0, messages_1.fromAiMessages)(responseMessages);
|
|
597
845
|
list.addResponse(newMessages);
|
|
598
846
|
if (aiFinishReason !== 'tool-calls') {
|
|
599
|
-
if (outputSpec) {
|
|
847
|
+
if (staticLoopContext.outputSpec) {
|
|
600
848
|
structuredOutput = await result.output;
|
|
601
849
|
}
|
|
602
|
-
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.
|
|
850
|
+
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(newMessages));
|
|
603
851
|
break;
|
|
604
852
|
}
|
|
605
853
|
const toolCalls = await result.toolCalls;
|
|
606
854
|
try {
|
|
607
|
-
const batch = await this.iterateToolCallsConcurrent(
|
|
855
|
+
const batch = await this.iterateToolCallsConcurrent({
|
|
856
|
+
toolMap,
|
|
857
|
+
list,
|
|
858
|
+
runId,
|
|
859
|
+
telemetry: runTelemetry,
|
|
860
|
+
executionCounter: options?.executionCounter,
|
|
861
|
+
toolCalls,
|
|
862
|
+
});
|
|
608
863
|
if (await handleAbort())
|
|
609
864
|
return;
|
|
610
865
|
for (const r of batch.results) {
|
|
611
866
|
if (r.subAgentUsage)
|
|
612
867
|
collectedSubAgentUsage.push(...r.subAgentUsage);
|
|
613
868
|
await writer.write({
|
|
614
|
-
type: '
|
|
615
|
-
|
|
869
|
+
type: 'tool-result',
|
|
870
|
+
toolCallId: r.toolCallId,
|
|
871
|
+
toolName: r.toolName,
|
|
872
|
+
output: r.modelOutput,
|
|
616
873
|
});
|
|
617
874
|
if (r.customMessage) {
|
|
618
875
|
await writer.write({ type: 'message', message: r.customMessage });
|
|
@@ -620,12 +877,15 @@ class AgentRuntime {
|
|
|
620
877
|
}
|
|
621
878
|
for (const e of batch.errors) {
|
|
622
879
|
await writer.write({
|
|
623
|
-
type: '
|
|
624
|
-
|
|
880
|
+
type: 'tool-result',
|
|
881
|
+
toolCallId: e.toolCallId,
|
|
882
|
+
toolName: e.toolName,
|
|
883
|
+
output: e.error,
|
|
884
|
+
isError: true,
|
|
625
885
|
});
|
|
626
886
|
}
|
|
627
887
|
if (Object.keys(batch.pending).length > 0) {
|
|
628
|
-
const suspendRunId = await this.persistSuspension(batch.pending, list, totalUsage);
|
|
888
|
+
const suspendRunId = await this.persistSuspension(batch.pending, options, list, totalUsage, runId);
|
|
629
889
|
for (const s of batch.suspensions) {
|
|
630
890
|
await writer.write({
|
|
631
891
|
type: 'tool-call-suspended',
|
|
@@ -643,11 +903,11 @@ class AgentRuntime {
|
|
|
643
903
|
}
|
|
644
904
|
}
|
|
645
905
|
catch (error) {
|
|
646
|
-
this.
|
|
906
|
+
this.eventBus.emit({ type: "error", message: String(error), error });
|
|
647
907
|
await closeStreamWithError(error, 'failed');
|
|
648
908
|
return;
|
|
649
909
|
}
|
|
650
|
-
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.
|
|
910
|
+
this.emitTurnEnd(newMessages, (0, runtime_helpers_1.extractSettledToolCalls)(list.responseDelta()));
|
|
651
911
|
}
|
|
652
912
|
const costUsage = this.applyCost(totalUsage);
|
|
653
913
|
const parentCost = costUsage?.cost ?? 0;
|
|
@@ -664,38 +924,101 @@ class AgentRuntime {
|
|
|
664
924
|
}),
|
|
665
925
|
});
|
|
666
926
|
try {
|
|
667
|
-
await this.saveToMemory(list);
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
void (0, title_generation_1.generateThreadTitle)({
|
|
927
|
+
await this.saveToMemory(list, options);
|
|
928
|
+
if (this.config.titleGeneration && options?.persistence && this.config.memory) {
|
|
929
|
+
const titlePromise = (0, title_generation_1.generateThreadTitle)({
|
|
671
930
|
memory: this.config.memory,
|
|
672
|
-
threadId: persistence.threadId,
|
|
673
|
-
resourceId: persistence.resourceId,
|
|
931
|
+
threadId: options.persistence.threadId,
|
|
932
|
+
resourceId: options.persistence.resourceId,
|
|
674
933
|
titleConfig: this.config.titleGeneration,
|
|
675
934
|
agentModel: this.config.model,
|
|
676
935
|
turnDelta: list.turnDelta(),
|
|
677
936
|
});
|
|
937
|
+
this.backgroundTasks.track(titlePromise);
|
|
938
|
+
if (this.config.titleGeneration.sync) {
|
|
939
|
+
await titlePromise;
|
|
940
|
+
}
|
|
678
941
|
}
|
|
679
|
-
await this.cleanupRun();
|
|
680
|
-
await this.flushTelemetry();
|
|
942
|
+
await this.cleanupRun(runId);
|
|
943
|
+
await this.flushTelemetry(options);
|
|
681
944
|
this.updateState({ status: 'success', messageList: list.serialize() });
|
|
682
|
-
this.
|
|
945
|
+
this.eventBus.emit({ type: "agent_end", messages: list.responseDelta() });
|
|
683
946
|
}
|
|
684
947
|
finally {
|
|
685
948
|
await writer.close();
|
|
686
949
|
}
|
|
687
950
|
}
|
|
688
|
-
async saveToMemory(list) {
|
|
689
|
-
|
|
690
|
-
if (!this.config.memory || !persistence)
|
|
951
|
+
async saveToMemory(list, options) {
|
|
952
|
+
if (!this.config.memory || !options?.persistence)
|
|
691
953
|
return;
|
|
692
954
|
const delta = list.turnDelta();
|
|
693
955
|
if (delta.length === 0)
|
|
694
956
|
return;
|
|
695
|
-
await (0, memory_store_1.saveMessagesToThread)(this.config.memory, persistence.threadId, persistence.resourceId, delta);
|
|
957
|
+
await (0, memory_store_1.saveMessagesToThread)(this.config.memory, options.persistence.threadId, options.persistence.resourceId, delta);
|
|
696
958
|
if (this.config.semanticRecall?.embedder && this.config.memory.saveEmbeddings) {
|
|
697
|
-
await this.saveEmbeddingsForMessages(persistence.threadId, persistence.resourceId, delta);
|
|
959
|
+
await this.saveEmbeddingsForMessages(options.persistence.threadId, options.persistence.resourceId, delta);
|
|
698
960
|
}
|
|
961
|
+
this.scheduleObservationLogJobs(options.persistence);
|
|
962
|
+
}
|
|
963
|
+
scheduleObservationLogJobs(persistence) {
|
|
964
|
+
const { memory, observationalMemory } = this.config;
|
|
965
|
+
if (!memory || !observationalMemory || !(0, observation_log_store_1.hasObservationLogStore)(memory))
|
|
966
|
+
return;
|
|
967
|
+
const scope = this.getObservationLogScope(persistence);
|
|
968
|
+
const runner = this.getMemoryTaskRunner(memory, observationalMemory);
|
|
969
|
+
const observe = observationalMemory.observe;
|
|
970
|
+
const observerThresholdTokens = observationalMemory.observerThresholdTokens;
|
|
971
|
+
if (observe &&
|
|
972
|
+
observerThresholdTokens !== undefined &&
|
|
973
|
+
hasObservationLogObserverMemory(memory)) {
|
|
974
|
+
runner.schedule({ ...scope, taskKind: 'observer' }, async () => await (0, observation_log_observer_1.runObservationLogObserver)({
|
|
975
|
+
memory,
|
|
976
|
+
...scope,
|
|
977
|
+
observerThresholdTokens,
|
|
978
|
+
observationLogTailLimit: observationalMemory.observationLogTailLimit ?? 0,
|
|
979
|
+
observe,
|
|
980
|
+
messageSource: {
|
|
981
|
+
threadId: persistence.threadId,
|
|
982
|
+
resourceId: persistence.resourceId,
|
|
983
|
+
},
|
|
984
|
+
}));
|
|
985
|
+
}
|
|
986
|
+
const reflect = observationalMemory.reflect;
|
|
987
|
+
const reflectorThresholdTokens = observationalMemory.reflectorThresholdTokens;
|
|
988
|
+
if (reflect && reflectorThresholdTokens !== undefined) {
|
|
989
|
+
runner.schedule({ ...scope, taskKind: 'reflector' }, async () => await (0, observation_log_reflector_1.runObservationLogReflector)({
|
|
990
|
+
memory,
|
|
991
|
+
...scope,
|
|
992
|
+
reflectorThresholdTokens,
|
|
993
|
+
reflect,
|
|
994
|
+
}));
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
getMemoryTaskRunner(memory, observationalMemory) {
|
|
998
|
+
this.memoryTasks ??= new scoped_memory_task_runner_1.ScopedMemoryTaskRunner({
|
|
999
|
+
tracker: this.backgroundTasks,
|
|
1000
|
+
lockStore: (0, observation_log_store_1.hasObservationLogTaskLockStore)(memory) ? memory : undefined,
|
|
1001
|
+
lockTtlMs: observationalMemory.lockTtlMs,
|
|
1002
|
+
onEvent: (event) => {
|
|
1003
|
+
if (event.type !== 'failed')
|
|
1004
|
+
return;
|
|
1005
|
+
const source = event.task.taskKind;
|
|
1006
|
+
const message = `Observation log ${source} task failed`;
|
|
1007
|
+
logger.warn(message, {
|
|
1008
|
+
error: event.error,
|
|
1009
|
+
scopeKind: event.task.scopeKind,
|
|
1010
|
+
scopeId: event.task.scopeId,
|
|
1011
|
+
});
|
|
1012
|
+
this.eventBus.emit({ type: "error", message, error: event.error, source });
|
|
1013
|
+
},
|
|
1014
|
+
});
|
|
1015
|
+
return this.memoryTasks;
|
|
1016
|
+
}
|
|
1017
|
+
getObservationLogScope(persistence) {
|
|
1018
|
+
return {
|
|
1019
|
+
scopeKind: 'thread',
|
|
1020
|
+
scopeId: (0, observation_log_1.createObservationLogThreadScopeId)(persistence.threadId, persistence.resourceId),
|
|
1021
|
+
};
|
|
699
1022
|
}
|
|
700
1023
|
async saveEmbeddingsForMessages(threadId, resourceId, messages) {
|
|
701
1024
|
const embeddable = [];
|
|
@@ -741,12 +1064,12 @@ class AgentRuntime {
|
|
|
741
1064
|
switch (provider) {
|
|
742
1065
|
case 'anthropic': {
|
|
743
1066
|
const cfg = thinking;
|
|
1067
|
+
if (cfg.mode === 'adaptive') {
|
|
1068
|
+
return { anthropic: { thinking: { type: 'adaptive' } } };
|
|
1069
|
+
}
|
|
744
1070
|
return {
|
|
745
1071
|
anthropic: {
|
|
746
|
-
thinking: {
|
|
747
|
-
type: 'enabled',
|
|
748
|
-
budgetTokens: cfg.budgetTokens ?? 10000,
|
|
749
|
-
},
|
|
1072
|
+
thinking: { type: 'enabled', budgetTokens: cfg.budgetTokens ?? 10000 },
|
|
750
1073
|
},
|
|
751
1074
|
};
|
|
752
1075
|
}
|
|
@@ -792,8 +1115,13 @@ class AgentRuntime {
|
|
|
792
1115
|
}
|
|
793
1116
|
return merged;
|
|
794
1117
|
}
|
|
795
|
-
async iterateToolCallsConcurrent(
|
|
1118
|
+
async iterateToolCallsConcurrent(ctx) {
|
|
1119
|
+
const { toolCalls, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter } = ctx;
|
|
796
1120
|
const executableCalls = toolCalls.filter((tc) => !tc.providerExecuted);
|
|
1121
|
+
const providerExecutedCount = toolCalls.length - executableCalls.length;
|
|
1122
|
+
for (let i = 0; i < providerExecutedCount; i++) {
|
|
1123
|
+
this.incrementToolCallCount(executionCounter);
|
|
1124
|
+
}
|
|
797
1125
|
const executableCallsById = new Map(executableCalls.map((tc) => [tc.toolCallId, tc]));
|
|
798
1126
|
const unexecutedIds = new Set(executableCalls.map((tc) => tc.toolCallId));
|
|
799
1127
|
const batchSize = this.concurrency;
|
|
@@ -807,7 +1135,7 @@ class AgentRuntime {
|
|
|
807
1135
|
throw new Error('Agent run was aborted');
|
|
808
1136
|
}
|
|
809
1137
|
const batch = executableCalls.slice(batchStart, batchStart + batchSize);
|
|
810
|
-
const settledResults = await Promise.allSettled(batch.map(async (tc) => await this.processToolCall(tc.toolCallId, tc.toolName, tc.input, toolMap, list, undefined, resolvedTelemetry)));
|
|
1138
|
+
const settledResults = await Promise.allSettled(batch.map(async (tc) => await this.processToolCall(tc.toolCallId, tc.toolName, tc.input, toolMap, list, undefined, resolvedTelemetry, executionCounter, true)));
|
|
811
1139
|
for (const tc of batch) {
|
|
812
1140
|
unexecutedIds.delete(tc.toolCallId);
|
|
813
1141
|
}
|
|
@@ -817,12 +1145,12 @@ class AgentRuntime {
|
|
|
817
1145
|
const tc = batch[i];
|
|
818
1146
|
const toolInput = tc.input;
|
|
819
1147
|
if (result.status === 'rejected') {
|
|
1148
|
+
list.setToolCallError(tc.toolCallId, result.reason);
|
|
820
1149
|
errors.push({
|
|
821
1150
|
toolCallId: tc.toolCallId,
|
|
822
1151
|
toolName: tc.toolName,
|
|
823
1152
|
input: toolInput,
|
|
824
1153
|
error: result.reason,
|
|
825
|
-
message: (0, runtime_helpers_1.makeErrorToolResultMessage)(tc.toolCallId, tc.toolName, result.reason),
|
|
826
1154
|
});
|
|
827
1155
|
}
|
|
828
1156
|
else if (result.value.outcome === 'suspended') {
|
|
@@ -841,6 +1169,7 @@ class AgentRuntime {
|
|
|
841
1169
|
input: toolInput,
|
|
842
1170
|
suspendPayload: result.value.payload,
|
|
843
1171
|
resumeSchema: result.value.resumeSchema,
|
|
1172
|
+
runId,
|
|
844
1173
|
};
|
|
845
1174
|
}
|
|
846
1175
|
else if (result.value.outcome === 'success') {
|
|
@@ -849,9 +1178,9 @@ class AgentRuntime {
|
|
|
849
1178
|
toolName: tc.toolName,
|
|
850
1179
|
input: toolInput,
|
|
851
1180
|
toolEntry: result.value.toolEntry,
|
|
1181
|
+
modelOutput: result.value.modelOutput,
|
|
852
1182
|
subAgentUsage: result.value.subAgentUsage,
|
|
853
1183
|
customMessage: result.value.customMessage,
|
|
854
|
-
message: result.value.message,
|
|
855
1184
|
});
|
|
856
1185
|
}
|
|
857
1186
|
else if (result.value.outcome === 'error') {
|
|
@@ -860,7 +1189,6 @@ class AgentRuntime {
|
|
|
860
1189
|
toolName: tc.toolName,
|
|
861
1190
|
input: toolInput,
|
|
862
1191
|
error: result.value.error,
|
|
863
|
-
message: result.value.message,
|
|
864
1192
|
});
|
|
865
1193
|
}
|
|
866
1194
|
else if (result.value.outcome === 'noop') {
|
|
@@ -881,7 +1209,8 @@ class AgentRuntime {
|
|
|
881
1209
|
}
|
|
882
1210
|
return { results, suspensions, errors, pending };
|
|
883
1211
|
}
|
|
884
|
-
async iteratePendingToolCallsConcurrent(
|
|
1212
|
+
async iteratePendingToolCallsConcurrent(ctx) {
|
|
1213
|
+
const { pendingResume, toolMap, list, runId, telemetry: resolvedTelemetry, executionCounter, } = ctx;
|
|
885
1214
|
const resumedId = pendingResume.resumeToolCallId;
|
|
886
1215
|
const resumedEntry = pendingResume.pendingToolCalls[resumedId];
|
|
887
1216
|
if (!resumedEntry) {
|
|
@@ -892,13 +1221,14 @@ class AgentRuntime {
|
|
|
892
1221
|
const suspensions = [];
|
|
893
1222
|
const errors = [];
|
|
894
1223
|
const pending = {};
|
|
895
|
-
const processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, pendingResume.resumeData, resolvedTelemetry);
|
|
1224
|
+
const processResult = await this.processToolCall(resumedEntry.toolCallId, resumedToolName, resumedEntry.input, toolMap, list, pendingResume.resumeData, resolvedTelemetry, executionCounter, false);
|
|
896
1225
|
if (processResult.outcome === 'suspended') {
|
|
897
1226
|
pending[resumedId] = {
|
|
898
1227
|
...resumedEntry,
|
|
899
1228
|
suspended: true,
|
|
900
1229
|
suspendPayload: processResult.payload,
|
|
901
1230
|
resumeSchema: processResult.resumeSchema,
|
|
1231
|
+
runId,
|
|
902
1232
|
};
|
|
903
1233
|
suspensions.push({
|
|
904
1234
|
toolCallId: resumedId,
|
|
@@ -914,9 +1244,9 @@ class AgentRuntime {
|
|
|
914
1244
|
toolName: resumedToolName,
|
|
915
1245
|
input: resumedEntry.input,
|
|
916
1246
|
toolEntry: processResult.toolEntry,
|
|
1247
|
+
modelOutput: processResult.modelOutput,
|
|
917
1248
|
subAgentUsage: processResult.subAgentUsage,
|
|
918
1249
|
customMessage: processResult.customMessage,
|
|
919
|
-
message: processResult.message,
|
|
920
1250
|
});
|
|
921
1251
|
}
|
|
922
1252
|
else if (processResult.outcome === 'error') {
|
|
@@ -925,7 +1255,6 @@ class AgentRuntime {
|
|
|
925
1255
|
toolName: resumedToolName,
|
|
926
1256
|
input: resumedEntry.input,
|
|
927
1257
|
error: processResult.error,
|
|
928
|
-
message: processResult.message,
|
|
929
1258
|
});
|
|
930
1259
|
}
|
|
931
1260
|
else if (processResult.outcome === 'noop') {
|
|
@@ -954,7 +1283,14 @@ class AgentRuntime {
|
|
|
954
1283
|
}
|
|
955
1284
|
}
|
|
956
1285
|
if (unexecuted.length > 0) {
|
|
957
|
-
const batch = await this.iterateToolCallsConcurrent(
|
|
1286
|
+
const batch = await this.iterateToolCallsConcurrent({
|
|
1287
|
+
toolCalls: unexecuted,
|
|
1288
|
+
toolMap,
|
|
1289
|
+
list,
|
|
1290
|
+
runId,
|
|
1291
|
+
telemetry: resolvedTelemetry,
|
|
1292
|
+
executionCounter,
|
|
1293
|
+
});
|
|
958
1294
|
results.push(...batch.results);
|
|
959
1295
|
suspensions.push(...batch.suspensions);
|
|
960
1296
|
errors.push(...batch.errors);
|
|
@@ -962,73 +1298,71 @@ class AgentRuntime {
|
|
|
962
1298
|
}
|
|
963
1299
|
return { results, suspensions, errors, pending };
|
|
964
1300
|
}
|
|
965
|
-
async processToolCall(toolCallId, toolName, toolInput, toolMap, list, resumeData, resolvedTelemetry) {
|
|
1301
|
+
async processToolCall(toolCallId, toolName, toolInput, toolMap, list, resumeData, resolvedTelemetry, executionCounter, countToolCall = true) {
|
|
966
1302
|
const builtTool = toolMap.get(toolName);
|
|
967
|
-
this.
|
|
1303
|
+
this.eventBus.emit({
|
|
968
1304
|
type: "tool_execution_start",
|
|
969
1305
|
toolCallId,
|
|
970
1306
|
toolName,
|
|
971
1307
|
args: toolInput,
|
|
972
1308
|
});
|
|
973
1309
|
const makeToolError = (error) => {
|
|
974
|
-
this.
|
|
1310
|
+
this.eventBus.emit({
|
|
975
1311
|
type: "tool_execution_end",
|
|
976
1312
|
toolCallId,
|
|
977
1313
|
toolName,
|
|
978
1314
|
result: error,
|
|
979
1315
|
isError: true,
|
|
980
1316
|
});
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
return { outcome: 'error', error, message: errorMsg };
|
|
1317
|
+
list.setToolCallError(toolCallId, error);
|
|
1318
|
+
return { outcome: 'error', error };
|
|
984
1319
|
};
|
|
985
1320
|
if (!builtTool) {
|
|
986
1321
|
return makeToolError(new Error(`Tool ${toolName} not found`));
|
|
987
1322
|
}
|
|
988
|
-
const
|
|
1323
|
+
const settledBlock = list
|
|
989
1324
|
.responseDelta()
|
|
990
|
-
.
|
|
991
|
-
.
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1325
|
+
.flatMap((m) => ((0, message_1.isLlmMessage)(m) && 'content' in m ? m.content : []))
|
|
1326
|
+
.find((c) => c.type === 'tool-call' && c.toolCallId === toolCallId && c.state !== 'pending');
|
|
1327
|
+
if (settledBlock) {
|
|
1328
|
+
let settledResult;
|
|
1329
|
+
if (settledBlock.state === 'resolved') {
|
|
1330
|
+
settledResult = settledBlock.output;
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
settledResult = settledBlock.error;
|
|
1334
|
+
}
|
|
1335
|
+
this.eventBus.emit({
|
|
995
1336
|
type: "tool_execution_end",
|
|
996
1337
|
toolCallId,
|
|
997
1338
|
toolName,
|
|
998
|
-
result:
|
|
999
|
-
isError:
|
|
1339
|
+
result: settledResult,
|
|
1340
|
+
isError: settledBlock.state === 'rejected',
|
|
1000
1341
|
});
|
|
1001
1342
|
return { outcome: 'noop' };
|
|
1002
1343
|
}
|
|
1344
|
+
if (countToolCall) {
|
|
1345
|
+
this.incrementToolCallCount(executionCounter);
|
|
1346
|
+
}
|
|
1003
1347
|
if (builtTool.inputSchema) {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
return makeToolError(new Error(`Invalid tool input: ${result.error.message}`));
|
|
1008
|
-
}
|
|
1009
|
-
toolInput = result.data;
|
|
1010
|
-
}
|
|
1011
|
-
else {
|
|
1012
|
-
const validate = ajv.compile(builtTool.inputSchema);
|
|
1013
|
-
const valid = validate(toolInput);
|
|
1014
|
-
if (!valid) {
|
|
1015
|
-
const message = ajv.errorsText(validate.errors);
|
|
1016
|
-
return makeToolError(new Error(`Invalid tool input: ${message}`));
|
|
1017
|
-
}
|
|
1348
|
+
const result = await (0, parse_1.parseWithSchema)(builtTool.inputSchema, toolInput);
|
|
1349
|
+
if (!result.success) {
|
|
1350
|
+
return makeToolError(new Error(`Invalid tool input: ${result.error}`));
|
|
1018
1351
|
}
|
|
1352
|
+
toolInput = result.data;
|
|
1019
1353
|
}
|
|
1020
1354
|
let toolResult;
|
|
1021
1355
|
try {
|
|
1022
|
-
toolResult = await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry);
|
|
1356
|
+
toolResult = await this.withTelemetryToolSpan(toolCallId, toolName, toolInput, resolvedTelemetry, async () => await (0, tool_adapter_1.executeTool)(toolInput, builtTool, resumeData, resolvedTelemetry, toolCallId));
|
|
1023
1357
|
}
|
|
1024
1358
|
catch (error) {
|
|
1025
1359
|
return makeToolError(error);
|
|
1026
1360
|
}
|
|
1027
1361
|
if ((0, tool_adapter_1.isSuspendedToolResult)(toolResult)) {
|
|
1028
1362
|
if (builtTool?.suspendSchema) {
|
|
1029
|
-
const parseResult = await builtTool.suspendSchema
|
|
1363
|
+
const parseResult = await (0, parse_1.parseWithSchema)(builtTool.suspendSchema, toolResult.payload);
|
|
1030
1364
|
if (!parseResult.success) {
|
|
1031
|
-
return makeToolError(new Error(`Invalid suspend payload: ${parseResult.error
|
|
1365
|
+
return makeToolError(new Error(`Invalid suspend payload: ${parseResult.error}`));
|
|
1032
1366
|
}
|
|
1033
1367
|
toolResult.payload = parseResult.data;
|
|
1034
1368
|
}
|
|
@@ -1036,8 +1370,17 @@ class AgentRuntime {
|
|
|
1036
1370
|
const error = new Error(`Tool ${toolName} has no resume schema`);
|
|
1037
1371
|
return makeToolError(error);
|
|
1038
1372
|
}
|
|
1039
|
-
const resumeSchema = (0,
|
|
1040
|
-
|
|
1373
|
+
const resumeSchema = (0, zod_1.isZodSchema)(builtTool.resumeSchema)
|
|
1374
|
+
? (0, zod_to_json_schema_1.zodToJsonSchema)(builtTool.resumeSchema)
|
|
1375
|
+
: builtTool.resumeSchema;
|
|
1376
|
+
if (!resumeSchema) {
|
|
1377
|
+
return makeToolError(new Error('Invalid resume schema'));
|
|
1378
|
+
}
|
|
1379
|
+
return {
|
|
1380
|
+
outcome: 'suspended',
|
|
1381
|
+
payload: toolResult.payload,
|
|
1382
|
+
resumeSchema,
|
|
1383
|
+
};
|
|
1041
1384
|
}
|
|
1042
1385
|
let actualResult = toolResult;
|
|
1043
1386
|
let extractedSubAgentUsage;
|
|
@@ -1045,7 +1388,7 @@ class AgentRuntime {
|
|
|
1045
1388
|
actualResult = toolResult.output;
|
|
1046
1389
|
extractedSubAgentUsage = toolResult.subAgentUsage;
|
|
1047
1390
|
}
|
|
1048
|
-
this.
|
|
1391
|
+
this.eventBus.emit({
|
|
1049
1392
|
type: "tool_execution_end",
|
|
1050
1393
|
toolCallId,
|
|
1051
1394
|
toolName,
|
|
@@ -1055,8 +1398,7 @@ class AgentRuntime {
|
|
|
1055
1398
|
const modelResult = builtTool.toModelOutput
|
|
1056
1399
|
? builtTool.toModelOutput(actualResult)
|
|
1057
1400
|
: actualResult;
|
|
1058
|
-
|
|
1059
|
-
list.addResponse([toolResultMsg]);
|
|
1401
|
+
list.setToolCallResult(toolCallId, (0, json_value_1.toJsonValue)(modelResult));
|
|
1060
1402
|
const customMessage = builtTool?.toMessage?.(actualResult);
|
|
1061
1403
|
if (customMessage) {
|
|
1062
1404
|
list.addResponse([customMessage]);
|
|
@@ -1069,65 +1411,86 @@ class AgentRuntime {
|
|
|
1069
1411
|
output: actualResult,
|
|
1070
1412
|
transformed: !!builtTool.toModelOutput,
|
|
1071
1413
|
},
|
|
1414
|
+
modelOutput: modelResult,
|
|
1072
1415
|
subAgentUsage: extractedSubAgentUsage,
|
|
1073
1416
|
customMessage,
|
|
1074
|
-
message: toolResultMsg,
|
|
1075
1417
|
};
|
|
1076
1418
|
}
|
|
1077
|
-
|
|
1078
|
-
const wmTool = this.buildWorkingMemoryToolForRun(this.currentState.persistence);
|
|
1079
|
-
const allUserTools = wmTool
|
|
1080
|
-
? [...(this.config.tools ?? []), wmTool]
|
|
1081
|
-
: (this.config.tools ?? []);
|
|
1082
|
-
const aiTools = (0, tool_adapter_1.toAiSdkTools)(allUserTools);
|
|
1419
|
+
buildStaticLoopContext(execOptions) {
|
|
1083
1420
|
const aiProviderTools = (0, tool_adapter_1.toAiSdkProviderTools)(this.config.providerTools);
|
|
1084
|
-
const
|
|
1421
|
+
const model = (0, model_factory_1.createModel)(this.config.model);
|
|
1085
1422
|
return {
|
|
1086
|
-
model
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
providerOptions: this.buildCallProviderOptions(this.executionOptions?.providerOptions),
|
|
1090
|
-
hasTools: Object.keys(allTools).length > 0,
|
|
1423
|
+
model,
|
|
1424
|
+
aiProviderTools,
|
|
1425
|
+
providerOptions: this.buildCallProviderOptions(execOptions?.providerOptions),
|
|
1091
1426
|
outputSpec: this.config.structuredOutput
|
|
1092
1427
|
? ai_1.Output.object({ schema: this.config.structuredOutput })
|
|
1093
1428
|
: undefined,
|
|
1094
1429
|
};
|
|
1095
1430
|
}
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1431
|
+
buildToolLoopContext(aiProviderTools) {
|
|
1432
|
+
const allUserTools = this.getCurrentTools();
|
|
1433
|
+
const aiTools = (0, tool_adapter_1.toAiSdkTools)(allUserTools);
|
|
1434
|
+
const allTools = { ...aiTools, ...aiProviderTools };
|
|
1435
|
+
const aiToolCount = Object.keys(allTools).length;
|
|
1436
|
+
const toolMap = (0, tool_adapter_1.buildToolMap)(allUserTools);
|
|
1437
|
+
const effectiveInstructions = this.composeEffectiveInstructions(allUserTools);
|
|
1438
|
+
return {
|
|
1439
|
+
toolMap,
|
|
1440
|
+
aiTools: allTools,
|
|
1441
|
+
hasTools: aiToolCount > 0,
|
|
1442
|
+
effectiveInstructions,
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
getCurrentTools() {
|
|
1446
|
+
const baseTools = this.config.tools ?? [];
|
|
1447
|
+
if (!this.deferredToolManager?.hasTools)
|
|
1448
|
+
return baseTools;
|
|
1449
|
+
return [
|
|
1450
|
+
...baseTools,
|
|
1451
|
+
...this.deferredToolManager.getControllerTools(),
|
|
1452
|
+
...this.deferredToolManager.getLoadedTools(),
|
|
1453
|
+
];
|
|
1454
|
+
}
|
|
1455
|
+
hydrateDeferredToolsFromList(list) {
|
|
1456
|
+
if (!this.deferredToolManager?.hasTools)
|
|
1457
|
+
return;
|
|
1458
|
+
this.deferredToolManager.hydrateLoadedToolsFromMessages(list.serialize().messages);
|
|
1459
|
+
}
|
|
1460
|
+
composeEffectiveInstructions(tools) {
|
|
1461
|
+
const fragments = tools
|
|
1462
|
+
.map((t) => t.systemInstruction)
|
|
1463
|
+
.filter((s) => typeof s === 'string' && s.trim().length > 0);
|
|
1464
|
+
const userInstructions = this.config.instructions;
|
|
1465
|
+
if (fragments.length === 0)
|
|
1466
|
+
return userInstructions;
|
|
1467
|
+
const block = `<built_in_rules>\n${fragments.map((f) => `- ${f}`).join('\n')}\n</built_in_rules>`;
|
|
1468
|
+
return userInstructions ? `${block}\n\n${userInstructions}` : block;
|
|
1105
1469
|
}
|
|
1106
|
-
async persistSuspension(pendingToolCalls, list, totalUsage) {
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
: undefined;
|
|
1470
|
+
async persistSuspension(pendingToolCalls, options, list, totalUsage, existingRunId) {
|
|
1471
|
+
const runId = existingRunId ?? (0, run_state_1.generateRunId)();
|
|
1472
|
+
const executionOptions = options?.maxIterations !== undefined ? { maxIterations: options.maxIterations } : undefined;
|
|
1110
1473
|
const state = {
|
|
1111
|
-
persistence:
|
|
1474
|
+
persistence: options?.persistence,
|
|
1112
1475
|
status: 'suspended',
|
|
1113
1476
|
messageList: list.serialize(),
|
|
1114
1477
|
pendingToolCalls,
|
|
1115
1478
|
usage: totalUsage,
|
|
1116
1479
|
executionOptions,
|
|
1117
1480
|
};
|
|
1118
|
-
await this.runState.suspend(
|
|
1481
|
+
await this.runState.suspend(runId, state);
|
|
1119
1482
|
this.updateState({ status: 'suspended', pendingToolCalls, messageList: list.serialize() });
|
|
1120
|
-
return
|
|
1483
|
+
return runId;
|
|
1121
1484
|
}
|
|
1122
|
-
async cleanupRun() {
|
|
1123
|
-
if (
|
|
1124
|
-
await this.runState.complete(
|
|
1485
|
+
async cleanupRun(runId) {
|
|
1486
|
+
if (runId) {
|
|
1487
|
+
await this.runState.complete(runId);
|
|
1125
1488
|
}
|
|
1126
1489
|
}
|
|
1127
1490
|
emitTurnEnd(newMessages, toolResults) {
|
|
1128
1491
|
const assistantMsg = newMessages.find((m) => 'role' in m && m.role === 'assistant');
|
|
1129
1492
|
if (assistantMsg) {
|
|
1130
|
-
this.
|
|
1493
|
+
this.eventBus.emit({ type: "turn_end", message: assistantMsg, toolResults });
|
|
1131
1494
|
}
|
|
1132
1495
|
}
|
|
1133
1496
|
updateState(patch) {
|
|
@@ -1163,49 +1526,22 @@ class AgentRuntime {
|
|
|
1163
1526
|
return usage;
|
|
1164
1527
|
return { ...usage, cost: (0, catalog_1.computeCost)(usage, this.modelCost) };
|
|
1165
1528
|
}
|
|
1166
|
-
async
|
|
1167
|
-
const
|
|
1168
|
-
if (!
|
|
1529
|
+
async setListObservationLogMemory(list, options) {
|
|
1530
|
+
const memory = this.config.memory;
|
|
1531
|
+
if (!memory || !options?.threadId || !(0, observation_log_store_1.hasObservationLogStore)(memory))
|
|
1169
1532
|
return;
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
this.eventBus.emit({ ...data, runId: this.runId });
|
|
1533
|
+
const scope = this.getObservationLogScope(options);
|
|
1534
|
+
const observations = await memory.getActiveObservationLog({
|
|
1535
|
+
...scope,
|
|
1536
|
+
order: 'asc',
|
|
1537
|
+
});
|
|
1538
|
+
list.observationLogMemory =
|
|
1539
|
+
(0, observation_log_renderer_1.renderObservationLog)(observations, {
|
|
1540
|
+
renderTokenBudget: this.config.observationLog?.renderTokenBudget,
|
|
1541
|
+
}) ?? undefined;
|
|
1180
1542
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
return null;
|
|
1184
|
-
if (!this.config.workingMemory)
|
|
1185
|
-
return null;
|
|
1186
|
-
const scope = this.config.workingMemory?.scope ?? 'resource';
|
|
1187
|
-
if (scope === 'resource' && !options.resourceId) {
|
|
1188
|
-
throw new Error('Working memory scope is "resource" but no resourceId was provided. ' +
|
|
1189
|
-
'Pass a resourceId in RunOptions or change the scope to "thread".');
|
|
1190
|
-
}
|
|
1191
|
-
if (!options)
|
|
1192
|
-
return null;
|
|
1193
|
-
const memoryParams = { ...options, scope };
|
|
1194
|
-
const persistFn = this.config.workingMemory && this.config.memory?.saveWorkingMemory && options
|
|
1195
|
-
? async (content) => {
|
|
1196
|
-
await this.config.memory.saveWorkingMemory(memoryParams, content);
|
|
1197
|
-
}
|
|
1198
|
-
: undefined;
|
|
1199
|
-
if (!persistFn)
|
|
1200
|
-
return null;
|
|
1201
|
-
return {
|
|
1202
|
-
persistFn,
|
|
1203
|
-
memoryParams,
|
|
1204
|
-
template: this.config.workingMemory.template,
|
|
1205
|
-
structured: this.config.workingMemory.structured,
|
|
1206
|
-
schema: this.config.workingMemory.schema,
|
|
1207
|
-
instruction: this.config.workingMemory.instruction,
|
|
1208
|
-
};
|
|
1543
|
+
getConfiguredTelemetry() {
|
|
1544
|
+
return this.config.telemetry;
|
|
1209
1545
|
}
|
|
1210
1546
|
}
|
|
1211
1547
|
exports.AgentRuntime = AgentRuntime;
|