veryfront 0.1.1289-rc.25708 → 0.1.1289-rc.25721
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/docs/README.md +1 -1
- package/docs/config.json +1 -1
- package/docs/guides/tools.md +27 -2
- package/docs/manifest.json +6 -6
- package/esm/deno.js +1 -1
- package/esm/src/agent/ag-ui/connected-local-platform-tools.d.ts.map +1 -1
- package/esm/src/agent/ag-ui/connected-local-platform-tools.js +9 -2
- package/esm/src/agent/ag-ui/tool-shared.d.ts +2 -0
- package/esm/src/agent/ag-ui/tool-shared.d.ts.map +1 -1
- package/esm/src/agent/ag-ui/tool-shared.js +7 -0
- package/esm/src/agent/hosted/chat-runtime-tool-assembly.d.ts +2 -0
- package/esm/src/agent/hosted/chat-runtime-tool-assembly.d.ts.map +1 -1
- package/esm/src/agent/hosted/chat-runtime-tool-assembly.js +18 -2
- package/esm/src/agent/hosted/child-fork-tool-sources.d.ts +1 -0
- package/esm/src/agent/hosted/child-fork-tool-sources.d.ts.map +1 -1
- package/esm/src/agent/hosted/child-fork-tool-sources.js +49 -12
- package/esm/src/agent/hosted/default-chat-runtime.d.ts.map +1 -1
- package/esm/src/agent/hosted/default-chat-runtime.js +26 -1
- package/esm/src/agent/hosted/executor-runtime-facades.d.ts.map +1 -1
- package/esm/src/agent/hosted/executor-runtime-facades.js +3 -0
- package/esm/src/agent/hosted/executor-tool-schema.d.ts.map +1 -1
- package/esm/src/agent/hosted/executor-tool-schema.js +7 -1
- package/esm/src/agent/hosted/platform-dependency-credential.d.ts.map +1 -1
- package/esm/src/agent/hosted/platform-dependency-credential.js +26 -1
- package/esm/src/agent/hosted/project-remote-tool-source.d.ts.map +1 -1
- package/esm/src/agent/hosted/project-remote-tool-source.js +18 -0
- package/esm/src/agent/hosted/terminal-credential.d.ts.map +1 -1
- package/esm/src/agent/hosted/terminal-credential.js +9 -3
- package/esm/src/agent/platform-mcp-tool-source.d.ts.map +1 -1
- package/esm/src/agent/platform-mcp-tool-source.js +4 -0
- package/esm/src/agent/project/live-studio-mcp-tools.d.ts.map +1 -1
- package/esm/src/agent/project/live-studio-mcp-tools.js +14 -2
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +2269 -2191
- package/esm/src/agent/runtime/mcp-server-tool-sources.d.ts +2 -0
- package/esm/src/agent/runtime/mcp-server-tool-sources.d.ts.map +1 -1
- package/esm/src/agent/runtime/mcp-server-tool-sources.js +27 -0
- package/esm/src/agent/runtime/runtime-tool-config.d.ts +1 -0
- package/esm/src/agent/runtime/runtime-tool-config.d.ts.map +1 -1
- package/esm/src/agent/runtime/runtime-tool-config.js +4 -0
- package/esm/src/agent/runtime/tool-exposure.d.ts +4 -2
- package/esm/src/agent/runtime/tool-exposure.d.ts.map +1 -1
- package/esm/src/agent/runtime/tool-exposure.js +17 -4
- package/esm/src/html/hydration-script-builder/hydration-runtime.generated.js +1 -1
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +95 -23
- package/esm/src/platform/compat/http/pinned-fetch.d.ts +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts +2 -0
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.js +24 -3
- package/esm/src/server/handlers/request/run-terminal-tool-source.d.ts.map +1 -1
- package/esm/src/server/handlers/request/run-terminal-tool-source.js +12 -3
- package/esm/src/tool/factory.d.ts +2 -0
- package/esm/src/tool/factory.d.ts.map +1 -1
- package/esm/src/tool/factory.js +7 -0
- package/esm/src/tool/host-tools.d.ts +2 -0
- package/esm/src/tool/host-tools.d.ts.map +1 -1
- package/esm/src/tool/host-tools.js +14 -0
- package/esm/src/tool/project-scoped-remote-tools.d.ts.map +1 -1
- package/esm/src/tool/project-scoped-remote-tools.js +12 -0
- package/esm/src/tool/registry.d.ts.map +1 -1
- package/esm/src/tool/registry.js +3 -0
- package/esm/src/tool/remote-mcp.d.ts +11 -0
- package/esm/src/tool/remote-mcp.d.ts.map +1 -1
- package/esm/src/tool/remote-mcp.js +685 -85
- package/esm/src/tool/remote-source-tools.d.ts.map +1 -1
- package/esm/src/tool/remote-source-tools.js +1 -0
- package/esm/src/tool/remote-tool-source-owner.d.ts +14 -0
- package/esm/src/tool/remote-tool-source-owner.d.ts.map +1 -0
- package/esm/src/tool/remote-tool-source-owner.js +65 -0
- package/esm/src/tool/types.d.ts +4 -0
- package/esm/src/tool/types.d.ts.map +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +9 -9
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import * as dntShim from "../../../_dnt.shims.js";
|
|
2
54
|
import { MAX_RUNTIME_PROVIDER_STREAM_RETRIES } from "./provider-stream-retry-policy.js";
|
|
3
55
|
import { introduceToolCallOccurrence, runWithToolCallOccurrenceDispatch, } from "../../runtime/tool-call-occurrence.js";
|
|
@@ -47,7 +99,7 @@ import { convertToTextGenerationRuntimeRequestMessages } from "./text-generation
|
|
|
47
99
|
import { attachProviderMetadata, isProviderReplayDelivered, markProviderReplayDelivered, readAttachedProviderMetadata, } from "./provider-metadata.js";
|
|
48
100
|
import { isRuntimeProviderSchemaHiddenTool } from "./local-tool.js";
|
|
49
101
|
import { convertToolsToRuntimeTools } from "./model-tool-converter.js";
|
|
50
|
-
import { bindRuntimeRemoteToolSourcesToCredentialOwner, constrainRuntimeRemoteToolSources, getRuntimeRemoteToolSources, getRuntimeUnavailableOptionalRemoteTools, } from "./mcp-server-tool-sources.js";
|
|
102
|
+
import { bindRuntimeRemoteToolSourcesToCredentialOwner, constrainRuntimeRemoteToolSources, createRuntimeMcpSourceCleanup, getRuntimeRemoteToolSources, getRuntimeUnavailableOptionalRemoteTools, } from "./mcp-server-tool-sources.js";
|
|
51
103
|
import { runWithRuntimeRemoteToolSources } from "./remote-tool-source-context.js";
|
|
52
104
|
import { hasRuntimeObservationCapability, } from "../../runtime/runtime-observation-carrier.js";
|
|
53
105
|
import { announceStreamedToolCallInput, createRuntimeStreamSource, createStreamState, processStream, resolveRelayableExecutionFailure, resolveRuntimeExecutionErrorEvent, withRuntimeProviderStreamErrorProvenance, } from "./chat-stream-handler.js";
|
|
@@ -1274,10 +1326,10 @@ function executeFrameworkToolSearch(input) {
|
|
|
1274
1326
|
...result,
|
|
1275
1327
|
contract,
|
|
1276
1328
|
nextStep: isAvailable
|
|
1277
|
-
? "This tool is already available. Call it with this input schema."
|
|
1329
|
+
? "This tool is already available. Call it with this input schema. The output schema is the declared result contract, or null when absent."
|
|
1278
1330
|
: result.loadedCount > 0
|
|
1279
|
-
? "This tool is loaded for the next step. Call it with this input schema."
|
|
1280
|
-
: "
|
|
1331
|
+
? "This tool is loaded for the next step. Call it with this input schema. The output schema is the declared result contract, or null when absent."
|
|
1332
|
+
: "These are the exact declared schemas, but the tool could not be loaded within the current tool limit.",
|
|
1281
1333
|
},
|
|
1282
1334
|
checkpoint: checkpoint(),
|
|
1283
1335
|
};
|
|
@@ -3244,869 +3296,882 @@ export class AgentRuntime {
|
|
|
3244
3296
|
async #executeAgentLoop(// NOSONAR: Existing loop shape; this patch only adds authority cleanup.
|
|
3245
3297
|
systemPrompt, messages, validateProviderRequest, persistMessage, prepareTerminalDispatch, toolContextBase, runtimeContext, runRuntimeContext, supportsToolCalling, providerReplayCheckpointEmission, modelString, resolvedModel, headers, providerOptions, reasoning, maxOutputTokensOverride, temperatureModelString, toolReplacements, abortSignal, outputSchema) {
|
|
3246
3298
|
return withSpan("agent.execution_loop", async (loopSpan) => {
|
|
3247
|
-
const {
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
const skillState = AgentLoopSkillState.hydrate(currentMessages, runtimeContext);
|
|
3260
|
-
const hasToolReplacements = toolReplacements !== undefined;
|
|
3261
|
-
const initialToolExposureCheckpoint = hasToolReplacements
|
|
3262
|
-
? undefined
|
|
3263
|
-
: getRuntimeToolExposureCheckpoint(this.config);
|
|
3264
|
-
const toolExposureState = createToolExposureState();
|
|
3265
|
-
const persistToolExposureCheckpoint = hasToolReplacements
|
|
3266
|
-
? undefined
|
|
3267
|
-
: getRuntimeToolExposureCheckpointPersister(this.config);
|
|
3268
|
-
const requireToolExposureCheckpointPersistence = hasToolReplacements
|
|
3269
|
-
? false
|
|
3270
|
-
: isRuntimeToolExposureCheckpointPersistenceRequired(this.config);
|
|
3271
|
-
const runtimeToolsConfig = hasToolReplacements ? toolReplacements : this.config.tools;
|
|
3272
|
-
const frameworkLocalTools = hasToolReplacements
|
|
3273
|
-
? undefined
|
|
3274
|
-
: createRuntimeFrameworkLocalTools(this.config);
|
|
3275
|
-
const toolLoadingResolution = resolveRuntimeToolLoading(this.config);
|
|
3276
|
-
const runConfig = {
|
|
3277
|
-
...this.config,
|
|
3278
|
-
__vfToolLoadingMode: hasToolReplacements ? "eager" : toolLoadingResolution.mode,
|
|
3279
|
-
};
|
|
3280
|
-
if (ObjectGetPrototypeOf(this.config) === null)
|
|
3281
|
-
ObjectSetPrototypeOf(runConfig, null);
|
|
3282
|
-
const runtimeStepConfig = hasToolReplacements
|
|
3283
|
-
? {
|
|
3284
|
-
...runConfig,
|
|
3285
|
-
tools: runtimeToolsConfig,
|
|
3286
|
-
skills: undefined,
|
|
3287
|
-
bootstrapTools: undefined,
|
|
3288
|
-
optionalBootstrapTools: undefined,
|
|
3289
|
-
providerTools: undefined,
|
|
3290
|
-
mcpServers: undefined,
|
|
3291
|
-
sandbox: undefined,
|
|
3299
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
3300
|
+
try {
|
|
3301
|
+
const { maxAgentSteps } = getPlatformCapabilities();
|
|
3302
|
+
const maxSteps = this.computeMaxSteps(maxAgentSteps);
|
|
3303
|
+
const effectiveModel = resolveRuntimeModel(modelString || this.config.model);
|
|
3304
|
+
const languageModel = resolvedModel ?? resolveModel(effectiveModel);
|
|
3305
|
+
const toolCalls = [];
|
|
3306
|
+
const currentMessages = mapPrivateArray(messages, (message) => message);
|
|
3307
|
+
applyProviderReplayCheckpointsToMessages(currentMessages, getRuntimeProviderReplayCheckpoints(this.config), { activeProvider: resolveActiveProviderReplayProvider(languageModel) });
|
|
3308
|
+
const totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
3309
|
+
if (!supportsToolCalling && this.config.tools) {
|
|
3310
|
+
warnUnsupportedToolCalling(this.id, effectiveModel);
|
|
3292
3311
|
}
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
hasToolReplacements
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
: undefined,
|
|
3355
|
-
forwardedRemoteToolDefinitions,
|
|
3356
|
-
getAvailableTools,
|
|
3357
|
-
unavailableOptionalRemoteToolNames: unavailableOptionalRemoteTools.names,
|
|
3358
|
-
unavailableOptionalRemoteToolPrefixes: unavailableOptionalRemoteTools.prefixes,
|
|
3312
|
+
// Request-scoped skill policy (not class-level mutable state)
|
|
3313
|
+
const skillState = AgentLoopSkillState.hydrate(currentMessages, runtimeContext);
|
|
3314
|
+
const hasToolReplacements = toolReplacements !== undefined;
|
|
3315
|
+
const initialToolExposureCheckpoint = hasToolReplacements
|
|
3316
|
+
? undefined
|
|
3317
|
+
: getRuntimeToolExposureCheckpoint(this.config);
|
|
3318
|
+
const toolExposureState = createToolExposureState();
|
|
3319
|
+
const persistToolExposureCheckpoint = hasToolReplacements
|
|
3320
|
+
? undefined
|
|
3321
|
+
: getRuntimeToolExposureCheckpointPersister(this.config);
|
|
3322
|
+
const requireToolExposureCheckpointPersistence = hasToolReplacements
|
|
3323
|
+
? false
|
|
3324
|
+
: isRuntimeToolExposureCheckpointPersistenceRequired(this.config);
|
|
3325
|
+
const runtimeToolsConfig = hasToolReplacements ? toolReplacements : this.config.tools;
|
|
3326
|
+
const frameworkLocalTools = hasToolReplacements
|
|
3327
|
+
? undefined
|
|
3328
|
+
: createRuntimeFrameworkLocalTools(this.config);
|
|
3329
|
+
const toolLoadingResolution = resolveRuntimeToolLoading(this.config);
|
|
3330
|
+
const runConfig = {
|
|
3331
|
+
...this.config,
|
|
3332
|
+
__vfToolLoadingMode: hasToolReplacements ? "eager" : toolLoadingResolution.mode,
|
|
3333
|
+
};
|
|
3334
|
+
if (ObjectGetPrototypeOf(this.config) === null)
|
|
3335
|
+
ObjectSetPrototypeOf(runConfig, null);
|
|
3336
|
+
const runtimeStepConfig = hasToolReplacements
|
|
3337
|
+
? {
|
|
3338
|
+
...runConfig,
|
|
3339
|
+
tools: runtimeToolsConfig,
|
|
3340
|
+
skills: undefined,
|
|
3341
|
+
bootstrapTools: undefined,
|
|
3342
|
+
optionalBootstrapTools: undefined,
|
|
3343
|
+
providerTools: undefined,
|
|
3344
|
+
mcpServers: undefined,
|
|
3345
|
+
sandbox: undefined,
|
|
3346
|
+
}
|
|
3347
|
+
: runConfig;
|
|
3348
|
+
if (ObjectGetPrototypeOf(this.config) === null)
|
|
3349
|
+
ObjectSetPrototypeOf(runtimeStepConfig, null);
|
|
3350
|
+
const runtimeStepToolLoading = resolveRuntimeToolLoading(runtimeStepConfig);
|
|
3351
|
+
const allowedRemoteToolNames = hasToolReplacements
|
|
3352
|
+
? undefined
|
|
3353
|
+
: getRuntimeAllowedRemoteTools(this.config);
|
|
3354
|
+
const forwardedRemoteToolDefinitions = hasToolReplacements
|
|
3355
|
+
? undefined
|
|
3356
|
+
: getRuntimeForwardedIntegrationToolDefs(this.config);
|
|
3357
|
+
const remoteToolSources = hasToolReplacements
|
|
3358
|
+
? undefined
|
|
3359
|
+
: getRuntimeRemoteToolSources(this.config, undefined, this.id);
|
|
3360
|
+
const _remoteMcpCleanup = __addDisposableResource(env_1, createRuntimeMcpSourceCleanup(remoteToolSources), true);
|
|
3361
|
+
const unavailableOptionalRemoteTools = hasToolReplacements
|
|
3362
|
+
? { names: [], prefixes: [] }
|
|
3363
|
+
: getRuntimeUnavailableOptionalRemoteTools(this.config, remoteToolSources);
|
|
3364
|
+
const sourceIntegrationPolicy = hasToolReplacements
|
|
3365
|
+
? undefined
|
|
3366
|
+
: getRuntimeSourceIntegrationPolicy(this.config);
|
|
3367
|
+
const configuredProviderTools = hasToolReplacements
|
|
3368
|
+
? []
|
|
3369
|
+
: getRuntimeProviderTools(this.config);
|
|
3370
|
+
const toolResultContext = createActiveToolResultContext({
|
|
3371
|
+
config: this.config,
|
|
3372
|
+
hasToolReplacements,
|
|
3359
3373
|
supportsToolCalling,
|
|
3360
|
-
includeStartupAuthorizedTools: !startupSkillLoaded,
|
|
3361
|
-
messages: currentMessages,
|
|
3362
|
-
mode: "generate",
|
|
3363
|
-
modelRuntime: languageModel,
|
|
3364
|
-
providerOptionKey: resolveModelProviderOptionKey(effectiveModel, languageModel),
|
|
3365
|
-
// The authorization catalog is independent of whether the model can
|
|
3366
|
-
// call tools; only the model-visible provider tools are withheld.
|
|
3367
|
-
authorizedProviderToolNames: providerTools,
|
|
3368
|
-
providerToolNames: supportsToolCalling && !agentWriteFinalResponseToolGuardEnabled
|
|
3369
|
-
? providerTools
|
|
3370
|
-
: [],
|
|
3371
|
-
remoteToolSources,
|
|
3372
|
-
sourceIntegrationPolicy,
|
|
3373
|
-
resolveRuntimeState: this.resolveRuntimeState.bind(this),
|
|
3374
|
-
runtimeContext: stepRuntimeContext,
|
|
3375
|
-
step,
|
|
3376
|
-
systemPrompt: currentSystemPrompt,
|
|
3377
|
-
toolContextBase: { ...toolContextBase, abortSignal },
|
|
3378
|
-
strictConfiguredToolsOnly: hasToolReplacements,
|
|
3379
|
-
frameworkLocalTools,
|
|
3380
|
-
toolExposureState,
|
|
3381
|
-
toolExposureCheckpoint: step === 0 ? initialToolExposureCheckpoint : undefined,
|
|
3382
3374
|
});
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
let
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3375
|
+
const providerTools = sourceIntegrationPolicy
|
|
3376
|
+
? applySourceIntegrationPolicy(configuredProviderTools, sourceIntegrationPolicy)
|
|
3377
|
+
: configuredProviderTools;
|
|
3378
|
+
let currentSystemPrompt = systemPrompt;
|
|
3379
|
+
let currentRuntimeContext = runtimeContext;
|
|
3380
|
+
let agentWriteFinalResponseToolGuardEnabled = false;
|
|
3381
|
+
let recoveredEmptyResponse = false;
|
|
3382
|
+
let maxStepsParsedOutput;
|
|
3383
|
+
// A submitted form continues a turn whose startup already ran, and the
|
|
3384
|
+
// post-form skill policy forbids loading or switching skill bodies.
|
|
3385
|
+
// Replacements suppress startup unless they retain the configured loader.
|
|
3386
|
+
let startupSkillLoaded = (hasToolReplacements && replacementsRetainingSkillLoader.get(toolReplacements) !== true) ||
|
|
3387
|
+
this.config.bootstrapSkill === undefined || skillState.hasSubmittedFormInput;
|
|
3388
|
+
for (let step = 0; step < maxSteps; step++) {
|
|
3389
|
+
throwIfAborted(abortSignal);
|
|
3390
|
+
this.status = "thinking";
|
|
3391
|
+
addSpanEvent(loopSpan, "step_start", { step });
|
|
3392
|
+
const stepRuntimeContext = skillState.hasSubmittedFormInput
|
|
3393
|
+
? markSubmittedFormInputRuntimeContext(currentRuntimeContext)
|
|
3394
|
+
: currentRuntimeContext;
|
|
3395
|
+
const preparedStep = await prepareAgentRuntimeStep({
|
|
3396
3396
|
agentId: this.id,
|
|
3397
|
-
|
|
3398
|
-
|
|
3397
|
+
activeSkillId: hasToolReplacements ? undefined : skillState.activeSkillId,
|
|
3398
|
+
activeSkillToolAvailability: hasToolReplacements
|
|
3399
|
+
? undefined
|
|
3400
|
+
: skillState.activeSkillToolAvailability,
|
|
3401
|
+
allowedRemoteToolNames,
|
|
3402
|
+
config: runtimeStepConfig,
|
|
3403
|
+
effectiveModel,
|
|
3404
|
+
...resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
3405
|
+
reservedToolSlots: getToolResultReaderReservedSlots(toolResultContext),
|
|
3406
|
+
excludedToolNames: agentWriteFinalResponseToolGuardEnabled &&
|
|
3407
|
+
runtimeStepToolLoading.mode === "eager"
|
|
3408
|
+
? AGENT_WRITE_FINAL_RESPONSE_EXCLUDED_TOOL_NAMES
|
|
3409
|
+
: undefined,
|
|
3410
|
+
forwardedRemoteToolDefinitions,
|
|
3411
|
+
getAvailableTools,
|
|
3412
|
+
unavailableOptionalRemoteToolNames: unavailableOptionalRemoteTools.names,
|
|
3413
|
+
unavailableOptionalRemoteToolPrefixes: unavailableOptionalRemoteTools.prefixes,
|
|
3414
|
+
supportsToolCalling,
|
|
3415
|
+
includeStartupAuthorizedTools: !startupSkillLoaded,
|
|
3399
3416
|
messages: currentMessages,
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
frameworkLocalTools,
|
|
3421
|
-
});
|
|
3422
|
-
await this.notifyToolResult({
|
|
3423
|
-
mode: "generate",
|
|
3424
|
-
toolName,
|
|
3425
|
-
toolCallId,
|
|
3426
|
-
input: args,
|
|
3427
|
-
result,
|
|
3428
|
-
context,
|
|
3429
|
-
});
|
|
3430
|
-
return result;
|
|
3431
|
-
},
|
|
3417
|
+
mode: "generate",
|
|
3418
|
+
modelRuntime: languageModel,
|
|
3419
|
+
providerOptionKey: resolveModelProviderOptionKey(effectiveModel, languageModel),
|
|
3420
|
+
// The authorization catalog is independent of whether the model can
|
|
3421
|
+
// call tools; only the model-visible provider tools are withheld.
|
|
3422
|
+
authorizedProviderToolNames: providerTools,
|
|
3423
|
+
providerToolNames: supportsToolCalling && !agentWriteFinalResponseToolGuardEnabled
|
|
3424
|
+
? providerTools
|
|
3425
|
+
: [],
|
|
3426
|
+
remoteToolSources,
|
|
3427
|
+
sourceIntegrationPolicy,
|
|
3428
|
+
resolveRuntimeState: this.resolveRuntimeState.bind(this),
|
|
3429
|
+
runtimeContext: stepRuntimeContext,
|
|
3430
|
+
step,
|
|
3431
|
+
systemPrompt: currentSystemPrompt,
|
|
3432
|
+
toolContextBase: { ...toolContextBase, abortSignal },
|
|
3433
|
+
strictConfiguredToolsOnly: hasToolReplacements,
|
|
3434
|
+
frameworkLocalTools,
|
|
3435
|
+
toolExposureState,
|
|
3436
|
+
toolExposureCheckpoint: step === 0 ? initialToolExposureCheckpoint : undefined,
|
|
3432
3437
|
});
|
|
3433
|
-
startupSkillLoaded = true;
|
|
3434
|
-
// Refresh the active skill policy without consuming a model step.
|
|
3435
|
-
step--;
|
|
3436
|
-
continue;
|
|
3437
|
-
}
|
|
3438
|
-
assertToolResultReaderNameAvailable({
|
|
3439
|
-
plan: effectiveToolExposurePlan,
|
|
3440
|
-
context: toolResultContext,
|
|
3441
|
-
});
|
|
3442
|
-
const baseSystemPrompt = currentSystemPrompt;
|
|
3443
|
-
const assembleRequest = (messages, plan = effectiveToolExposurePlan) => assembleRuntimeStepRequest({
|
|
3444
|
-
messages,
|
|
3445
|
-
plan,
|
|
3446
|
-
toolResultContext,
|
|
3447
|
-
supportsToolCalling,
|
|
3448
|
-
agentWriteFinalResponseToolGuardEnabled,
|
|
3449
|
-
providerTools,
|
|
3450
|
-
model: effectiveModel,
|
|
3451
|
-
languageModel,
|
|
3452
|
-
toolSchemaOptions: resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
3453
|
-
baseSystemPrompt,
|
|
3454
|
-
integrationToolDiscovery: preparedStep.integrationToolDiscovery,
|
|
3455
|
-
runRuntimeContext,
|
|
3456
|
-
bootstrapToolNames: preparedStep.configuredBootstrapToolNames,
|
|
3457
|
-
});
|
|
3458
|
-
const requestedOutputTokens = this.resolveMaxOutputTokens(effectiveModel, maxOutputTokensOverride);
|
|
3459
|
-
// An invalid thinking budget is left for the provider path to reject.
|
|
3460
|
-
const additionalOutputTokens = resolveAdditiveReasoningOutputTokens(languageModel, {
|
|
3461
|
-
reasoning,
|
|
3462
|
-
providerOptions,
|
|
3463
|
-
}) ?? 0;
|
|
3464
|
-
const fittedRequest = await fitRuntimeStepRequestToServedBudget({
|
|
3465
|
-
sourceMessages: currentMessages,
|
|
3466
|
-
modelMessages: toolResultContext
|
|
3467
|
-
? createModelToolResultContextMessages(currentMessages, toolResultContext)
|
|
3468
|
-
: currentMessages,
|
|
3469
|
-
plan: effectiveToolExposurePlan,
|
|
3470
|
-
assembleRequest,
|
|
3471
|
-
toolResultContext,
|
|
3472
|
-
supportsToolCalling,
|
|
3473
|
-
deferredToolLoading: runtimeStepToolLoading.mode === "deferred",
|
|
3474
|
-
languageModel,
|
|
3475
|
-
model: effectiveModel,
|
|
3476
|
-
reasoning,
|
|
3477
|
-
providerOptions,
|
|
3478
|
-
responseFormat: outputSchema?.responseFormat,
|
|
3479
|
-
requestedOutputTokens,
|
|
3480
|
-
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
3481
|
-
additionalOutputTokens,
|
|
3482
|
-
toolExposureState,
|
|
3483
|
-
persistToolExposureCheckpoint,
|
|
3484
|
-
requireToolExposureCheckpointPersistence,
|
|
3485
|
-
turnAdmission: validateProviderRequest,
|
|
3486
|
-
});
|
|
3487
|
-
const modelMessages = fittedRequest.modelMessages;
|
|
3488
|
-
effectiveToolExposurePlan = fittedRequest.plan;
|
|
3489
|
-
if (this.#persistRecoveryBoundary) {
|
|
3490
|
-
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
3491
3438
|
throwIfAborted(abortSignal);
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3439
|
+
currentSystemPrompt = preparedStep.systemPrompt;
|
|
3440
|
+
currentRuntimeContext = preparedStep.runtimeContext;
|
|
3441
|
+
const toolContext = preparedStep.toolContext;
|
|
3442
|
+
// Keep this snapshot fixed while calls from the same provider response execute.
|
|
3443
|
+
const providerObservedSkillBodies = getProviderObservedSkillBodies(currentMessages);
|
|
3444
|
+
let effectiveToolExposurePlan = agentWriteFinalResponseToolGuardEnabled
|
|
3445
|
+
? applyAgentWriteFinalResponseGuard(preparedStep.toolExposurePlan, {
|
|
3446
|
+
reloadable: runtimeStepToolLoading.mode === "deferred",
|
|
3447
|
+
})
|
|
3448
|
+
: preparedStep.toolExposurePlan;
|
|
3449
|
+
if (!startupSkillLoaded) {
|
|
3450
|
+
await executeStartupSkill({
|
|
3451
|
+
agentId: this.id,
|
|
3452
|
+
skillId: this.config.bootstrapSkill,
|
|
3453
|
+
authorized: preparedStep.startupAuthorizedTools,
|
|
3454
|
+
messages: currentMessages,
|
|
3455
|
+
persistMessage,
|
|
3456
|
+
skillState,
|
|
3457
|
+
toolCalls,
|
|
3458
|
+
resolveLoader: (name) => resolveConfiguredTool(runtimeToolsConfig, name, { agentId: this.id }),
|
|
3459
|
+
execute: async (toolName, toolCallId, args, admittedTurn) => {
|
|
3460
|
+
const context = applicationExecutionContext(toolContext);
|
|
3461
|
+
const result = await traceConfiguredToolExecution({
|
|
3462
|
+
mode: "generate",
|
|
3463
|
+
agentId: this.id,
|
|
3464
|
+
toolName,
|
|
3465
|
+
toolCallId,
|
|
3466
|
+
args,
|
|
3467
|
+
admittedTurn,
|
|
3468
|
+
owner: currentMessages,
|
|
3469
|
+
prepareTerminalDispatch,
|
|
3470
|
+
toolsConfig: runtimeToolsConfig,
|
|
3471
|
+
context,
|
|
3472
|
+
allowedRemoteToolNames,
|
|
3473
|
+
remoteToolSources,
|
|
3474
|
+
sourceIntegrationPolicy,
|
|
3475
|
+
frameworkLocalTools,
|
|
3476
|
+
});
|
|
3477
|
+
await this.notifyToolResult({
|
|
3478
|
+
mode: "generate",
|
|
3479
|
+
toolName,
|
|
3480
|
+
toolCallId,
|
|
3481
|
+
input: args,
|
|
3482
|
+
result,
|
|
3483
|
+
context,
|
|
3484
|
+
});
|
|
3485
|
+
return result;
|
|
3486
|
+
},
|
|
3487
|
+
});
|
|
3488
|
+
startupSkillLoaded = true;
|
|
3489
|
+
// Refresh the active skill policy without consuming a model step.
|
|
3490
|
+
step--;
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
assertToolResultReaderNameAvailable({
|
|
3494
|
+
plan: effectiveToolExposurePlan,
|
|
3495
|
+
context: toolResultContext,
|
|
3496
|
+
});
|
|
3497
|
+
const baseSystemPrompt = currentSystemPrompt;
|
|
3498
|
+
const assembleRequest = (messages, plan = effectiveToolExposurePlan) => assembleRuntimeStepRequest({
|
|
3499
|
+
messages,
|
|
3500
|
+
plan,
|
|
3501
|
+
toolResultContext,
|
|
3502
|
+
supportsToolCalling,
|
|
3503
|
+
agentWriteFinalResponseToolGuardEnabled,
|
|
3504
|
+
providerTools,
|
|
3505
|
+
model: effectiveModel,
|
|
3506
|
+
languageModel,
|
|
3507
|
+
toolSchemaOptions: resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
3508
|
+
baseSystemPrompt,
|
|
3509
|
+
integrationToolDiscovery: preparedStep.integrationToolDiscovery,
|
|
3510
|
+
runRuntimeContext,
|
|
3511
|
+
bootstrapToolNames: preparedStep.configuredBootstrapToolNames,
|
|
3509
3512
|
});
|
|
3510
|
-
|
|
3511
|
-
//
|
|
3512
|
-
const
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3513
|
+
const requestedOutputTokens = this.resolveMaxOutputTokens(effectiveModel, maxOutputTokensOverride);
|
|
3514
|
+
// An invalid thinking budget is left for the provider path to reject.
|
|
3515
|
+
const additionalOutputTokens = resolveAdditiveReasoningOutputTokens(languageModel, {
|
|
3516
|
+
reasoning,
|
|
3517
|
+
providerOptions,
|
|
3518
|
+
}) ?? 0;
|
|
3519
|
+
const fittedRequest = await fitRuntimeStepRequestToServedBudget({
|
|
3520
|
+
sourceMessages: currentMessages,
|
|
3521
|
+
modelMessages: toolResultContext
|
|
3522
|
+
? createModelToolResultContextMessages(currentMessages, toolResultContext)
|
|
3523
|
+
: currentMessages,
|
|
3524
|
+
plan: effectiveToolExposurePlan,
|
|
3525
|
+
assembleRequest,
|
|
3526
|
+
toolResultContext,
|
|
3527
|
+
supportsToolCalling,
|
|
3528
|
+
deferredToolLoading: runtimeStepToolLoading.mode === "deferred",
|
|
3529
|
+
languageModel,
|
|
3530
|
+
model: effectiveModel,
|
|
3531
|
+
reasoning,
|
|
3532
|
+
providerOptions,
|
|
3519
3533
|
responseFormat: outputSchema?.responseFormat,
|
|
3520
|
-
limit: fittedRequest.servedLimit,
|
|
3521
3534
|
requestedOutputTokens,
|
|
3522
3535
|
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
3523
3536
|
additionalOutputTokens,
|
|
3537
|
+
toolExposureState,
|
|
3538
|
+
persistToolExposureCheckpoint,
|
|
3539
|
+
requireToolExposureCheckpointPersistence,
|
|
3540
|
+
turnAdmission: validateProviderRequest,
|
|
3524
3541
|
});
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
abortSignal,
|
|
3542
|
-
});
|
|
3543
|
-
setSpanAttributes(span, buildRuntimeUsageTraceAttributes(result.usage));
|
|
3544
|
-
return result;
|
|
3545
|
-
});
|
|
3546
|
-
throwIfAborted(abortSignal);
|
|
3547
|
-
// Accumulate usage
|
|
3548
|
-
if (response.usage) {
|
|
3549
|
-
const input = response.usage.inputTokens ?? 0;
|
|
3550
|
-
const output = response.usage.outputTokens ?? 0;
|
|
3551
|
-
accumulateUsage(totalUsage, {
|
|
3552
|
-
promptTokens: input,
|
|
3553
|
-
completionTokens: output,
|
|
3554
|
-
totalTokens: response.usage.totalTokens ?? input + output,
|
|
3555
|
-
cachedInputTokens: response.usage.cachedInputTokens ??
|
|
3556
|
-
response.usage.cacheReadInputTokens,
|
|
3557
|
-
cacheCreationInputTokens: response.usage.cacheCreationInputTokens,
|
|
3558
|
-
cacheCreation1hInputTokens: response.usage.cacheCreation1hInputTokens,
|
|
3559
|
-
cacheReadInputTokens: response.usage.cacheReadInputTokens,
|
|
3560
|
-
reasoningTokens: response.usage.reasoningTokens,
|
|
3561
|
-
billableInputTokens: response.usage.billableInputTokens,
|
|
3562
|
-
billableOutputTokens: response.usage.billableOutputTokens,
|
|
3563
|
-
costUsd: response.usage.costUsd,
|
|
3564
|
-
providerInputCostUsd: response.usage.providerInputCostUsd,
|
|
3565
|
-
providerOutputCostUsd: response.usage.providerOutputCostUsd,
|
|
3566
|
-
providerCostUsd: response.usage.providerCostUsd,
|
|
3567
|
-
veryfrontInputChargeUsd: response.usage.veryfrontInputChargeUsd,
|
|
3568
|
-
veryfrontOutputChargeUsd: response.usage.veryfrontOutputChargeUsd,
|
|
3569
|
-
veryfrontChargeUsd: response.usage.veryfrontChargeUsd,
|
|
3570
|
-
veryfrontBilledUsd: response.usage.veryfrontBilledUsd,
|
|
3571
|
-
costCredits: response.usage.costCredits,
|
|
3572
|
-
costSource: response.usage.costSource,
|
|
3573
|
-
billingMode: response.usage.billingMode,
|
|
3574
|
-
usageCaptureStatus: response.usage.usageCaptureStatus,
|
|
3542
|
+
const modelMessages = fittedRequest.modelMessages;
|
|
3543
|
+
effectiveToolExposurePlan = fittedRequest.plan;
|
|
3544
|
+
if (this.#persistRecoveryBoundary) {
|
|
3545
|
+
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
3546
|
+
throwIfAborted(abortSignal);
|
|
3547
|
+
}
|
|
3548
|
+
const request = assembleRequest(modelMessages);
|
|
3549
|
+
currentSystemPrompt = request.systemPrompt;
|
|
3550
|
+
const { tools, runtimeTools, providerSystemPrompt, providerMessages } = request;
|
|
3551
|
+
setSpanAttributes(loopSpan, {
|
|
3552
|
+
"tool.loading.mode": runtimeStepToolLoading.mode,
|
|
3553
|
+
"tool.loading.provenance": toolLoadingResolution.provenance,
|
|
3554
|
+
"tool.catalog.authorized_count": preparedStep.toolExposurePlan.authorized.length,
|
|
3555
|
+
"tool.catalog.visible_count": tools.length,
|
|
3556
|
+
"tool.catalog.deferred_count": effectiveToolExposurePlan.deferred.length,
|
|
3557
|
+
"tool.loading.path": "framework-fallback",
|
|
3575
3558
|
});
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3559
|
+
const temperature = this.resolveTemperature(temperatureModelString ?? effectiveModel, providerOptions);
|
|
3560
|
+
const response = await withSpan("agent.generate_text", async (span) => {
|
|
3561
|
+
setSpanAttributes(span, {
|
|
3562
|
+
"model.id": effectiveModel,
|
|
3563
|
+
"messages.count": currentMessages.length,
|
|
3564
|
+
});
|
|
3565
|
+
// Reject an over-budget request before the turn is accepted, so a
|
|
3566
|
+
// rejected input is rolled back instead of committed to memory.
|
|
3567
|
+
const { maxOutputTokens } = enforceProviderRequestBudget({
|
|
3568
|
+
model: languageModel,
|
|
3569
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
3570
|
+
...(providerOptions === undefined ? {} : { providerOptions }),
|
|
3571
|
+
system: providerSystemPrompt,
|
|
3572
|
+
messages: providerMessages,
|
|
3573
|
+
tools: runtimeTools,
|
|
3574
|
+
responseFormat: outputSchema?.responseFormat,
|
|
3575
|
+
limit: fittedRequest.servedLimit,
|
|
3576
|
+
requestedOutputTokens,
|
|
3577
|
+
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
3578
|
+
additionalOutputTokens,
|
|
3579
|
+
});
|
|
3580
|
+
await validateProviderRequest(providerSystemPrompt, modelMessages, { beforeAcceptance: fittedRequest.commitToolExposure });
|
|
3581
|
+
const result = await generateText({
|
|
3582
|
+
model: languageModel,
|
|
3583
|
+
system: providerSystemPrompt,
|
|
3584
|
+
messages: providerMessages,
|
|
3585
|
+
tools: runtimeTools,
|
|
3586
|
+
experimental_repairToolCall: repairToolCall,
|
|
3587
|
+
maxOutputTokens,
|
|
3588
|
+
...(temperature === undefined ? {} : { temperature }),
|
|
3589
|
+
...(this.config.parallelToolCalls === undefined
|
|
3590
|
+
? {}
|
|
3591
|
+
: { parallelToolCalls: this.config.parallelToolCalls }),
|
|
3592
|
+
...(headers ? { headers } : {}),
|
|
3593
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
3594
|
+
...(reasoning ? { reasoning } : {}),
|
|
3595
|
+
...(outputSchema ? { responseFormat: outputSchema.responseFormat } : {}),
|
|
3596
|
+
abortSignal,
|
|
3597
|
+
});
|
|
3598
|
+
setSpanAttributes(span, buildRuntimeUsageTraceAttributes(result.usage));
|
|
3599
|
+
return result;
|
|
3605
3600
|
});
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3601
|
+
throwIfAborted(abortSignal);
|
|
3602
|
+
// Accumulate usage
|
|
3603
|
+
if (response.usage) {
|
|
3604
|
+
const input = response.usage.inputTokens ?? 0;
|
|
3605
|
+
const output = response.usage.outputTokens ?? 0;
|
|
3606
|
+
accumulateUsage(totalUsage, {
|
|
3607
|
+
promptTokens: input,
|
|
3608
|
+
completionTokens: output,
|
|
3609
|
+
totalTokens: response.usage.totalTokens ?? input + output,
|
|
3610
|
+
cachedInputTokens: response.usage.cachedInputTokens ??
|
|
3611
|
+
response.usage.cacheReadInputTokens,
|
|
3612
|
+
cacheCreationInputTokens: response.usage.cacheCreationInputTokens,
|
|
3613
|
+
cacheCreation1hInputTokens: response.usage.cacheCreation1hInputTokens,
|
|
3614
|
+
cacheReadInputTokens: response.usage.cacheReadInputTokens,
|
|
3615
|
+
reasoningTokens: response.usage.reasoningTokens,
|
|
3616
|
+
billableInputTokens: response.usage.billableInputTokens,
|
|
3617
|
+
billableOutputTokens: response.usage.billableOutputTokens,
|
|
3618
|
+
costUsd: response.usage.costUsd,
|
|
3619
|
+
providerInputCostUsd: response.usage.providerInputCostUsd,
|
|
3620
|
+
providerOutputCostUsd: response.usage.providerOutputCostUsd,
|
|
3621
|
+
providerCostUsd: response.usage.providerCostUsd,
|
|
3622
|
+
veryfrontInputChargeUsd: response.usage.veryfrontInputChargeUsd,
|
|
3623
|
+
veryfrontOutputChargeUsd: response.usage.veryfrontOutputChargeUsd,
|
|
3624
|
+
veryfrontChargeUsd: response.usage.veryfrontChargeUsd,
|
|
3625
|
+
veryfrontBilledUsd: response.usage.veryfrontBilledUsd,
|
|
3626
|
+
costCredits: response.usage.costCredits,
|
|
3627
|
+
costSource: response.usage.costSource,
|
|
3628
|
+
billingMode: response.usage.billingMode,
|
|
3629
|
+
usageCaptureStatus: response.usage.usageCaptureStatus,
|
|
3621
3630
|
});
|
|
3631
|
+
setSpanAttributes(loopSpan, buildRuntimeUsageTraceAttributes(totalUsage));
|
|
3622
3632
|
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
pushPrivateArray(currentMessages, assistantMessage);
|
|
3640
|
-
try {
|
|
3641
|
-
await persistMessage(assistantMessage);
|
|
3642
|
-
}
|
|
3643
|
-
catch (error) {
|
|
3644
|
-
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3645
|
-
throw error;
|
|
3646
|
-
}
|
|
3647
|
-
const persistGeneratedToolResult = async (generatedToolResult) => {
|
|
3648
|
-
const executionAuthority = resolveToolExecutionAuthority({
|
|
3649
|
-
toolName: generatedToolResult.toolName,
|
|
3650
|
-
plan: effectiveToolExposurePlan,
|
|
3633
|
+
const generatedToolResults = collectGeneratedToolResults(response.toolResults);
|
|
3634
|
+
const generatedInvokeAgentBatch = providerReplayCheckpointEmission.complete
|
|
3635
|
+
? collectGeneratedParallelInvokeAgentToolCalls(response.toolCalls, generatedToolResults, providerReplayCheckpointEmission.invokeAgentToolNames, effectiveToolExposurePlan, {
|
|
3636
|
+
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
3637
|
+
toolsConfig: runtimeToolsConfig,
|
|
3638
|
+
agentId: this.id,
|
|
3639
|
+
hasToolReplacements,
|
|
3640
|
+
})
|
|
3641
|
+
: undefined;
|
|
3642
|
+
const generatedSkillDelegationOrder = generatedInvokeAgentBatch && !hasToolReplacements
|
|
3643
|
+
? generatedSameTurnSkillDelegationOrder(response.toolCalls, generatedToolResults, providerReplayCheckpointEmission.invokeAgentToolNames)
|
|
3644
|
+
: undefined;
|
|
3645
|
+
let generatedBatchCompletionDeferred = generatedSkillDelegationOrder === "prefix";
|
|
3646
|
+
const assistantMessage = buildGeneratedAssistantMessage(response, {
|
|
3647
|
+
id: `msg_${Date.now()}_${step}`,
|
|
3648
|
+
timestamp: Date.now(),
|
|
3651
3649
|
});
|
|
3652
|
-
const
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
const toolCall = {
|
|
3665
|
-
id: generatedToolResult.toolCallId,
|
|
3666
|
-
name: generatedToolResult.toolName,
|
|
3667
|
-
args: {},
|
|
3668
|
-
status: "error",
|
|
3669
|
-
error,
|
|
3650
|
+
const admittedTurn = snapshotAdmittedToolTurn(assistantMessage, currentMessages.length);
|
|
3651
|
+
let generatedTurnAdmissionObserved = false;
|
|
3652
|
+
let generatedTurnFinishObserved = false;
|
|
3653
|
+
const observeGeneratedProviderTurnAdmissionOnce = async () => {
|
|
3654
|
+
if (generatedTurnAdmissionObserved)
|
|
3655
|
+
return;
|
|
3656
|
+
await observeGeneratedAgentTurn(assistantMessage.id, {
|
|
3657
|
+
...response,
|
|
3658
|
+
finishReason: undefined,
|
|
3659
|
+
usage: undefined,
|
|
3660
|
+
});
|
|
3661
|
+
generatedTurnAdmissionObserved = true;
|
|
3670
3662
|
};
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
if (!response.toolCalls?.length) {
|
|
3678
|
-
try {
|
|
3679
|
-
for (const generatedToolResult of generatedToolResults.values()) {
|
|
3680
|
-
if (await rejectUnpairedRequestScopedGeneratedToolResult(generatedToolResult)) {
|
|
3681
|
-
continue;
|
|
3682
|
-
}
|
|
3683
|
-
await persistGeneratedToolResult(generatedToolResult);
|
|
3663
|
+
const observeGeneratedProviderTurnFinishOnce = async (turn) => {
|
|
3664
|
+
if (generatedTurnFinishObserved)
|
|
3665
|
+
return;
|
|
3666
|
+
if (!generatedTurnAdmissionObserved) {
|
|
3667
|
+
await observeGeneratedAgentTurn(assistantMessage.id, withOutputSchemaObservationObject(turn));
|
|
3668
|
+
generatedTurnAdmissionObserved = true;
|
|
3684
3669
|
}
|
|
3670
|
+
else {
|
|
3671
|
+
const structured = readStructuredAgentOutput(turn);
|
|
3672
|
+
await observeGeneratedAgentTurnFinish({
|
|
3673
|
+
finishReason: turn.finishReason,
|
|
3674
|
+
usage: turn.usage,
|
|
3675
|
+
...(structured ? createOutputSchemaObservationFields(structured.data) : {}),
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
generatedTurnFinishObserved = true;
|
|
3679
|
+
};
|
|
3680
|
+
let generatedProviderReplayCheckpointPersisted = false;
|
|
3681
|
+
const persistGeneratedProviderReplayCheckpoint = async () => {
|
|
3682
|
+
if (generatedProviderReplayCheckpointPersisted)
|
|
3683
|
+
return;
|
|
3684
|
+
await persistProviderReplayCheckpointAfterTurn({
|
|
3685
|
+
emission: providerReplayCheckpointEmission,
|
|
3686
|
+
providerMetadata: readAttachedProviderMetadata(assistantMessage),
|
|
3687
|
+
invokeAgentToolCalls: generatedSkillDelegationOrder === "interleaved"
|
|
3688
|
+
? undefined
|
|
3689
|
+
: generatedInvokeAgentBatch,
|
|
3690
|
+
deferCompletion: generatedBatchCompletionDeferred,
|
|
3691
|
+
});
|
|
3692
|
+
generatedProviderReplayCheckpointPersisted = true;
|
|
3693
|
+
};
|
|
3694
|
+
pushPrivateArray(currentMessages, assistantMessage);
|
|
3695
|
+
try {
|
|
3696
|
+
await persistMessage(assistantMessage);
|
|
3685
3697
|
}
|
|
3686
3698
|
catch (error) {
|
|
3687
3699
|
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3688
|
-
await persistGeneratedProviderReplayCheckpoint();
|
|
3689
3700
|
throw error;
|
|
3690
3701
|
}
|
|
3691
|
-
const
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3702
|
+
const persistGeneratedToolResult = async (generatedToolResult) => {
|
|
3703
|
+
const executionAuthority = resolveToolExecutionAuthority({
|
|
3704
|
+
toolName: generatedToolResult.toolName,
|
|
3705
|
+
plan: effectiveToolExposurePlan,
|
|
3706
|
+
});
|
|
3707
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(generatedToolResult.toolCallId, generatedToolResult.toolName, generatedToolResult.isError === true
|
|
3708
|
+
? { error: stringifyToolError(generatedToolResult.result) }
|
|
3709
|
+
: generatedToolResult.result, executionAuthority?.toolDefinition, generatedToolResult.providerExecuted === true);
|
|
3710
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
3711
|
+
await persistMessage(toolResultMessage);
|
|
3698
3712
|
throwIfAborted(abortSignal);
|
|
3699
|
-
|
|
3700
|
-
|
|
3713
|
+
};
|
|
3714
|
+
const rejectUnpairedRequestScopedGeneratedToolResult = async (generatedToolResult) => {
|
|
3715
|
+
if (!hasToolReplacements) {
|
|
3716
|
+
return false;
|
|
3701
3717
|
}
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
id:
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
let parsedObject;
|
|
3718
|
-
if (outputSchema) {
|
|
3718
|
+
const error = `Tool "${generatedToolResult.toolName}" is not available in request-scoped replacement tools`;
|
|
3719
|
+
const toolCall = {
|
|
3720
|
+
id: generatedToolResult.toolCallId,
|
|
3721
|
+
name: generatedToolResult.toolName,
|
|
3722
|
+
args: {},
|
|
3723
|
+
status: "error",
|
|
3724
|
+
error,
|
|
3725
|
+
};
|
|
3726
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3727
|
+
const errorMessage = createToolErrorMessage(generatedToolResult.toolCallId, generatedToolResult.toolName, error);
|
|
3728
|
+
pushPrivateArray(currentMessages, errorMessage);
|
|
3729
|
+
await persistMessage(errorMessage);
|
|
3730
|
+
return true;
|
|
3731
|
+
};
|
|
3732
|
+
if (!response.toolCalls?.length) {
|
|
3719
3733
|
try {
|
|
3720
|
-
|
|
3734
|
+
for (const generatedToolResult of generatedToolResults.values()) {
|
|
3735
|
+
if (await rejectUnpairedRequestScopedGeneratedToolResult(generatedToolResult)) {
|
|
3736
|
+
continue;
|
|
3737
|
+
}
|
|
3738
|
+
await persistGeneratedToolResult(generatedToolResult);
|
|
3739
|
+
}
|
|
3721
3740
|
}
|
|
3722
3741
|
catch (error) {
|
|
3723
|
-
await
|
|
3742
|
+
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3724
3743
|
await persistGeneratedProviderReplayCheckpoint();
|
|
3725
3744
|
throw error;
|
|
3726
3745
|
}
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
}
|
|
3749
|
-
else {
|
|
3750
|
-
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3751
|
-
}
|
|
3752
|
-
await persistGeneratedProviderReplayCheckpoint();
|
|
3753
|
-
throwIfAborted(abortSignal);
|
|
3754
|
-
this.status = "tool_execution";
|
|
3755
|
-
addSpanEvent(loopSpan, "tool_execution_start", { count: response.toolCalls.length });
|
|
3756
|
-
for (let toolCallIndex = 0; toolCallIndex < response.toolCalls.length; toolCallIndex++) {
|
|
3757
|
-
if (!ObjectHasOwn(response.toolCalls, toolCallIndex))
|
|
3746
|
+
const stoppedEmptyAfterCompletedTool = response.finishReason === "stop" &&
|
|
3747
|
+
!hasSubstantiveAssistantText(response.text) &&
|
|
3748
|
+
generatedToolResults.size === 0 &&
|
|
3749
|
+
somePrivateArray(toolCalls, (toolCall) => toolCall.status === "completed");
|
|
3750
|
+
if (stoppedEmptyAfterCompletedTool) {
|
|
3751
|
+
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3752
|
+
await persistGeneratedProviderReplayCheckpoint();
|
|
3753
|
+
throwIfAborted(abortSignal);
|
|
3754
|
+
if (recoveredEmptyResponse || step + 1 >= maxSteps) {
|
|
3755
|
+
throw new RuntimeEmptyResponseError();
|
|
3756
|
+
}
|
|
3757
|
+
recoveredEmptyResponse = true;
|
|
3758
|
+
pushPrivateArray(currentMessages, markRuntimeGeneratedUserMessage({
|
|
3759
|
+
id: `runtime_empty_response_${Date.now()}_${step}`,
|
|
3760
|
+
role: "user",
|
|
3761
|
+
parts: [{
|
|
3762
|
+
type: "text",
|
|
3763
|
+
text: EMPTY_RESPONSE_RECOVERY_PROMPT,
|
|
3764
|
+
}],
|
|
3765
|
+
timestamp: Date.now(),
|
|
3766
|
+
}));
|
|
3758
3767
|
continue;
|
|
3759
|
-
|
|
3768
|
+
}
|
|
3769
|
+
this.status = "completed";
|
|
3770
|
+
addSpanEvent(loopSpan, "loop_complete");
|
|
3771
|
+
setSpanAttributes(loopSpan, buildRuntimeUsageTraceAttributes(totalUsage));
|
|
3772
|
+
let parsedObject;
|
|
3773
|
+
if (outputSchema) {
|
|
3774
|
+
try {
|
|
3775
|
+
parsedObject = await outputSchema.parseOutput(response.text);
|
|
3776
|
+
}
|
|
3777
|
+
catch (error) {
|
|
3778
|
+
await observeGeneratedAgentOutputSchemaRejection(assistantMessage.id, response);
|
|
3779
|
+
await persistGeneratedProviderReplayCheckpoint();
|
|
3780
|
+
throw error;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
await observeGeneratedProviderTurnFinishOnce({
|
|
3784
|
+
...response,
|
|
3785
|
+
...(outputSchema ? { data: parsedObject, object: parsedObject } : {}),
|
|
3786
|
+
});
|
|
3787
|
+
await persistGeneratedProviderReplayCheckpoint();
|
|
3760
3788
|
throwIfAborted(abortSignal);
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
}));
|
|
3776
|
-
generatedBatchCompletionDeferred = false;
|
|
3789
|
+
return attachOutputSchemaParser({
|
|
3790
|
+
text: response.text,
|
|
3791
|
+
...(outputSchema ? { data: parsedObject, object: parsedObject } : {}),
|
|
3792
|
+
messages: currentMessages,
|
|
3793
|
+
toolCalls,
|
|
3794
|
+
status: this.status,
|
|
3795
|
+
usage: totalUsage,
|
|
3796
|
+
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, response.finishReason ? { finishReason: response.finishReason } : undefined),
|
|
3797
|
+
}, outputSchema);
|
|
3798
|
+
}
|
|
3799
|
+
const isFinalMaxStepsToolTurn = step + 1 >= maxSteps;
|
|
3800
|
+
try {
|
|
3801
|
+
if (isFinalMaxStepsToolTurn) {
|
|
3802
|
+
await observeGeneratedProviderTurnAdmissionOnce();
|
|
3777
3803
|
}
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3804
|
+
else {
|
|
3805
|
+
await observeGeneratedProviderTurnFinishOnce(response);
|
|
3806
|
+
}
|
|
3807
|
+
await persistGeneratedProviderReplayCheckpoint();
|
|
3808
|
+
throwIfAborted(abortSignal);
|
|
3809
|
+
this.status = "tool_execution";
|
|
3810
|
+
addSpanEvent(loopSpan, "tool_execution_start", { count: response.toolCalls.length });
|
|
3811
|
+
for (let toolCallIndex = 0; toolCallIndex < response.toolCalls.length; toolCallIndex++) {
|
|
3812
|
+
if (!ObjectHasOwn(response.toolCalls, toolCallIndex))
|
|
3813
|
+
continue;
|
|
3814
|
+
const tc = response.toolCalls[toolCallIndex];
|
|
3815
|
+
throwIfAborted(abortSignal);
|
|
3816
|
+
const toolCall = {
|
|
3817
|
+
id: tc.toolCallId,
|
|
3818
|
+
name: tc.toolName,
|
|
3819
|
+
args: tc.input,
|
|
3820
|
+
status: "pending",
|
|
3821
|
+
};
|
|
3822
|
+
const generatedToolResult = generatedToolResults.get(tc.toolCallId);
|
|
3823
|
+
if (generatedBatchCompletionDeferred && generatedToolResult === undefined &&
|
|
3824
|
+
IntrinsicReflectApply(IntrinsicSetHas, providerReplayCheckpointEmission.invokeAgentToolNames, [tc.toolName])) {
|
|
3825
|
+
await completeDeferredProviderReplayCheckpointTurn(providerReplayCheckpointEmission, collectGeneratedParallelInvokeAgentToolCalls(response.toolCalls, generatedToolResults, providerReplayCheckpointEmission.invokeAgentToolNames, effectiveToolExposurePlan, {
|
|
3826
|
+
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
3827
|
+
toolsConfig: runtimeToolsConfig,
|
|
3828
|
+
agentId: this.id,
|
|
3829
|
+
hasToolReplacements,
|
|
3830
|
+
}));
|
|
3831
|
+
generatedBatchCompletionDeferred = false;
|
|
3832
|
+
}
|
|
3833
|
+
await withSpan("agent.tool_execute", async (toolSpan) => {
|
|
3834
|
+
const inputSizeBytes = estimateSerializedSizeBytes(tc.input);
|
|
3835
|
+
setSpanAttributes(toolSpan, compactRuntimeTraceAttributes({
|
|
3836
|
+
"tool.name": tc.toolName,
|
|
3837
|
+
"tool.call.id": tc.toolCallId,
|
|
3838
|
+
"tool.id": tc.toolCallId,
|
|
3839
|
+
"tool.status": "executing",
|
|
3840
|
+
"tool.input.size_bytes": inputSizeBytes,
|
|
3841
|
+
"gen_ai.operation.name": "execute_tool",
|
|
3842
|
+
"gen_ai.tool.name": tc.toolName,
|
|
3843
|
+
"gen_ai.tool.type": "function",
|
|
3844
|
+
"gen_ai.tool.call.id": tc.toolCallId,
|
|
3845
|
+
}));
|
|
3846
|
+
const executionAuthority = resolveToolExecutionAuthority({
|
|
3797
3847
|
toolName: tc.toolName,
|
|
3798
3848
|
plan: effectiveToolExposurePlan,
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3849
|
+
});
|
|
3850
|
+
if (generatedToolResult === undefined &&
|
|
3851
|
+
shouldHandleToolResultRead({
|
|
3852
|
+
toolName: tc.toolName,
|
|
3853
|
+
plan: effectiveToolExposurePlan,
|
|
3854
|
+
context: toolResultContext,
|
|
3855
|
+
})) {
|
|
3856
|
+
try {
|
|
3857
|
+
if (toolResultContext === undefined) {
|
|
3858
|
+
throw new ReferenceError("Tool result context is not available");
|
|
3859
|
+
}
|
|
3860
|
+
const result = readToolResultContext(toolResultContext, toolCall.args);
|
|
3861
|
+
toolCall.status = "completed";
|
|
3862
|
+
toolCall.result = result;
|
|
3863
|
+
setSpanAttributes(toolSpan, {
|
|
3864
|
+
"tool.status": "completed",
|
|
3865
|
+
"tool.output.size_bytes": estimateSerializedSizeBytes(result),
|
|
3866
|
+
});
|
|
3867
|
+
const toolResultMessage = createToolResultMessage(tc.toolCallId, tc.toolName, result);
|
|
3868
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
3869
|
+
await persistMessage(toolResultMessage);
|
|
3804
3870
|
}
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
await persistMessage(toolResultMessage);
|
|
3871
|
+
catch (error) {
|
|
3872
|
+
toolCall.status = "error";
|
|
3873
|
+
toolCall.error = error instanceof Error ? error.message : String(error);
|
|
3874
|
+
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3875
|
+
pushPrivateArray(currentMessages, errorMessage);
|
|
3876
|
+
await persistMessage(errorMessage);
|
|
3877
|
+
}
|
|
3878
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3879
|
+
return;
|
|
3815
3880
|
}
|
|
3816
|
-
|
|
3881
|
+
if (generatedToolResult === undefined &&
|
|
3882
|
+
shouldBlockToolResultReadName({
|
|
3883
|
+
toolName: tc.toolName,
|
|
3884
|
+
context: toolResultContext,
|
|
3885
|
+
})) {
|
|
3817
3886
|
toolCall.status = "error";
|
|
3818
|
-
toolCall.error =
|
|
3887
|
+
toolCall.error = toolResultReaderUnavailableError();
|
|
3819
3888
|
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3820
3889
|
pushPrivateArray(currentMessages, errorMessage);
|
|
3821
3890
|
await persistMessage(errorMessage);
|
|
3891
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3892
|
+
return;
|
|
3822
3893
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
toolCall.error = toolResultReaderUnavailableError();
|
|
3833
|
-
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3834
|
-
pushPrivateArray(currentMessages, errorMessage);
|
|
3835
|
-
await persistMessage(errorMessage);
|
|
3836
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
3837
|
-
return;
|
|
3838
|
-
}
|
|
3839
|
-
if (!hasToolReplacements &&
|
|
3840
|
-
generatedToolResult === undefined &&
|
|
3841
|
-
executionAuthority === undefined) {
|
|
3842
|
-
toolCall.status = "error";
|
|
3843
|
-
toolCall.error = toolNotVisibleError(tc.toolName);
|
|
3844
|
-
setSpanAttributes(toolSpan, {
|
|
3845
|
-
"tool.status": "blocked",
|
|
3846
|
-
error: true,
|
|
3847
|
-
"error.type": "ToolExposureBlocked",
|
|
3848
|
-
});
|
|
3849
|
-
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3850
|
-
pushPrivateArray(currentMessages, errorMessage);
|
|
3851
|
-
await persistMessage(errorMessage);
|
|
3852
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
3853
|
-
return;
|
|
3854
|
-
}
|
|
3855
|
-
if (generatedToolResult === undefined &&
|
|
3856
|
-
isFrameworkToolSearch(tc.toolName, effectiveToolExposurePlan)) {
|
|
3857
|
-
let checkpoint;
|
|
3858
|
-
try {
|
|
3859
|
-
const search = executeFrameworkToolSearch({
|
|
3860
|
-
args: toolCall.args,
|
|
3861
|
-
plan: effectiveToolExposurePlan,
|
|
3862
|
-
state: toolExposureState,
|
|
3894
|
+
if (!hasToolReplacements &&
|
|
3895
|
+
generatedToolResult === undefined &&
|
|
3896
|
+
executionAuthority === undefined) {
|
|
3897
|
+
toolCall.status = "error";
|
|
3898
|
+
toolCall.error = toolNotVisibleError(tc.toolName);
|
|
3899
|
+
setSpanAttributes(toolSpan, {
|
|
3900
|
+
"tool.status": "blocked",
|
|
3901
|
+
error: true,
|
|
3902
|
+
"error.type": "ToolExposureBlocked",
|
|
3863
3903
|
});
|
|
3864
|
-
|
|
3865
|
-
|
|
3904
|
+
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3905
|
+
pushPrivateArray(currentMessages, errorMessage);
|
|
3906
|
+
await persistMessage(errorMessage);
|
|
3907
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3908
|
+
return;
|
|
3909
|
+
}
|
|
3910
|
+
if (generatedToolResult === undefined &&
|
|
3911
|
+
isFrameworkToolSearch(tc.toolName, effectiveToolExposurePlan)) {
|
|
3912
|
+
let checkpoint;
|
|
3913
|
+
try {
|
|
3914
|
+
const search = executeFrameworkToolSearch({
|
|
3915
|
+
args: toolCall.args,
|
|
3916
|
+
plan: effectiveToolExposurePlan,
|
|
3917
|
+
state: toolExposureState,
|
|
3918
|
+
});
|
|
3919
|
+
if (didReloadProjectAgentWriteTool(search.result)) {
|
|
3920
|
+
agentWriteFinalResponseToolGuardEnabled = false;
|
|
3921
|
+
}
|
|
3922
|
+
toolCall.status = "completed";
|
|
3923
|
+
toolCall.result = search.result;
|
|
3924
|
+
setSpanAttributes(toolSpan, {
|
|
3925
|
+
"tool.status": "completed",
|
|
3926
|
+
"tool.search.result_count": search.result.resultCount,
|
|
3927
|
+
"tool.search.loaded_count": search.result.loadedCount,
|
|
3928
|
+
"tool.search.miss": search.result.miss,
|
|
3929
|
+
});
|
|
3930
|
+
const toolResultMessage = createToolResultMessage(tc.toolCallId, tc.toolName, search.result);
|
|
3931
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
3932
|
+
await persistMessage(toolResultMessage);
|
|
3933
|
+
checkpoint = search.checkpoint;
|
|
3866
3934
|
}
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3935
|
+
catch (error) {
|
|
3936
|
+
toolCall.status = "error";
|
|
3937
|
+
toolCall.error = error instanceof Error ? error.message : String(error);
|
|
3938
|
+
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
3939
|
+
pushPrivateArray(currentMessages, errorMessage);
|
|
3940
|
+
await persistMessage(errorMessage);
|
|
3941
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
await persistToolExposureCheckpointBeforeContinuation({
|
|
3945
|
+
checkpoint,
|
|
3946
|
+
persist: persistToolExposureCheckpoint,
|
|
3947
|
+
required: requireToolExposureCheckpointPersistence,
|
|
3874
3948
|
});
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
await persistMessage(toolResultMessage);
|
|
3878
|
-
checkpoint = search.checkpoint;
|
|
3949
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
3950
|
+
return;
|
|
3879
3951
|
}
|
|
3880
|
-
|
|
3952
|
+
// Provider-executed tools (web_search/web_fetch) return results without skill-state
|
|
3953
|
+
// transitions. Unlike locally-executed paths, load_skill and form_input are client-side
|
|
3954
|
+
// function tools that the runtime executes itself, so they never appear in
|
|
3955
|
+
// response.toolResults. This branch mirrors the streaming loop's providerExecuted===true
|
|
3956
|
+
// path, not the locally-executed ones. If provider-executed tools expand beyond web_*,
|
|
3957
|
+
// the transitions (skillState.applySuccessfulResult, markFormInputSubmitted) would apply.
|
|
3958
|
+
if (generatedToolResult && !hasToolReplacements) {
|
|
3959
|
+
if (generatedToolResult.providerExecuted === true) {
|
|
3960
|
+
await traceProviderExecutedTool({
|
|
3961
|
+
mode: "generate",
|
|
3962
|
+
agentId: this.id,
|
|
3963
|
+
toolName: tc.toolName,
|
|
3964
|
+
toolCallId: tc.toolCallId,
|
|
3965
|
+
context: {
|
|
3966
|
+
toolCallId: tc.toolCallId,
|
|
3967
|
+
...toolContext,
|
|
3968
|
+
agentId: this.id,
|
|
3969
|
+
},
|
|
3970
|
+
args: tc.input,
|
|
3971
|
+
result: generatedToolResult.result,
|
|
3972
|
+
isError: generatedToolResult.isError === true,
|
|
3973
|
+
});
|
|
3974
|
+
}
|
|
3975
|
+
await persistGeneratedToolResult(generatedToolResult);
|
|
3976
|
+
toolCall.status = generatedToolResult.isError === true ? "error" : "completed";
|
|
3977
|
+
toolCall.result = generatedToolResult.result;
|
|
3978
|
+
toolCall.error = generatedToolResult.isError === true
|
|
3979
|
+
? stringifyToolError(generatedToolResult.result)
|
|
3980
|
+
: undefined;
|
|
3981
|
+
if (toolCall.error !== undefined) {
|
|
3982
|
+
setOtelActiveSpanErrorStatus(new NativeError(`Tool "${tc.toolName}" failed`));
|
|
3983
|
+
}
|
|
3984
|
+
if (generatedToolResult.isError !== true &&
|
|
3985
|
+
shouldHideProjectToolAfterAgentWriteSuccess(tc.toolName)) {
|
|
3986
|
+
agentWriteFinalResponseToolGuardEnabled = true;
|
|
3987
|
+
}
|
|
3988
|
+
setSpanAttributes(toolSpan, compactRuntimeTraceAttributes({
|
|
3989
|
+
"tool.status": generatedToolResult.isError === true ? "failed" : "completed",
|
|
3990
|
+
"tool.provider_executed": generatedToolResult.providerExecuted === true,
|
|
3991
|
+
"tool.output.size_bytes": estimateSerializedSizeBytes(generatedToolResult.result),
|
|
3992
|
+
...(toolCall.error
|
|
3993
|
+
? {
|
|
3994
|
+
error: true,
|
|
3995
|
+
"error.type": "ProviderExecutedToolError",
|
|
3996
|
+
}
|
|
3997
|
+
: {}),
|
|
3998
|
+
}));
|
|
3999
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
4000
|
+
return;
|
|
4001
|
+
}
|
|
4002
|
+
const policyCheck = enforceSkillPolicy(tc.toolName, {
|
|
4003
|
+
activeSkillId: skillState.activeSkillId,
|
|
4004
|
+
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
4005
|
+
skillToolAvailability: skillState.activeSkillToolAvailability,
|
|
4006
|
+
toolInput: tc.input,
|
|
4007
|
+
toolDefinition: executionAuthority?.toolDefinition,
|
|
4008
|
+
});
|
|
4009
|
+
if (!policyCheck.allowed) {
|
|
3881
4010
|
toolCall.status = "error";
|
|
3882
|
-
toolCall.error =
|
|
3883
|
-
|
|
4011
|
+
toolCall.error = policyCheck.error;
|
|
4012
|
+
setSpanAttributes(toolSpan, {
|
|
4013
|
+
"tool.status": "blocked",
|
|
4014
|
+
error: true,
|
|
4015
|
+
"error.type": "ToolPolicyBlocked",
|
|
4016
|
+
});
|
|
4017
|
+
const errorMessage = {
|
|
4018
|
+
id: `tool_error_${tc.toolCallId}`,
|
|
4019
|
+
role: "tool",
|
|
4020
|
+
parts: [{
|
|
4021
|
+
type: "tool-result",
|
|
4022
|
+
toolCallId: tc.toolCallId,
|
|
4023
|
+
toolName: tc.toolName,
|
|
4024
|
+
result: { error: policyCheck.error },
|
|
4025
|
+
}],
|
|
4026
|
+
timestamp: Date.now(),
|
|
4027
|
+
};
|
|
3884
4028
|
pushPrivateArray(currentMessages, errorMessage);
|
|
3885
4029
|
await persistMessage(errorMessage);
|
|
3886
4030
|
pushPrivateArray(toolCalls, toolCall);
|
|
3887
4031
|
return;
|
|
3888
4032
|
}
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
if (generatedToolResult && !hasToolReplacements) {
|
|
3904
|
-
if (generatedToolResult.providerExecuted === true) {
|
|
3905
|
-
await traceProviderExecutedTool({
|
|
4033
|
+
try {
|
|
4034
|
+
toolCall.status = "executing";
|
|
4035
|
+
const startTime = Date.now();
|
|
4036
|
+
const cacheCtx = tryGetCacheKeyContext();
|
|
4037
|
+
toolCall.args = applySkillDelegationOverridesToToolInput(tc.toolName, toolCall.args, hasToolReplacements ? undefined : skillState.activeSkillDelegationOverrides, hasToolReplacements
|
|
4038
|
+
? undefined
|
|
4039
|
+
: resolveConfiguredTool(runtimeToolsConfig, tc.toolName, {
|
|
4040
|
+
agentId: this.id,
|
|
4041
|
+
}) ??
|
|
4042
|
+
undefined);
|
|
4043
|
+
const executionContext = applicationExecutionContext(toolContext, providerObservedSkillBodies);
|
|
4044
|
+
executionContext.projectId = cacheCtx?.projectId ?? toolContext?.projectId;
|
|
4045
|
+
throwIfAborted(abortSignal);
|
|
4046
|
+
const result = await traceConfiguredToolExecution({
|
|
3906
4047
|
mode: "generate",
|
|
3907
4048
|
agentId: this.id,
|
|
3908
4049
|
toolName: tc.toolName,
|
|
3909
4050
|
toolCallId: tc.toolCallId,
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4051
|
+
args: toolCall.args,
|
|
4052
|
+
admittedTurn,
|
|
4053
|
+
owner: currentMessages,
|
|
4054
|
+
prepareTerminalDispatch,
|
|
4055
|
+
toolsConfig: runtimeToolsConfig,
|
|
4056
|
+
context: executionContext,
|
|
4057
|
+
allowedRemoteToolNames,
|
|
4058
|
+
remoteToolSources,
|
|
4059
|
+
sourceIntegrationPolicy,
|
|
4060
|
+
strictConfiguredToolsOnly: hasToolReplacements,
|
|
4061
|
+
frameworkLocalTools,
|
|
3918
4062
|
});
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4063
|
+
await this.notifyToolResult({
|
|
4064
|
+
mode: "generate",
|
|
4065
|
+
toolName: tc.toolName,
|
|
4066
|
+
toolCallId: tc.toolCallId,
|
|
4067
|
+
input: toolCall.args,
|
|
4068
|
+
result,
|
|
4069
|
+
context: executionContext,
|
|
4070
|
+
});
|
|
4071
|
+
const resultError = getToolResultError(result);
|
|
4072
|
+
if (resultError !== undefined) {
|
|
4073
|
+
setOtelActiveSpanErrorStatus(new NativeError(`Tool "${tc.toolName}" failed`));
|
|
4074
|
+
}
|
|
4075
|
+
toolCall.status = resultError === undefined ? "completed" : "error";
|
|
4076
|
+
toolCall.result = result;
|
|
4077
|
+
toolCall.error = resultError;
|
|
4078
|
+
toolCall.executionTime = Date.now() - startTime;
|
|
4079
|
+
setSpanAttributes(toolSpan, compactRuntimeTraceAttributes({
|
|
4080
|
+
"tool.status": resultError === undefined ? "completed" : "failed",
|
|
4081
|
+
"tool.provider_executed": false,
|
|
4082
|
+
"tool.output.size_bytes": estimateSerializedSizeBytes(result),
|
|
4083
|
+
...(resultError === undefined ? {} : {
|
|
3939
4084
|
error: true,
|
|
3940
|
-
"error.type": "
|
|
4085
|
+
"error.type": "ToolResultError",
|
|
4086
|
+
}),
|
|
4087
|
+
}));
|
|
4088
|
+
if (resultError === undefined) {
|
|
4089
|
+
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.toolName)) {
|
|
4090
|
+
agentWriteFinalResponseToolGuardEnabled = true;
|
|
3941
4091
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
toolCall.error = policyCheck.error;
|
|
3957
|
-
setSpanAttributes(toolSpan, {
|
|
3958
|
-
"tool.status": "blocked",
|
|
3959
|
-
error: true,
|
|
3960
|
-
"error.type": "ToolPolicyBlocked",
|
|
3961
|
-
});
|
|
3962
|
-
const errorMessage = {
|
|
3963
|
-
id: `tool_error_${tc.toolCallId}`,
|
|
3964
|
-
role: "tool",
|
|
3965
|
-
parts: [{
|
|
3966
|
-
type: "tool-result",
|
|
3967
|
-
toolCallId: tc.toolCallId,
|
|
3968
|
-
toolName: tc.toolName,
|
|
3969
|
-
result: { error: policyCheck.error },
|
|
3970
|
-
}],
|
|
3971
|
-
timestamp: Date.now(),
|
|
3972
|
-
};
|
|
3973
|
-
pushPrivateArray(currentMessages, errorMessage);
|
|
3974
|
-
await persistMessage(errorMessage);
|
|
3975
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
3976
|
-
return;
|
|
3977
|
-
}
|
|
3978
|
-
try {
|
|
3979
|
-
toolCall.status = "executing";
|
|
3980
|
-
const startTime = Date.now();
|
|
3981
|
-
const cacheCtx = tryGetCacheKeyContext();
|
|
3982
|
-
toolCall.args = applySkillDelegationOverridesToToolInput(tc.toolName, toolCall.args, hasToolReplacements ? undefined : skillState.activeSkillDelegationOverrides, hasToolReplacements
|
|
3983
|
-
? undefined
|
|
3984
|
-
: resolveConfiguredTool(runtimeToolsConfig, tc.toolName, {
|
|
3985
|
-
agentId: this.id,
|
|
3986
|
-
}) ??
|
|
3987
|
-
undefined);
|
|
3988
|
-
const executionContext = applicationExecutionContext(toolContext, providerObservedSkillBodies);
|
|
3989
|
-
executionContext.projectId = cacheCtx?.projectId ?? toolContext?.projectId;
|
|
3990
|
-
throwIfAborted(abortSignal);
|
|
3991
|
-
const result = await traceConfiguredToolExecution({
|
|
3992
|
-
mode: "generate",
|
|
3993
|
-
agentId: this.id,
|
|
3994
|
-
toolName: tc.toolName,
|
|
3995
|
-
toolCallId: tc.toolCallId,
|
|
3996
|
-
args: toolCall.args,
|
|
3997
|
-
admittedTurn,
|
|
3998
|
-
owner: currentMessages,
|
|
3999
|
-
prepareTerminalDispatch,
|
|
4000
|
-
toolsConfig: runtimeToolsConfig,
|
|
4001
|
-
context: executionContext,
|
|
4002
|
-
allowedRemoteToolNames,
|
|
4003
|
-
remoteToolSources,
|
|
4004
|
-
sourceIntegrationPolicy,
|
|
4005
|
-
strictConfiguredToolsOnly: hasToolReplacements,
|
|
4006
|
-
frameworkLocalTools,
|
|
4007
|
-
});
|
|
4008
|
-
await this.notifyToolResult({
|
|
4009
|
-
mode: "generate",
|
|
4010
|
-
toolName: tc.toolName,
|
|
4011
|
-
toolCallId: tc.toolCallId,
|
|
4012
|
-
input: toolCall.args,
|
|
4013
|
-
result,
|
|
4014
|
-
context: executionContext,
|
|
4015
|
-
});
|
|
4016
|
-
const resultError = getToolResultError(result);
|
|
4017
|
-
if (resultError !== undefined) {
|
|
4018
|
-
setOtelActiveSpanErrorStatus(new NativeError(`Tool "${tc.toolName}" failed`));
|
|
4092
|
+
// Track skill policy only from successful framework-owned load_skill results.
|
|
4093
|
+
if (isLoadSkillToolName(tc.toolName) &&
|
|
4094
|
+
hasTrustedPlatformPolicyToolDefinition(executionAuthority?.toolDefinition)) {
|
|
4095
|
+
skillState.applySuccessfulResult(result);
|
|
4096
|
+
}
|
|
4097
|
+
const submittedFormInput = isSubmittedFormInputExecutionResult(tc.toolName, result, executionAuthority?.toolDefinition);
|
|
4098
|
+
skillState.markFormInputSubmitted(submittedFormInput);
|
|
4099
|
+
if (submittedFormInput) {
|
|
4100
|
+
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(tc.toolCallId, tc.toolName, result, executionAuthority?.toolDefinition, false, true);
|
|
4104
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
4105
|
+
await persistMessage(toolResultMessage);
|
|
4019
4106
|
}
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
"tool.output.size_bytes": estimateSerializedSizeBytes(result),
|
|
4028
|
-
...(resultError === undefined ? {} : {
|
|
4107
|
+
catch (error) {
|
|
4108
|
+
await this.recordTerminalToolResult(error, toolCall, persistMessage, currentMessages, toolCalls, totalUsage);
|
|
4109
|
+
throwIfAborted(abortSignal);
|
|
4110
|
+
toolCall.status = "error";
|
|
4111
|
+
toolCall.error = error instanceof Error ? error.message : String(error);
|
|
4112
|
+
setSpanAttributes(toolSpan, {
|
|
4113
|
+
"tool.status": "failed",
|
|
4029
4114
|
error: true,
|
|
4030
|
-
"error.type":
|
|
4031
|
-
})
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
agentWriteFinalResponseToolGuardEnabled = true;
|
|
4036
|
-
}
|
|
4037
|
-
// Track skill policy only from successful framework-owned load_skill results.
|
|
4038
|
-
if (isLoadSkillToolName(tc.toolName) &&
|
|
4039
|
-
hasTrustedPlatformPolicyToolDefinition(executionAuthority?.toolDefinition)) {
|
|
4040
|
-
skillState.applySuccessfulResult(result);
|
|
4041
|
-
}
|
|
4042
|
-
const submittedFormInput = isSubmittedFormInputExecutionResult(tc.toolName, result, executionAuthority?.toolDefinition);
|
|
4043
|
-
skillState.markFormInputSubmitted(submittedFormInput);
|
|
4044
|
-
if (submittedFormInput) {
|
|
4045
|
-
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
4046
|
-
}
|
|
4115
|
+
"error.type": telemetryErrorType(error),
|
|
4116
|
+
});
|
|
4117
|
+
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
4118
|
+
pushPrivateArray(currentMessages, errorMessage);
|
|
4119
|
+
await persistMessage(errorMessage);
|
|
4047
4120
|
}
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
catch (error) {
|
|
4053
|
-
await this.recordTerminalToolResult(error, toolCall, persistMessage, currentMessages, toolCalls, totalUsage);
|
|
4054
|
-
throwIfAborted(abortSignal);
|
|
4055
|
-
toolCall.status = "error";
|
|
4056
|
-
toolCall.error = error instanceof Error ? error.message : String(error);
|
|
4057
|
-
setSpanAttributes(toolSpan, {
|
|
4058
|
-
"tool.status": "failed",
|
|
4059
|
-
error: true,
|
|
4060
|
-
"error.type": telemetryErrorType(error),
|
|
4061
|
-
});
|
|
4062
|
-
const errorMessage = createToolErrorMessage(tc.toolCallId, tc.toolName, toolCall.error);
|
|
4063
|
-
pushPrivateArray(currentMessages, errorMessage);
|
|
4064
|
-
await persistMessage(errorMessage);
|
|
4065
|
-
}
|
|
4066
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
4067
|
-
});
|
|
4068
|
-
throwIfAborted(abortSignal);
|
|
4121
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
4122
|
+
});
|
|
4123
|
+
throwIfAborted(abortSignal);
|
|
4124
|
+
}
|
|
4069
4125
|
}
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4126
|
+
catch (error) {
|
|
4127
|
+
if (generatedTurnAdmissionObserved) {
|
|
4128
|
+
await observeGeneratedProviderTurnFinishOnce(response);
|
|
4129
|
+
}
|
|
4130
|
+
throw error;
|
|
4131
|
+
}
|
|
4132
|
+
if (isFinalMaxStepsToolTurn) {
|
|
4133
|
+
maxStepsParsedOutput = await tryParseMaxStepsOutput(response.text, outputSchema);
|
|
4134
|
+
await observeGeneratedProviderTurnFinishOnce({
|
|
4135
|
+
...response,
|
|
4136
|
+
...(maxStepsParsedOutput.parsed
|
|
4137
|
+
? { data: maxStepsParsedOutput.object, object: maxStepsParsedOutput.object }
|
|
4138
|
+
: {}),
|
|
4139
|
+
});
|
|
4074
4140
|
}
|
|
4075
|
-
throw error;
|
|
4076
4141
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4142
|
+
throwIfAborted(abortSignal);
|
|
4143
|
+
this.status = "completed";
|
|
4144
|
+
addSpanEvent(loopSpan, "max_steps_reached", { maxSteps });
|
|
4145
|
+
setSpanAttributes(loopSpan, buildRuntimeUsageTraceAttributes(totalUsage));
|
|
4146
|
+
// The last message on this exit is a tool result, so the response text
|
|
4147
|
+
// and the structured-output candidate come from the final assistant turn.
|
|
4148
|
+
const finalText = getFinalAssistantText(currentMessages);
|
|
4149
|
+
const parsedOutput = maxStepsParsedOutput ??
|
|
4150
|
+
await tryParseMaxStepsOutput(finalText, outputSchema);
|
|
4151
|
+
return attachOutputSchemaParser({
|
|
4152
|
+
text: finalText,
|
|
4153
|
+
...(parsedOutput.parsed ? { data: parsedOutput.object, object: parsedOutput.object } : {}),
|
|
4154
|
+
messages: currentMessages,
|
|
4155
|
+
toolCalls,
|
|
4156
|
+
status: this.status,
|
|
4157
|
+
usage: totalUsage,
|
|
4158
|
+
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, {
|
|
4159
|
+
warning: `Max steps (${maxSteps}) reached`,
|
|
4160
|
+
...(!parsedOutput.parsed && parsedOutput.outputSchemaError !== undefined
|
|
4161
|
+
? { outputSchemaError: parsedOutput.outputSchemaError }
|
|
4083
4162
|
: {}),
|
|
4084
|
-
})
|
|
4085
|
-
}
|
|
4163
|
+
}),
|
|
4164
|
+
}, withCachedMaxStepsOutputParse(outputSchema, parsedOutput, finalText));
|
|
4165
|
+
}
|
|
4166
|
+
catch (e_1) {
|
|
4167
|
+
env_1.error = e_1;
|
|
4168
|
+
env_1.hasError = true;
|
|
4169
|
+
}
|
|
4170
|
+
finally {
|
|
4171
|
+
const result_1 = __disposeResources(env_1);
|
|
4172
|
+
if (result_1)
|
|
4173
|
+
await result_1;
|
|
4086
4174
|
}
|
|
4087
|
-
throwIfAborted(abortSignal);
|
|
4088
|
-
this.status = "completed";
|
|
4089
|
-
addSpanEvent(loopSpan, "max_steps_reached", { maxSteps });
|
|
4090
|
-
setSpanAttributes(loopSpan, buildRuntimeUsageTraceAttributes(totalUsage));
|
|
4091
|
-
// The last message on this exit is a tool result, so the response text
|
|
4092
|
-
// and the structured-output candidate come from the final assistant turn.
|
|
4093
|
-
const finalText = getFinalAssistantText(currentMessages);
|
|
4094
|
-
const parsedOutput = maxStepsParsedOutput ??
|
|
4095
|
-
await tryParseMaxStepsOutput(finalText, outputSchema);
|
|
4096
|
-
return attachOutputSchemaParser({
|
|
4097
|
-
text: finalText,
|
|
4098
|
-
...(parsedOutput.parsed ? { data: parsedOutput.object, object: parsedOutput.object } : {}),
|
|
4099
|
-
messages: currentMessages,
|
|
4100
|
-
toolCalls,
|
|
4101
|
-
status: this.status,
|
|
4102
|
-
usage: totalUsage,
|
|
4103
|
-
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, {
|
|
4104
|
-
warning: `Max steps (${maxSteps}) reached`,
|
|
4105
|
-
...(!parsedOutput.parsed && parsedOutput.outputSchemaError !== undefined
|
|
4106
|
-
? { outputSchemaError: parsedOutput.outputSchemaError }
|
|
4107
|
-
: {}),
|
|
4108
|
-
}),
|
|
4109
|
-
}, withCachedMaxStepsOutputParse(outputSchema, parsedOutput, finalText));
|
|
4110
4175
|
});
|
|
4111
4176
|
}
|
|
4112
4177
|
/**
|
|
@@ -4116,1516 +4181,1529 @@ export class AgentRuntime {
|
|
|
4116
4181
|
*/
|
|
4117
4182
|
async #executeAgentLoopStreaming(// NOSONAR: Existing loop shape; this patch only adds authority cleanup.
|
|
4118
4183
|
systemPrompt, messages, validateProviderRequest, persistMessage, prepareTerminalDispatch, controller, encoder, callbacks, textPartId, toolContextBase, runtimeContext, runRuntimeContext, runtimeObservationsEnabled, supportsToolCalling, providerReplayCheckpointEmission, modelString, resolvedModel, headers, providerOptions, reasoning, maxOutputTokensOverride, abortSignal, temperatureModelString, outputSchema) {
|
|
4119
|
-
const {
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
const toolCalls = checkpoint?.toolCalls ?? [];
|
|
4134
|
-
const currentMessages = mapPrivateArray(checkpoint?.messages ?? messages, (message) => message);
|
|
4135
|
-
const runtimeGeneratedMessageIds = createPrivateSet(checkpoint?.runtimeGeneratedMessageIds ?? []);
|
|
4136
|
-
mapPrivateArray(currentMessages, (message) => {
|
|
4137
|
-
if (runtimeGeneratedMessageIds.has(message.id))
|
|
4138
|
-
markRuntimeGeneratedUserMessage(message);
|
|
4139
|
-
return message;
|
|
4140
|
-
});
|
|
4141
|
-
// JSON checkpoints drop in-memory trust markers; restore only the runtime-recorded results.
|
|
4142
|
-
restoreTrustedSkillLoadResultsFromPauseCheckpoint(currentMessages, checkpoint?.trustedSkillLoadResultIds, checkpoint?.trustedSkillLoadResultRefs);
|
|
4143
|
-
applyProviderReplayCheckpointsToMessages(currentMessages, getRuntimeProviderReplayCheckpoints(this.config), { activeProvider: resolveActiveProviderReplayProvider(languageModel) });
|
|
4144
|
-
const totalUsage = checkpoint?.usage ??
|
|
4145
|
-
{ promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
4146
|
-
if (!supportsToolCalling && this.config.tools) {
|
|
4147
|
-
warnUnsupportedToolCalling(this.id, effectiveModel);
|
|
4148
|
-
}
|
|
4149
|
-
// Request-scoped skill policy (not class-level mutable state)
|
|
4150
|
-
const skillState = AgentLoopSkillState.hydrate(currentMessages, runtimeContext);
|
|
4151
|
-
if (checkpoint) {
|
|
4152
|
-
skillState.activeSkillDelegationOverrides = checkpoint.activeSkillDelegationOverrides;
|
|
4153
|
-
skillState.markFormInputSubmitted(checkpoint.hasSubmittedFormInput === true);
|
|
4154
|
-
}
|
|
4155
|
-
let finalFinishReason = checkpoint?.finishReason;
|
|
4156
|
-
let latestAssistantText = checkpoint?.latestAssistantText ?? "";
|
|
4157
|
-
let completedWithinStepBudget = checkpoint?.completed ?? false;
|
|
4158
|
-
const initialToolExposureCheckpoint = checkpoint?.toolExposureCheckpoint ??
|
|
4159
|
-
getRuntimeToolExposureCheckpoint(this.config);
|
|
4160
|
-
let pauseToolExposureAuthorized;
|
|
4161
|
-
const toolExposureState = createToolExposureState();
|
|
4162
|
-
const persistToolExposureCheckpoint = getRuntimeToolExposureCheckpointPersister(this.config);
|
|
4163
|
-
const requireToolExposureCheckpointPersistence = isRuntimeToolExposureCheckpointPersistenceRequired(this.config);
|
|
4164
|
-
const toolLoadingResolution = resolveRuntimeToolLoading(this.config);
|
|
4165
|
-
const runtimeStepConfig = {
|
|
4166
|
-
...this.config,
|
|
4167
|
-
__vfToolLoadingMode: toolLoadingResolution.mode,
|
|
4168
|
-
};
|
|
4169
|
-
if (ObjectGetPrototypeOf(this.config) === null)
|
|
4170
|
-
ObjectSetPrototypeOf(runtimeStepConfig, null);
|
|
4171
|
-
const allowedRemoteToolNames = getRuntimeAllowedRemoteTools(this.config);
|
|
4172
|
-
const forwardedRemoteToolDefinitions = getRuntimeForwardedIntegrationToolDefs(this.config);
|
|
4173
|
-
const remoteToolSources = getRuntimeRemoteToolSources(this.config, undefined, this.id);
|
|
4174
|
-
const unavailableOptionalRemoteTools = getRuntimeUnavailableOptionalRemoteTools(this.config, remoteToolSources);
|
|
4175
|
-
const sourceIntegrationPolicy = getRuntimeSourceIntegrationPolicy(this.config);
|
|
4176
|
-
const frameworkLocalTools = createRuntimeFrameworkLocalTools(this.config);
|
|
4177
|
-
const configuredProviderTools = getRuntimeProviderTools(this.config);
|
|
4178
|
-
const toolResultContext = createActiveToolResultContext({
|
|
4179
|
-
config: this.config,
|
|
4180
|
-
supportsToolCalling,
|
|
4181
|
-
});
|
|
4182
|
-
const providerTools = sourceIntegrationPolicy
|
|
4183
|
-
? applySourceIntegrationPolicy(configuredProviderTools, sourceIntegrationPolicy)
|
|
4184
|
-
: configuredProviderTools;
|
|
4185
|
-
let currentSystemPrompt = systemPrompt;
|
|
4186
|
-
let currentRuntimeContext = runtimeContext;
|
|
4187
|
-
let agentWriteFinalResponseToolGuardEnabled = checkpoint?.agentWriteFinalResponseToolGuardEnabled ?? false;
|
|
4188
|
-
let recoveredEmptyResponse = checkpoint?.recoveredEmptyResponse ?? false;
|
|
4189
|
-
// One retry gives the model a chance to reconstruct a transport-truncated
|
|
4190
|
-
// batch without allowing a repeatedly broken provider stream to loop.
|
|
4191
|
-
let recoveredInterruptedLocalToolBatch = checkpoint?.recoveredInterruptedLocalToolBatch ??
|
|
4192
|
-
false;
|
|
4193
|
-
let interruptedLocalToolBatchRecoveryStep = checkpoint?.interruptedLocalToolBatchRecoveryStep;
|
|
4194
|
-
let interruptedLocalToolBatchRecoveryText = checkpoint?.interruptedLocalToolBatchRecoveryText;
|
|
4195
|
-
let resumeToolCallExecuted = checkpoint?.resumeToolCallExecuted ?? false;
|
|
4196
|
-
// A pause can precede startup preparation; checkpoint existence is not completion.
|
|
4197
|
-
let startupSkillLoaded = checkpoint?.startupSkillLoaded === true ||
|
|
4198
|
-
this.config.bootstrapSkill === undefined || skillState.hasSubmittedFormInput;
|
|
4199
|
-
const captureRecoveryContinuation = (nextStep) => parseAgentPauseCheckpoint({
|
|
4200
|
-
version: 1,
|
|
4201
|
-
nextStep,
|
|
4202
|
-
messages: currentMessages,
|
|
4203
|
-
toolCalls,
|
|
4204
|
-
usage: totalUsage,
|
|
4205
|
-
latestAssistantText,
|
|
4206
|
-
completed: completedWithinStepBudget,
|
|
4207
|
-
...(finalFinishReason ? { finishReason: finalFinishReason } : {}),
|
|
4208
|
-
recoveredEmptyResponse,
|
|
4209
|
-
startupSkillLoaded,
|
|
4210
|
-
recoveredInterruptedLocalToolBatch,
|
|
4211
|
-
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
4212
|
-
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
4213
|
-
// Omitted when empty so checkpoints without trusted loads keep the prior shape.
|
|
4214
|
-
...(() => {
|
|
4215
|
-
const ids = getTrustedSkillLoadResultIds(currentMessages);
|
|
4216
|
-
if (hasAmbiguousTrustedSkillLoadResultIds(currentMessages)) {
|
|
4217
|
-
return { trustedSkillLoadResultRefs: getTrustedSkillLoadResultRefs(currentMessages) };
|
|
4218
|
-
}
|
|
4219
|
-
return ids.length > 0 ? { trustedSkillLoadResultIds: ids } : {};
|
|
4220
|
-
})(),
|
|
4221
|
-
resumeToolCallExecuted,
|
|
4222
|
-
agentWriteFinalResponseToolGuardEnabled,
|
|
4223
|
-
interruptedLocalToolBatchRecoveryStep,
|
|
4224
|
-
interruptedLocalToolBatchRecoveryText,
|
|
4225
|
-
providerMetadata: mapPrivateArray(filterPrivateArray(currentMessages, (message) => readAttachedProviderMetadata(message) !== undefined), (message) => ({
|
|
4226
|
-
messageId: message.id,
|
|
4227
|
-
metadata: readAttachedProviderMetadata(message),
|
|
4228
|
-
})),
|
|
4229
|
-
runtimeGeneratedMessageIds: mapPrivateArray(filterPrivateArray(currentMessages, isRuntimeGeneratedUserMessage), (message) => message.id),
|
|
4230
|
-
toolExposureCheckpoint: pauseToolExposureAuthorized
|
|
4231
|
-
? createToolExposureCheckpoint(pauseToolExposureAuthorized, toolExposureState)
|
|
4232
|
-
: initialToolExposureCheckpoint,
|
|
4233
|
-
});
|
|
4234
|
-
const pauseAtBoundary = async (nextStep) => {
|
|
4235
|
-
if (this.#persistRecoveryBoundary) {
|
|
4236
|
-
const runtimeContinuation = captureRecoveryContinuation(nextStep);
|
|
4237
|
-
await this.#persistRecoveryBoundary({
|
|
4238
|
-
...createRecoveryBoundary(currentMessages, runtimeContinuation.toolExposureCheckpoint ?? { version: 2, loadedToolNames: [] }),
|
|
4239
|
-
runtimeContinuation,
|
|
4184
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
4185
|
+
try {
|
|
4186
|
+
const { maxAgentSteps } = getPlatformCapabilities();
|
|
4187
|
+
const maxSteps = this.computeMaxSteps(maxAgentSteps);
|
|
4188
|
+
const effectiveModel = resolveRuntimeModel(modelString || this.config.model);
|
|
4189
|
+
const languageModel = resolvedModel ?? resolveModel(effectiveModel);
|
|
4190
|
+
const continuation = this.#recoveryContinuation ?? await this.#manualPause?.load();
|
|
4191
|
+
const checkpoint = continuation == null ? undefined : parseAgentPauseCheckpoint(continuation);
|
|
4192
|
+
if (checkpoint?.providerMetadata) {
|
|
4193
|
+
mapPrivateArray(checkpoint.messages, (message) => {
|
|
4194
|
+
const saved = filterPrivateArray(checkpoint.providerMetadata, (entry) => entry.messageId === message.id)[0];
|
|
4195
|
+
if (saved) {
|
|
4196
|
+
attachProviderMetadata(message, saved.metadata);
|
|
4197
|
+
}
|
|
4240
4198
|
});
|
|
4241
4199
|
}
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
}
|
|
4250
|
-
catch {
|
|
4251
|
-
if (this.#manualPause.release && !(await this.#manualPause.release()))
|
|
4252
|
-
return;
|
|
4253
|
-
logger.warn("Agent is held at a safe boundary because its pause continuation cannot be saved");
|
|
4254
|
-
throw agentManualPauseBoundary();
|
|
4255
|
-
}
|
|
4256
|
-
let acknowledged = false;
|
|
4257
|
-
await validateProviderRequest(withAgentRunRuntimeContext(systemPrompt, runRuntimeContext), currentMessages, {
|
|
4258
|
-
beforeAcceptance: async () => {
|
|
4259
|
-
acknowledged = await this.#manualPause.acknowledge(saved);
|
|
4260
|
-
return acknowledged;
|
|
4261
|
-
},
|
|
4200
|
+
const toolCalls = checkpoint?.toolCalls ?? [];
|
|
4201
|
+
const currentMessages = mapPrivateArray(checkpoint?.messages ?? messages, (message) => message);
|
|
4202
|
+
const runtimeGeneratedMessageIds = createPrivateSet(checkpoint?.runtimeGeneratedMessageIds ?? []);
|
|
4203
|
+
mapPrivateArray(currentMessages, (message) => {
|
|
4204
|
+
if (runtimeGeneratedMessageIds.has(message.id))
|
|
4205
|
+
markRuntimeGeneratedUserMessage(message);
|
|
4206
|
+
return message;
|
|
4262
4207
|
});
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4208
|
+
// JSON checkpoints drop in-memory trust markers; restore only the runtime-recorded results.
|
|
4209
|
+
restoreTrustedSkillLoadResultsFromPauseCheckpoint(currentMessages, checkpoint?.trustedSkillLoadResultIds, checkpoint?.trustedSkillLoadResultRefs);
|
|
4210
|
+
applyProviderReplayCheckpointsToMessages(currentMessages, getRuntimeProviderReplayCheckpoints(this.config), { activeProvider: resolveActiveProviderReplayProvider(languageModel) });
|
|
4211
|
+
const totalUsage = checkpoint?.usage ??
|
|
4212
|
+
{ promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
4213
|
+
if (!supportsToolCalling && this.config.tools) {
|
|
4214
|
+
warnUnsupportedToolCalling(this.id, effectiveModel);
|
|
4215
|
+
}
|
|
4216
|
+
// Request-scoped skill policy (not class-level mutable state)
|
|
4217
|
+
const skillState = AgentLoopSkillState.hydrate(currentMessages, runtimeContext);
|
|
4268
4218
|
if (checkpoint) {
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
const message = currentMessages[index];
|
|
4272
|
-
if (persistedIds.has(message.id))
|
|
4273
|
-
continue;
|
|
4274
|
-
await persistMessage(message);
|
|
4275
|
-
persistedIds.add(message.id);
|
|
4276
|
-
}
|
|
4219
|
+
skillState.activeSkillDelegationOverrides = checkpoint.activeSkillDelegationOverrides;
|
|
4220
|
+
skillState.markFormInputSubmitted(checkpoint.hasSubmittedFormInput === true);
|
|
4277
4221
|
}
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
const
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4222
|
+
let finalFinishReason = checkpoint?.finishReason;
|
|
4223
|
+
let latestAssistantText = checkpoint?.latestAssistantText ?? "";
|
|
4224
|
+
let completedWithinStepBudget = checkpoint?.completed ?? false;
|
|
4225
|
+
const initialToolExposureCheckpoint = checkpoint?.toolExposureCheckpoint ??
|
|
4226
|
+
getRuntimeToolExposureCheckpoint(this.config);
|
|
4227
|
+
let pauseToolExposureAuthorized;
|
|
4228
|
+
const toolExposureState = createToolExposureState();
|
|
4229
|
+
const persistToolExposureCheckpoint = getRuntimeToolExposureCheckpointPersister(this.config);
|
|
4230
|
+
const requireToolExposureCheckpointPersistence = isRuntimeToolExposureCheckpointPersistenceRequired(this.config);
|
|
4231
|
+
const toolLoadingResolution = resolveRuntimeToolLoading(this.config);
|
|
4232
|
+
const runtimeStepConfig = {
|
|
4233
|
+
...this.config,
|
|
4234
|
+
__vfToolLoadingMode: toolLoadingResolution.mode,
|
|
4235
|
+
};
|
|
4236
|
+
if (ObjectGetPrototypeOf(this.config) === null)
|
|
4237
|
+
ObjectSetPrototypeOf(runtimeStepConfig, null);
|
|
4238
|
+
const allowedRemoteToolNames = getRuntimeAllowedRemoteTools(this.config);
|
|
4239
|
+
const forwardedRemoteToolDefinitions = getRuntimeForwardedIntegrationToolDefs(this.config);
|
|
4240
|
+
const remoteToolSources = getRuntimeRemoteToolSources(this.config, undefined, this.id);
|
|
4241
|
+
const _remoteMcpCleanup = __addDisposableResource(env_2, createRuntimeMcpSourceCleanup(remoteToolSources), true);
|
|
4242
|
+
const unavailableOptionalRemoteTools = getRuntimeUnavailableOptionalRemoteTools(this.config, remoteToolSources);
|
|
4243
|
+
const sourceIntegrationPolicy = getRuntimeSourceIntegrationPolicy(this.config);
|
|
4244
|
+
const frameworkLocalTools = createRuntimeFrameworkLocalTools(this.config);
|
|
4245
|
+
const configuredProviderTools = getRuntimeProviderTools(this.config);
|
|
4246
|
+
const toolResultContext = createActiveToolResultContext({
|
|
4247
|
+
config: this.config,
|
|
4303
4248
|
supportsToolCalling,
|
|
4304
|
-
|
|
4249
|
+
});
|
|
4250
|
+
const providerTools = sourceIntegrationPolicy
|
|
4251
|
+
? applySourceIntegrationPolicy(configuredProviderTools, sourceIntegrationPolicy)
|
|
4252
|
+
: configuredProviderTools;
|
|
4253
|
+
let currentSystemPrompt = systemPrompt;
|
|
4254
|
+
let currentRuntimeContext = runtimeContext;
|
|
4255
|
+
let agentWriteFinalResponseToolGuardEnabled = checkpoint?.agentWriteFinalResponseToolGuardEnabled ?? false;
|
|
4256
|
+
let recoveredEmptyResponse = checkpoint?.recoveredEmptyResponse ?? false;
|
|
4257
|
+
// One retry gives the model a chance to reconstruct a transport-truncated
|
|
4258
|
+
// batch without allowing a repeatedly broken provider stream to loop.
|
|
4259
|
+
let recoveredInterruptedLocalToolBatch = checkpoint?.recoveredInterruptedLocalToolBatch ??
|
|
4260
|
+
false;
|
|
4261
|
+
let interruptedLocalToolBatchRecoveryStep = checkpoint?.interruptedLocalToolBatchRecoveryStep;
|
|
4262
|
+
let interruptedLocalToolBatchRecoveryText = checkpoint?.interruptedLocalToolBatchRecoveryText;
|
|
4263
|
+
let resumeToolCallExecuted = checkpoint?.resumeToolCallExecuted ?? false;
|
|
4264
|
+
// A pause can precede startup preparation; checkpoint existence is not completion.
|
|
4265
|
+
let startupSkillLoaded = checkpoint?.startupSkillLoaded === true ||
|
|
4266
|
+
this.config.bootstrapSkill === undefined || skillState.hasSubmittedFormInput;
|
|
4267
|
+
const captureRecoveryContinuation = (nextStep) => parseAgentPauseCheckpoint({
|
|
4268
|
+
version: 1,
|
|
4269
|
+
nextStep,
|
|
4305
4270
|
messages: currentMessages,
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4271
|
+
toolCalls,
|
|
4272
|
+
usage: totalUsage,
|
|
4273
|
+
latestAssistantText,
|
|
4274
|
+
completed: completedWithinStepBudget,
|
|
4275
|
+
...(finalFinishReason ? { finishReason: finalFinishReason } : {}),
|
|
4276
|
+
recoveredEmptyResponse,
|
|
4277
|
+
startupSkillLoaded,
|
|
4278
|
+
recoveredInterruptedLocalToolBatch,
|
|
4279
|
+
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
4280
|
+
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
4281
|
+
// Omitted when empty so checkpoints without trusted loads keep the prior shape.
|
|
4282
|
+
...(() => {
|
|
4283
|
+
const ids = getTrustedSkillLoadResultIds(currentMessages);
|
|
4284
|
+
if (hasAmbiguousTrustedSkillLoadResultIds(currentMessages)) {
|
|
4285
|
+
return { trustedSkillLoadResultRefs: getTrustedSkillLoadResultRefs(currentMessages) };
|
|
4286
|
+
}
|
|
4287
|
+
return ids.length > 0 ? { trustedSkillLoadResultIds: ids } : {};
|
|
4288
|
+
})(),
|
|
4289
|
+
resumeToolCallExecuted,
|
|
4290
|
+
agentWriteFinalResponseToolGuardEnabled,
|
|
4291
|
+
interruptedLocalToolBatchRecoveryStep,
|
|
4292
|
+
interruptedLocalToolBatchRecoveryText,
|
|
4293
|
+
providerMetadata: mapPrivateArray(filterPrivateArray(currentMessages, (message) => readAttachedProviderMetadata(message) !== undefined), (message) => ({
|
|
4294
|
+
messageId: message.id,
|
|
4295
|
+
metadata: readAttachedProviderMetadata(message),
|
|
4296
|
+
})),
|
|
4297
|
+
runtimeGeneratedMessageIds: mapPrivateArray(filterPrivateArray(currentMessages, isRuntimeGeneratedUserMessage), (message) => message.id),
|
|
4298
|
+
toolExposureCheckpoint: pauseToolExposureAuthorized
|
|
4299
|
+
? createToolExposureCheckpoint(pauseToolExposureAuthorized, toolExposureState)
|
|
4300
|
+
: initialToolExposureCheckpoint,
|
|
4325
4301
|
});
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
}
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
await
|
|
4302
|
+
const pauseAtBoundary = async (nextStep) => {
|
|
4303
|
+
if (this.#persistRecoveryBoundary) {
|
|
4304
|
+
const runtimeContinuation = captureRecoveryContinuation(nextStep);
|
|
4305
|
+
await this.#persistRecoveryBoundary({
|
|
4306
|
+
...createRecoveryBoundary(currentMessages, runtimeContinuation.toolExposureCheckpoint ?? { version: 2, loadedToolNames: [] }),
|
|
4307
|
+
runtimeContinuation,
|
|
4308
|
+
});
|
|
4309
|
+
}
|
|
4310
|
+
if (!this.#manualPause)
|
|
4311
|
+
return;
|
|
4312
|
+
if (this.#manualPause.requested && !(await this.#manualPause.requested()))
|
|
4313
|
+
return;
|
|
4314
|
+
let saved;
|
|
4315
|
+
try {
|
|
4316
|
+
saved = captureRecoveryContinuation(nextStep);
|
|
4317
|
+
}
|
|
4318
|
+
catch {
|
|
4319
|
+
if (this.#manualPause.release && !(await this.#manualPause.release()))
|
|
4320
|
+
return;
|
|
4321
|
+
logger.warn("Agent is held at a safe boundary because its pause continuation cannot be saved");
|
|
4322
|
+
throw agentManualPauseBoundary();
|
|
4323
|
+
}
|
|
4324
|
+
let acknowledged = false;
|
|
4325
|
+
await validateProviderRequest(withAgentRunRuntimeContext(systemPrompt, runRuntimeContext), currentMessages, {
|
|
4326
|
+
beforeAcceptance: async () => {
|
|
4327
|
+
acknowledged = await this.#manualPause.acknowledge(saved);
|
|
4328
|
+
return acknowledged;
|
|
4329
|
+
},
|
|
4330
|
+
});
|
|
4331
|
+
if (acknowledged)
|
|
4332
|
+
throw agentManualPauseBoundary();
|
|
4333
|
+
};
|
|
4334
|
+
if (this.#manualPause) {
|
|
4335
|
+
await validateProviderRequest(withAgentRunRuntimeContext(systemPrompt, runRuntimeContext), currentMessages, { deferAcceptance: true });
|
|
4336
|
+
if (checkpoint) {
|
|
4337
|
+
const persistedIds = createPrivateSet(mapPrivateArray(messages, (message) => message.id));
|
|
4338
|
+
for (let index = 0; index < currentMessages.length; index++) {
|
|
4339
|
+
const message = currentMessages[index];
|
|
4340
|
+
if (persistedIds.has(message.id))
|
|
4341
|
+
continue;
|
|
4342
|
+
await persistMessage(message);
|
|
4343
|
+
persistedIds.add(message.id);
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
await pauseAtBoundary(checkpoint?.nextStep ?? 0);
|
|
4348
|
+
for (let step = checkpoint?.nextStep ?? 0; !completedWithinStepBudget && step < maxSteps; step++) {
|
|
4349
|
+
throwIfAborted(abortSignal);
|
|
4350
|
+
const currentStepToolResults = createPrivateMap();
|
|
4351
|
+
const stepRuntimeContext = skillState.hasSubmittedFormInput
|
|
4352
|
+
? markSubmittedFormInputRuntimeContext(currentRuntimeContext)
|
|
4353
|
+
: currentRuntimeContext;
|
|
4354
|
+
const preparedStep = await prepareAgentRuntimeStep({
|
|
4337
4355
|
agentId: this.id,
|
|
4338
|
-
|
|
4339
|
-
|
|
4356
|
+
activeSkillId: skillState.activeSkillId,
|
|
4357
|
+
activeSkillToolAvailability: skillState.activeSkillToolAvailability,
|
|
4358
|
+
allowedRemoteToolNames,
|
|
4359
|
+
config: runtimeStepConfig,
|
|
4360
|
+
effectiveModel,
|
|
4361
|
+
...resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
4362
|
+
reservedToolSlots: getToolResultReaderReservedSlots(toolResultContext),
|
|
4363
|
+
excludedToolNames: agentWriteFinalResponseToolGuardEnabled &&
|
|
4364
|
+
toolLoadingResolution.mode === "eager"
|
|
4365
|
+
? AGENT_WRITE_FINAL_RESPONSE_EXCLUDED_TOOL_NAMES
|
|
4366
|
+
: undefined,
|
|
4367
|
+
forwardedRemoteToolDefinitions,
|
|
4368
|
+
getAvailableTools,
|
|
4369
|
+
unavailableOptionalRemoteToolNames: unavailableOptionalRemoteTools.names,
|
|
4370
|
+
unavailableOptionalRemoteToolPrefixes: unavailableOptionalRemoteTools.prefixes,
|
|
4371
|
+
supportsToolCalling,
|
|
4372
|
+
includeStartupAuthorizedTools: !startupSkillLoaded,
|
|
4340
4373
|
messages: currentMessages,
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4374
|
+
mode: "stream",
|
|
4375
|
+
modelRuntime: languageModel,
|
|
4376
|
+
providerOptionKey: resolveModelProviderOptionKey(effectiveModel, languageModel),
|
|
4377
|
+
authorizedProviderToolNames: providerTools,
|
|
4378
|
+
providerToolNames: supportsToolCalling && !agentWriteFinalResponseToolGuardEnabled
|
|
4379
|
+
? providerTools
|
|
4380
|
+
: [],
|
|
4381
|
+
remoteToolSources,
|
|
4382
|
+
sourceIntegrationPolicy,
|
|
4383
|
+
resolveRuntimeState: this.resolveRuntimeState.bind(this),
|
|
4384
|
+
runtimeContext: stepRuntimeContext,
|
|
4385
|
+
step,
|
|
4386
|
+
systemPrompt: currentSystemPrompt,
|
|
4387
|
+
toolContextBase,
|
|
4388
|
+
frameworkLocalTools,
|
|
4389
|
+
toolExposureState,
|
|
4390
|
+
toolExposureCheckpoint: step === (checkpoint?.nextStep ?? 0)
|
|
4391
|
+
? initialToolExposureCheckpoint
|
|
4392
|
+
: undefined,
|
|
4393
|
+
});
|
|
4394
|
+
pauseToolExposureAuthorized = preparedStep.toolExposurePlan.authorized;
|
|
4395
|
+
currentSystemPrompt = preparedStep.systemPrompt;
|
|
4396
|
+
currentRuntimeContext = preparedStep.runtimeContext;
|
|
4397
|
+
const toolContext = preparedStep.toolContext;
|
|
4398
|
+
let effectiveToolExposurePlan = agentWriteFinalResponseToolGuardEnabled
|
|
4399
|
+
? applyAgentWriteFinalResponseGuard(preparedStep.toolExposurePlan, {
|
|
4400
|
+
reloadable: toolLoadingResolution.mode === "deferred",
|
|
4401
|
+
})
|
|
4402
|
+
: preparedStep.toolExposurePlan;
|
|
4403
|
+
if (!startupSkillLoaded) {
|
|
4404
|
+
await executeStartupSkill({
|
|
4405
|
+
agentId: this.id,
|
|
4406
|
+
skillId: this.config.bootstrapSkill,
|
|
4407
|
+
authorized: preparedStep.startupAuthorizedTools,
|
|
4408
|
+
messages: currentMessages,
|
|
4409
|
+
persistMessage,
|
|
4410
|
+
skillState,
|
|
4411
|
+
toolCalls,
|
|
4412
|
+
onToolCall: callbacks?.onToolCall,
|
|
4413
|
+
...(this.#persistRecoveryBoundary
|
|
4414
|
+
? {
|
|
4415
|
+
recoveryStart: async (call) => {
|
|
4416
|
+
const occurrenceId = introduceToolCallOccurrence(call);
|
|
4417
|
+
if (!occurrenceId) {
|
|
4418
|
+
throw new Error("Managed startup skill lacks original occurrence scope");
|
|
4419
|
+
}
|
|
4420
|
+
sendSSE(controller, encoder, {
|
|
4421
|
+
type: "tool-input-start",
|
|
4422
|
+
toolCallId: call.id,
|
|
4423
|
+
toolName: call.name,
|
|
4424
|
+
privateToolCallOccurrenceId: occurrenceId,
|
|
4425
|
+
});
|
|
4426
|
+
sendSSE(controller, encoder, {
|
|
4427
|
+
type: "tool-input-available",
|
|
4428
|
+
toolCallId: call.id,
|
|
4429
|
+
toolName: call.name,
|
|
4430
|
+
input: call.args,
|
|
4431
|
+
});
|
|
4432
|
+
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
4433
|
+
},
|
|
4434
|
+
recoveryResult: async (call) => {
|
|
4435
|
+
sendSSE(controller, encoder, call.error === undefined
|
|
4436
|
+
? { type: "tool-output-available", toolCallId: call.id, output: call.result }
|
|
4437
|
+
: { type: "tool-output-error", toolCallId: call.id, errorText: call.error });
|
|
4438
|
+
if (call.status === "completed")
|
|
4439
|
+
startupSkillLoaded = true;
|
|
4440
|
+
const runtimeContinuation = captureRecoveryContinuation(step);
|
|
4441
|
+
await this.#persistRecoveryBoundary({
|
|
4442
|
+
...createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)),
|
|
4443
|
+
runtimeContinuation,
|
|
4444
|
+
});
|
|
4445
|
+
},
|
|
4446
|
+
}
|
|
4447
|
+
: {}),
|
|
4448
|
+
resolveLoader: (name) => resolveConfiguredTool(this.config.tools, name, { agentId: this.id }),
|
|
4449
|
+
execute: async (toolName, toolCallId, args, admittedTurn) => {
|
|
4450
|
+
const context = applicationExecutionContext(toolContext);
|
|
4451
|
+
const result = await traceConfiguredToolExecution({
|
|
4452
|
+
mode: "stream",
|
|
4453
|
+
agentId: this.id,
|
|
4454
|
+
toolName,
|
|
4455
|
+
toolCallId,
|
|
4456
|
+
args,
|
|
4457
|
+
admittedTurn,
|
|
4458
|
+
owner: currentMessages,
|
|
4459
|
+
prepareTerminalDispatch,
|
|
4460
|
+
toolsConfig: this.config.tools,
|
|
4461
|
+
context,
|
|
4462
|
+
allowedRemoteToolNames,
|
|
4463
|
+
remoteToolSources,
|
|
4464
|
+
sourceIntegrationPolicy,
|
|
4465
|
+
frameworkLocalTools,
|
|
4466
|
+
});
|
|
4467
|
+
await this.notifyToolResult({
|
|
4468
|
+
mode: "stream",
|
|
4469
|
+
toolName,
|
|
4470
|
+
toolCallId,
|
|
4471
|
+
input: args,
|
|
4472
|
+
result,
|
|
4473
|
+
context,
|
|
4474
|
+
});
|
|
4475
|
+
return result;
|
|
4476
|
+
},
|
|
4477
|
+
});
|
|
4478
|
+
startupSkillLoaded = true;
|
|
4479
|
+
// A pause requested while the loader ran must hold before the first
|
|
4480
|
+
// provider call; the checkpoint records the completed startup load.
|
|
4481
|
+
await pauseAtBoundary(step);
|
|
4482
|
+
// Refresh the active skill policy without consuming a model step.
|
|
4483
|
+
step--;
|
|
4484
|
+
continue;
|
|
4485
|
+
}
|
|
4486
|
+
const runtimeObservationStepId = runtimeObservationsEnabled ? dntShim.crypto.randomUUID() : undefined;
|
|
4487
|
+
const runtimeObservationMessageSpanId = runtimeObservationStepId !== undefined
|
|
4488
|
+
? dntShim.crypto.randomUUID()
|
|
4489
|
+
: undefined;
|
|
4490
|
+
sendSSE(controller, encoder, {
|
|
4491
|
+
type: "step-start",
|
|
4492
|
+
...(runtimeObservationStepId
|
|
4346
4493
|
? {
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
}
|
|
4352
|
-
sendSSE(controller, encoder, {
|
|
4353
|
-
type: "tool-input-start",
|
|
4354
|
-
toolCallId: call.id,
|
|
4355
|
-
toolName: call.name,
|
|
4356
|
-
privateToolCallOccurrenceId: occurrenceId,
|
|
4357
|
-
});
|
|
4358
|
-
sendSSE(controller, encoder, {
|
|
4359
|
-
type: "tool-input-available",
|
|
4360
|
-
toolCallId: call.id,
|
|
4361
|
-
toolName: call.name,
|
|
4362
|
-
input: call.args,
|
|
4363
|
-
});
|
|
4364
|
-
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
4365
|
-
},
|
|
4366
|
-
recoveryResult: async (call) => {
|
|
4367
|
-
sendSSE(controller, encoder, call.error === undefined
|
|
4368
|
-
? { type: "tool-output-available", toolCallId: call.id, output: call.result }
|
|
4369
|
-
: { type: "tool-output-error", toolCallId: call.id, errorText: call.error });
|
|
4370
|
-
if (call.status === "completed")
|
|
4371
|
-
startupSkillLoaded = true;
|
|
4372
|
-
const runtimeContinuation = captureRecoveryContinuation(step);
|
|
4373
|
-
await this.#persistRecoveryBoundary({
|
|
4374
|
-
...createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)),
|
|
4375
|
-
runtimeContinuation,
|
|
4376
|
-
});
|
|
4494
|
+
privateRuntimeObservation: {
|
|
4495
|
+
version: 1,
|
|
4496
|
+
kind: "step_started",
|
|
4497
|
+
stepId: runtimeObservationStepId,
|
|
4377
4498
|
},
|
|
4378
4499
|
}
|
|
4379
4500
|
: {}),
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4501
|
+
});
|
|
4502
|
+
assertToolResultReaderNameAvailable({
|
|
4503
|
+
plan: effectiveToolExposurePlan,
|
|
4504
|
+
context: toolResultContext,
|
|
4505
|
+
});
|
|
4506
|
+
if (!resumeToolCallExecuted && this.#resumeToolCall) {
|
|
4507
|
+
const resumeToolCall = this.#resumeToolCall;
|
|
4508
|
+
resumeToolCallExecuted = true;
|
|
4509
|
+
const inputText = privateJsonStringify(resumeToolCall.input);
|
|
4510
|
+
const streamedCall = {
|
|
4511
|
+
id: resumeToolCall.id,
|
|
4512
|
+
name: resumeToolCall.name,
|
|
4513
|
+
arguments: inputText,
|
|
4514
|
+
inputDeltas: [inputText],
|
|
4515
|
+
inputAvailable: true,
|
|
4516
|
+
};
|
|
4517
|
+
announceStreamedToolCallInput(controller, encoder, streamedCall, (name) => isDynamicTool(name, this.config.tools));
|
|
4518
|
+
sendSSE(controller, encoder, {
|
|
4519
|
+
type: "tool-input-available",
|
|
4520
|
+
toolCallId: resumeToolCall.id,
|
|
4521
|
+
toolName: resumeToolCall.name,
|
|
4522
|
+
input: resumeToolCall.input,
|
|
4523
|
+
...(isDynamicTool(resumeToolCall.name, this.config.tools) ? { dynamic: true } : {}),
|
|
4524
|
+
});
|
|
4525
|
+
const assistantToolCallMessage = {
|
|
4526
|
+
id: generateMessageId(),
|
|
4527
|
+
role: "assistant",
|
|
4528
|
+
parts: [{
|
|
4529
|
+
type: `tool-${resumeToolCall.name}`,
|
|
4530
|
+
toolCallId: resumeToolCall.id,
|
|
4531
|
+
toolName: resumeToolCall.name,
|
|
4532
|
+
args: resumeToolCall.input,
|
|
4533
|
+
}],
|
|
4534
|
+
};
|
|
4535
|
+
let admittedTurn = snapshotAdmittedToolTurn(assistantToolCallMessage, currentMessages.length);
|
|
4536
|
+
for (let index = currentMessages.length - 1; index >= 0; index--) {
|
|
4537
|
+
const message = currentMessages[index];
|
|
4538
|
+
if (message.role !== "assistant")
|
|
4539
|
+
continue;
|
|
4540
|
+
const candidate = snapshotAdmittedToolTurn(message, index);
|
|
4541
|
+
if (somePrivateArray(candidate.calls, (call) => call.toolCallId === resumeToolCall.id && call.toolName === resumeToolCall.name &&
|
|
4542
|
+
providerValuesEqual(call.input, resumeToolCall.input, new IntrinsicWeakMap())))
|
|
4543
|
+
admittedTurn = candidate;
|
|
4544
|
+
// A newer assistant envelope is a turn boundary, never inferred replay provenance.
|
|
4545
|
+
break;
|
|
4546
|
+
}
|
|
4547
|
+
if (admittedTurn.start === currentMessages.length) {
|
|
4548
|
+
pushPrivateArray(currentMessages, assistantToolCallMessage);
|
|
4549
|
+
await persistMessage(assistantToolCallMessage);
|
|
4550
|
+
}
|
|
4551
|
+
if (this.#persistRecoveryBoundary) {
|
|
4552
|
+
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
4553
|
+
throwIfAborted(abortSignal);
|
|
4554
|
+
}
|
|
4555
|
+
const toolCall = {
|
|
4556
|
+
id: resumeToolCall.id,
|
|
4557
|
+
name: resumeToolCall.name,
|
|
4558
|
+
args: resumeToolCall.input,
|
|
4559
|
+
status: "executing",
|
|
4560
|
+
};
|
|
4561
|
+
const executionContext = applicationExecutionContext(toolContext,
|
|
4562
|
+
// Partial results from the parked assistant turn were not yet provider-observed.
|
|
4563
|
+
getProviderObservedSkillBodies(slicePrivateArray(currentMessages, 0, admittedTurn.start)));
|
|
4564
|
+
let resumeAdmissionRejection;
|
|
4565
|
+
try {
|
|
4566
|
+
// The trusted parked call was already exposed in the prior segment.
|
|
4567
|
+
// Recheck current authorization without requiring its lost step visibility.
|
|
4568
|
+
const resumeExecutionAuthority = resolveToolExecutionAuthority({
|
|
4569
|
+
toolName: resumeToolCall.name,
|
|
4570
|
+
plan: {
|
|
4571
|
+
...effectiveToolExposurePlan,
|
|
4572
|
+
visible: effectiveToolExposurePlan.authorized,
|
|
4573
|
+
},
|
|
4574
|
+
});
|
|
4575
|
+
if (resumeExecutionAuthority === undefined) {
|
|
4576
|
+
throw new Error(toolNotVisibleError(resumeToolCall.name));
|
|
4577
|
+
}
|
|
4578
|
+
const policyCheck = enforceSkillPolicy(resumeToolCall.name, {
|
|
4579
|
+
activeSkillId: skillState.activeSkillId,
|
|
4580
|
+
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
4581
|
+
skillToolAvailability: skillState.activeSkillToolAvailability,
|
|
4582
|
+
toolInput: toolCall.args,
|
|
4583
|
+
toolDefinition: resumeExecutionAuthority.toolDefinition,
|
|
4584
|
+
});
|
|
4585
|
+
if (!policyCheck.allowed)
|
|
4586
|
+
throw new Error(policyCheck.error);
|
|
4587
|
+
toolCall.args = applySkillDelegationOverridesToToolInput(resumeToolCall.name, toolCall.args, skillState.activeSkillDelegationOverrides, resolveConfiguredTool(this.config.tools, resumeToolCall.name, { agentId: this.id }) ??
|
|
4588
|
+
undefined);
|
|
4589
|
+
// Resuming a trusted parked tool is a real dispatch even before the
|
|
4590
|
+
// next provider request. Validate before its effect, then retain that
|
|
4591
|
+
// accepted history if later provider preparation fails.
|
|
4592
|
+
try {
|
|
4593
|
+
await validateProviderRequest(withAgentRunRuntimeContext(currentSystemPrompt, runRuntimeContext), currentMessages);
|
|
4594
|
+
}
|
|
4595
|
+
catch (error) {
|
|
4596
|
+
resumeAdmissionRejection = { error };
|
|
4597
|
+
throw error;
|
|
4598
|
+
}
|
|
4599
|
+
callbacks?.onToolCall?.(toolCall);
|
|
4600
|
+
const startTime = Date.now();
|
|
4601
|
+
const result = await runWithToolCallOccurrenceDispatch(streamedCall, () => traceConfiguredToolExecution({
|
|
4384
4602
|
mode: "stream",
|
|
4385
4603
|
agentId: this.id,
|
|
4386
|
-
toolName,
|
|
4387
|
-
toolCallId,
|
|
4388
|
-
args,
|
|
4604
|
+
toolName: resumeToolCall.name,
|
|
4605
|
+
toolCallId: resumeToolCall.id,
|
|
4606
|
+
args: toolCall.args,
|
|
4389
4607
|
admittedTurn,
|
|
4390
4608
|
owner: currentMessages,
|
|
4391
4609
|
prepareTerminalDispatch,
|
|
4392
4610
|
toolsConfig: this.config.tools,
|
|
4393
|
-
context,
|
|
4611
|
+
context: executionContext,
|
|
4394
4612
|
allowedRemoteToolNames,
|
|
4395
4613
|
remoteToolSources,
|
|
4396
4614
|
sourceIntegrationPolicy,
|
|
4397
4615
|
frameworkLocalTools,
|
|
4398
|
-
});
|
|
4616
|
+
}));
|
|
4617
|
+
throwIfAborted(abortSignal);
|
|
4399
4618
|
await this.notifyToolResult({
|
|
4400
4619
|
mode: "stream",
|
|
4401
|
-
toolName,
|
|
4402
|
-
toolCallId,
|
|
4403
|
-
input: args,
|
|
4620
|
+
toolName: resumeToolCall.name,
|
|
4621
|
+
toolCallId: resumeToolCall.id,
|
|
4622
|
+
input: toolCall.args,
|
|
4404
4623
|
result,
|
|
4405
|
-
context,
|
|
4624
|
+
context: executionContext,
|
|
4406
4625
|
});
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4626
|
+
const resultError = getToolResultError(result);
|
|
4627
|
+
toolCall.status = resultError === undefined ? "completed" : "error";
|
|
4628
|
+
toolCall.result = result;
|
|
4629
|
+
toolCall.error = resultError;
|
|
4630
|
+
toolCall.executionTime = Date.now() - startTime;
|
|
4631
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
4632
|
+
if (resultError === undefined) {
|
|
4633
|
+
sendSSE(controller, encoder, {
|
|
4634
|
+
type: "tool-output-available",
|
|
4635
|
+
toolCallId: resumeToolCall.id,
|
|
4636
|
+
output: result,
|
|
4637
|
+
...(isDynamicTool(resumeToolCall.name, this.config.tools) ? { dynamic: true } : {}),
|
|
4638
|
+
});
|
|
4639
|
+
}
|
|
4640
|
+
else {
|
|
4641
|
+
sendSSE(controller, encoder, {
|
|
4642
|
+
type: "tool-output-error",
|
|
4643
|
+
toolCallId: resumeToolCall.id,
|
|
4644
|
+
errorText: resultError,
|
|
4645
|
+
...(isDynamicTool(resumeToolCall.name, this.config.tools) ? { dynamic: true } : {}),
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(resumeToolCall.id, resumeToolCall.name, result, resumeExecutionAuthority.toolDefinition, false, true);
|
|
4649
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
4650
|
+
await persistMessage(toolResultMessage);
|
|
4431
4651
|
}
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
id: resumeToolCall.id,
|
|
4444
|
-
name: resumeToolCall.name,
|
|
4445
|
-
arguments: inputText,
|
|
4446
|
-
inputDeltas: [inputText],
|
|
4447
|
-
inputAvailable: true,
|
|
4448
|
-
};
|
|
4449
|
-
announceStreamedToolCallInput(controller, encoder, streamedCall, (name) => isDynamicTool(name, this.config.tools));
|
|
4450
|
-
sendSSE(controller, encoder, {
|
|
4451
|
-
type: "tool-input-available",
|
|
4452
|
-
toolCallId: resumeToolCall.id,
|
|
4453
|
-
toolName: resumeToolCall.name,
|
|
4454
|
-
input: resumeToolCall.input,
|
|
4455
|
-
...(isDynamicTool(resumeToolCall.name, this.config.tools) ? { dynamic: true } : {}),
|
|
4456
|
-
});
|
|
4457
|
-
const assistantToolCallMessage = {
|
|
4458
|
-
id: generateMessageId(),
|
|
4459
|
-
role: "assistant",
|
|
4460
|
-
parts: [{
|
|
4461
|
-
type: `tool-${resumeToolCall.name}`,
|
|
4462
|
-
toolCallId: resumeToolCall.id,
|
|
4463
|
-
toolName: resumeToolCall.name,
|
|
4464
|
-
args: resumeToolCall.input,
|
|
4465
|
-
}],
|
|
4466
|
-
};
|
|
4467
|
-
let admittedTurn = snapshotAdmittedToolTurn(assistantToolCallMessage, currentMessages.length);
|
|
4468
|
-
for (let index = currentMessages.length - 1; index >= 0; index--) {
|
|
4469
|
-
const message = currentMessages[index];
|
|
4470
|
-
if (message.role !== "assistant")
|
|
4471
|
-
continue;
|
|
4472
|
-
const candidate = snapshotAdmittedToolTurn(message, index);
|
|
4473
|
-
if (somePrivateArray(candidate.calls, (call) => call.toolCallId === resumeToolCall.id && call.toolName === resumeToolCall.name &&
|
|
4474
|
-
providerValuesEqual(call.input, resumeToolCall.input, new IntrinsicWeakMap())))
|
|
4475
|
-
admittedTurn = candidate;
|
|
4476
|
-
// A newer assistant envelope is a turn boundary, never inferred replay provenance.
|
|
4477
|
-
break;
|
|
4478
|
-
}
|
|
4479
|
-
if (admittedTurn.start === currentMessages.length) {
|
|
4480
|
-
pushPrivateArray(currentMessages, assistantToolCallMessage);
|
|
4481
|
-
await persistMessage(assistantToolCallMessage);
|
|
4652
|
+
catch (error) {
|
|
4653
|
+
// A turn admission rejection belongs to the request, not the tool:
|
|
4654
|
+
// surface it as the request error instead of a tool-output-error.
|
|
4655
|
+
if (resumeAdmissionRejection?.error === error)
|
|
4656
|
+
throw error;
|
|
4657
|
+
await this.recordTerminalToolResult(error, toolCall, persistMessage, currentMessages, toolCalls, totalUsage, { controller, encoder });
|
|
4658
|
+
throwIfAborted(abortSignal);
|
|
4659
|
+
await this.recordToolError(persistMessage, toolCall, error instanceof Error ? error.message : String(error), { controller, encoder }, currentMessages, toolCalls);
|
|
4660
|
+
}
|
|
4661
|
+
if (this.#persistRecoveryBoundary)
|
|
4662
|
+
await pauseAtBoundary(step);
|
|
4482
4663
|
}
|
|
4664
|
+
// Include the parked result delivered to this request, but exclude new same-step results.
|
|
4665
|
+
const providerObservedSkillBodies = getProviderObservedSkillBodies(currentMessages);
|
|
4666
|
+
const baseSystemPrompt = currentSystemPrompt;
|
|
4667
|
+
const assembleRequest = (messages, plan = effectiveToolExposurePlan) => assembleRuntimeStepRequest({
|
|
4668
|
+
messages,
|
|
4669
|
+
plan,
|
|
4670
|
+
toolResultContext,
|
|
4671
|
+
supportsToolCalling,
|
|
4672
|
+
agentWriteFinalResponseToolGuardEnabled,
|
|
4673
|
+
providerTools,
|
|
4674
|
+
model: effectiveModel,
|
|
4675
|
+
languageModel,
|
|
4676
|
+
toolSchemaOptions: resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
4677
|
+
baseSystemPrompt,
|
|
4678
|
+
integrationToolDiscovery: preparedStep.integrationToolDiscovery,
|
|
4679
|
+
runRuntimeContext,
|
|
4680
|
+
bootstrapToolNames: preparedStep.configuredBootstrapToolNames,
|
|
4681
|
+
});
|
|
4682
|
+
const requestedOutputTokens = this.resolveMaxOutputTokens(effectiveModel, maxOutputTokensOverride);
|
|
4683
|
+
// An invalid thinking budget is left for the provider path to reject.
|
|
4684
|
+
const additionalOutputTokens = resolveAdditiveReasoningOutputTokens(languageModel, {
|
|
4685
|
+
reasoning,
|
|
4686
|
+
providerOptions,
|
|
4687
|
+
}) ?? 0;
|
|
4688
|
+
const fittedRequest = await fitRuntimeStepRequestToServedBudget({
|
|
4689
|
+
sourceMessages: currentMessages,
|
|
4690
|
+
modelMessages: toolResultContext
|
|
4691
|
+
? createModelToolResultContextMessages(currentMessages, toolResultContext)
|
|
4692
|
+
: currentMessages,
|
|
4693
|
+
plan: effectiveToolExposurePlan,
|
|
4694
|
+
assembleRequest,
|
|
4695
|
+
toolResultContext,
|
|
4696
|
+
supportsToolCalling,
|
|
4697
|
+
deferredToolLoading: toolLoadingResolution.mode === "deferred",
|
|
4698
|
+
languageModel,
|
|
4699
|
+
model: effectiveModel,
|
|
4700
|
+
reasoning,
|
|
4701
|
+
providerOptions,
|
|
4702
|
+
responseFormat: outputSchema?.responseFormat,
|
|
4703
|
+
requestedOutputTokens,
|
|
4704
|
+
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
4705
|
+
additionalOutputTokens,
|
|
4706
|
+
toolExposureState,
|
|
4707
|
+
persistToolExposureCheckpoint,
|
|
4708
|
+
requireToolExposureCheckpointPersistence,
|
|
4709
|
+
turnAdmission: validateProviderRequest,
|
|
4710
|
+
});
|
|
4711
|
+
const modelMessages = fittedRequest.modelMessages;
|
|
4712
|
+
effectiveToolExposurePlan = fittedRequest.plan;
|
|
4483
4713
|
if (this.#persistRecoveryBoundary) {
|
|
4484
4714
|
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
4485
4715
|
throwIfAborted(abortSignal);
|
|
4486
4716
|
}
|
|
4487
|
-
const
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4717
|
+
const request = assembleRequest(modelMessages);
|
|
4718
|
+
currentSystemPrompt = request.systemPrompt;
|
|
4719
|
+
const { tools, runtimeTools, providerSystemPrompt, providerMessages } = request;
|
|
4720
|
+
setOtelActiveSpanAttributes({
|
|
4721
|
+
"tool.loading.mode": resolveRuntimeToolLoading(runtimeStepConfig).mode,
|
|
4722
|
+
"tool.loading.provenance": toolLoadingResolution.provenance,
|
|
4723
|
+
"tool.catalog.authorized_count": preparedStep.toolExposurePlan.authorized.length,
|
|
4724
|
+
"tool.catalog.visible_count": tools.length,
|
|
4725
|
+
"tool.catalog.deferred_count": effectiveToolExposurePlan.deferred.length,
|
|
4726
|
+
"tool.loading.path": "framework-fallback",
|
|
4727
|
+
});
|
|
4728
|
+
const runtimeToolNames = Object.keys(runtimeTools ?? {}).sort(compareStrings);
|
|
4729
|
+
const acceptedRuntimeToolNames = collectAcceptedRuntimeToolNames(runtimeTools, effectiveToolExposurePlan);
|
|
4730
|
+
const temperature = this.resolveTemperature(temperatureModelString ?? effectiveModel, providerOptions);
|
|
4731
|
+
const genAiProviderName = resolveRuntimeGenAiProviderName(effectiveModel);
|
|
4732
|
+
// Reject an over-budget request before the turn is accepted, so a
|
|
4733
|
+
// rejected input is rolled back instead of committed to memory.
|
|
4734
|
+
const { maxOutputTokens } = enforceProviderRequestBudget({
|
|
4735
|
+
model: languageModel,
|
|
4736
|
+
...(reasoning === undefined ? {} : { reasoning }),
|
|
4737
|
+
...(providerOptions === undefined ? {} : { providerOptions }),
|
|
4738
|
+
system: providerSystemPrompt,
|
|
4739
|
+
messages: providerMessages,
|
|
4740
|
+
tools: runtimeTools,
|
|
4741
|
+
responseFormat: outputSchema?.responseFormat,
|
|
4742
|
+
limit: fittedRequest.servedLimit,
|
|
4743
|
+
requestedOutputTokens,
|
|
4744
|
+
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
4745
|
+
additionalOutputTokens,
|
|
4746
|
+
});
|
|
4747
|
+
await validateProviderRequest(providerSystemPrompt, modelMessages, { beforeAcceptance: fittedRequest.commitToolExposure });
|
|
4748
|
+
const streamLifecycleMode = resolveStreamLifecycleModeFromEnv();
|
|
4749
|
+
const streamModel = withRuntimeProviderStreamErrorProvenance(languageModel);
|
|
4750
|
+
const streamSource = createRuntimeStreamSource((streamSignal) => streamTextForObservedAgentRuntime({
|
|
4751
|
+
model: streamModel,
|
|
4752
|
+
system: providerSystemPrompt,
|
|
4753
|
+
messages: providerMessages,
|
|
4754
|
+
tools: runtimeTools,
|
|
4755
|
+
experimental_repairToolCall: repairToolCall,
|
|
4756
|
+
maxOutputTokens,
|
|
4757
|
+
...(temperature === undefined ? {} : { temperature }),
|
|
4758
|
+
...(this.config.parallelToolCalls === undefined
|
|
4759
|
+
? {}
|
|
4760
|
+
: { parallelToolCalls: this.config.parallelToolCalls }),
|
|
4761
|
+
...(headers ? { headers } : {}),
|
|
4762
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
4763
|
+
...(reasoning ? { reasoning } : {}),
|
|
4764
|
+
...(outputSchema ? { responseFormat: outputSchema.responseFormat } : {}),
|
|
4765
|
+
abortSignal: streamSignal,
|
|
4766
|
+
}));
|
|
4767
|
+
let state = createStreamState();
|
|
4768
|
+
// Hold a possible replay only while it remains a prefix of the text the
|
|
4769
|
+
// client already received. Once it diverges, resume live delivery.
|
|
4770
|
+
const deferInterruptedRecoveryOutput = step === interruptedLocalToolBatchRecoveryStep &&
|
|
4771
|
+
interruptedLocalToolBatchRecoveryText !== undefined;
|
|
4772
|
+
const deferredRecoveryOutput = deferInterruptedRecoveryOutput ? [] : undefined;
|
|
4773
|
+
const previousRecoveryText = interruptedLocalToolBatchRecoveryText ?? "";
|
|
4774
|
+
let deferredRecoverySseText = "";
|
|
4775
|
+
let deferredRecoveryCallbackText = "";
|
|
4776
|
+
let releasedDeferredRecoveryOutput = false;
|
|
4777
|
+
let releasedRecoveryReplacementTextPartId;
|
|
4778
|
+
let suppressedRecoveryReplayTextLength = 0;
|
|
4779
|
+
const stepTextPartId = textPartId === undefined || step === 0
|
|
4780
|
+
? textPartId
|
|
4781
|
+
: `${textPartId}:step:${step}`;
|
|
4782
|
+
const replacementRecoveryTextPartId = stepTextPartId === undefined
|
|
4783
|
+
? `recovery:step:${step}`
|
|
4784
|
+
: `${stepTextPartId}:recovery`;
|
|
4785
|
+
const remainingRecoveryReplayText = () => privateTextSlice(previousRecoveryText, suppressedRecoveryReplayTextLength);
|
|
4786
|
+
const flushDeferredRecoveryOutput = (interruptedRecoveryPrefixLength, repeatsInterruptedRecoveryText, useReplacementTextPartId) => {
|
|
4787
|
+
if (deferredRecoveryOutput === undefined)
|
|
4788
|
+
return;
|
|
4789
|
+
let remainingSsePrefixLength = interruptedRecoveryPrefixLength;
|
|
4790
|
+
let remainingCallbackPrefixLength = interruptedRecoveryPrefixLength;
|
|
4791
|
+
for (let outputIndex = 0; outputIndex < deferredRecoveryOutput.length; outputIndex++) {
|
|
4792
|
+
if (!ObjectHasOwn(deferredRecoveryOutput, outputIndex))
|
|
4793
|
+
continue;
|
|
4794
|
+
const output = deferredRecoveryOutput[outputIndex];
|
|
4795
|
+
if (repeatsInterruptedRecoveryText &&
|
|
4796
|
+
(output.kind === "callback" || output.isTextEvent)) {
|
|
4797
|
+
continue;
|
|
4798
|
+
}
|
|
4799
|
+
if (output.kind === "callback") {
|
|
4800
|
+
const stripped = stripLeadingText(output.chunk, remainingCallbackPrefixLength);
|
|
4801
|
+
remainingCallbackPrefixLength = stripped.remainingPrefixLength;
|
|
4802
|
+
if (stripped.text.length > 0) {
|
|
4803
|
+
callbacks?.onChunk?.(stripped.text);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
else {
|
|
4807
|
+
const textChunk = useReplacementTextPartId && output.isTextEvent
|
|
4808
|
+
? rewriteRecoveryTextSseChunkId(output.chunk, replacementRecoveryTextPartId, encoder)
|
|
4809
|
+
: output.chunk;
|
|
4810
|
+
const stripped = output.isTextEvent
|
|
4811
|
+
? stripTextDeltaPrefixFromSseChunk(textChunk, remainingSsePrefixLength, encoder)
|
|
4812
|
+
: { chunk: textChunk, remainingPrefixLength: remainingSsePrefixLength };
|
|
4813
|
+
remainingSsePrefixLength = stripped.remainingPrefixLength;
|
|
4814
|
+
if (stripped.chunk !== undefined) {
|
|
4815
|
+
enqueuePrivateStream(controller, stripped.chunk);
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
deferredRecoveryOutput.length = 0;
|
|
4492
4820
|
};
|
|
4493
|
-
const
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
if (
|
|
4508
|
-
|
|
4821
|
+
const releaseDeferredRecoveryOutputAfterDivergence = () => {
|
|
4822
|
+
if (deferredRecoveryOutput === undefined || releasedDeferredRecoveryOutput)
|
|
4823
|
+
return;
|
|
4824
|
+
const expectedReplayText = remainingRecoveryReplayText();
|
|
4825
|
+
const sseDiverged = !privateTextStartsWith(expectedReplayText, deferredRecoverySseText);
|
|
4826
|
+
const callbackDiverged = callbacks?.onChunk === undefined ||
|
|
4827
|
+
!privateTextStartsWith(expectedReplayText, deferredRecoveryCallbackText);
|
|
4828
|
+
if (!sseDiverged || !callbackDiverged)
|
|
4829
|
+
return;
|
|
4830
|
+
const observedRecoveryText = callbacks?.onChunk === undefined
|
|
4831
|
+
? deferredRecoverySseText
|
|
4832
|
+
: deferredRecoveryCallbackText;
|
|
4833
|
+
const extendsPreviousRecoveryText = privateTextStartsWith(observedRecoveryText, expectedReplayText);
|
|
4834
|
+
flushDeferredRecoveryOutput(extendsPreviousRecoveryText ? expectedReplayText.length : 0, false, !extendsPreviousRecoveryText && suppressedRecoveryReplayTextLength === 0);
|
|
4835
|
+
if (extendsPreviousRecoveryText && suppressedRecoveryReplayTextLength > 0) {
|
|
4836
|
+
suppressedRecoveryReplayTextLength += expectedReplayText.length;
|
|
4509
4837
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
4513
|
-
skillToolAvailability: skillState.activeSkillToolAvailability,
|
|
4514
|
-
toolInput: toolCall.args,
|
|
4515
|
-
toolDefinition: resumeExecutionAuthority.toolDefinition,
|
|
4516
|
-
});
|
|
4517
|
-
if (!policyCheck.allowed)
|
|
4518
|
-
throw new Error(policyCheck.error);
|
|
4519
|
-
toolCall.args = applySkillDelegationOverridesToToolInput(resumeToolCall.name, toolCall.args, skillState.activeSkillDelegationOverrides, resolveConfiguredTool(this.config.tools, resumeToolCall.name, { agentId: this.id }) ??
|
|
4520
|
-
undefined);
|
|
4521
|
-
// Resuming a trusted parked tool is a real dispatch even before the
|
|
4522
|
-
// next provider request. Validate before its effect, then retain that
|
|
4523
|
-
// accepted history if later provider preparation fails.
|
|
4524
|
-
try {
|
|
4525
|
-
await validateProviderRequest(withAgentRunRuntimeContext(currentSystemPrompt, runRuntimeContext), currentMessages);
|
|
4838
|
+
if (!extendsPreviousRecoveryText && suppressedRecoveryReplayTextLength === 0) {
|
|
4839
|
+
releasedRecoveryReplacementTextPartId = replacementRecoveryTextPartId;
|
|
4526
4840
|
}
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4841
|
+
releasedDeferredRecoveryOutput = true;
|
|
4842
|
+
};
|
|
4843
|
+
const releaseDeferredRecoveryOutputAfterExactReplay = (isTextEvent) => {
|
|
4844
|
+
if (isTextEvent || deferredRecoveryOutput === undefined || releasedDeferredRecoveryOutput ||
|
|
4845
|
+
deferredRecoverySseText !== remainingRecoveryReplayText() ||
|
|
4846
|
+
(callbacks?.onChunk !== undefined &&
|
|
4847
|
+
deferredRecoveryCallbackText !== remainingRecoveryReplayText())) {
|
|
4848
|
+
return;
|
|
4530
4849
|
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
toolCallId: resumeToolCall.id,
|
|
4538
|
-
args: toolCall.args,
|
|
4539
|
-
admittedTurn,
|
|
4540
|
-
owner: currentMessages,
|
|
4541
|
-
prepareTerminalDispatch,
|
|
4542
|
-
toolsConfig: this.config.tools,
|
|
4543
|
-
context: executionContext,
|
|
4544
|
-
allowedRemoteToolNames,
|
|
4545
|
-
remoteToolSources,
|
|
4546
|
-
sourceIntegrationPolicy,
|
|
4547
|
-
frameworkLocalTools,
|
|
4548
|
-
}));
|
|
4549
|
-
throwIfAborted(abortSignal);
|
|
4550
|
-
await this.notifyToolResult({
|
|
4551
|
-
mode: "stream",
|
|
4552
|
-
toolName: resumeToolCall.name,
|
|
4553
|
-
toolCallId: resumeToolCall.id,
|
|
4554
|
-
input: toolCall.args,
|
|
4555
|
-
result,
|
|
4556
|
-
context: executionContext,
|
|
4557
|
-
});
|
|
4558
|
-
const resultError = getToolResultError(result);
|
|
4559
|
-
toolCall.status = resultError === undefined ? "completed" : "error";
|
|
4560
|
-
toolCall.result = result;
|
|
4561
|
-
toolCall.error = resultError;
|
|
4562
|
-
toolCall.executionTime = Date.now() - startTime;
|
|
4563
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
4564
|
-
if (resultError === undefined) {
|
|
4565
|
-
sendSSE(controller, encoder, {
|
|
4566
|
-
type: "tool-output-available",
|
|
4567
|
-
toolCallId: resumeToolCall.id,
|
|
4568
|
-
output: result,
|
|
4569
|
-
...(isDynamicTool(resumeToolCall.name, this.config.tools) ? { dynamic: true } : {}),
|
|
4570
|
-
});
|
|
4850
|
+
flushDeferredRecoveryOutput(remainingRecoveryReplayText().length, false, false);
|
|
4851
|
+
releasedDeferredRecoveryOutput = true;
|
|
4852
|
+
};
|
|
4853
|
+
const releaseDeferredRecoveryNonTextOutput = (isTextEvent) => {
|
|
4854
|
+
if (isTextEvent || deferredRecoveryOutput === undefined || releasedDeferredRecoveryOutput) {
|
|
4855
|
+
return;
|
|
4571
4856
|
}
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4857
|
+
const retainedOutput = filterPrivateArray(deferredRecoveryOutput, (output) => output.kind === "callback" || output.isTextEvent);
|
|
4858
|
+
for (let outputIndex = 0; outputIndex < deferredRecoveryOutput.length; outputIndex++) {
|
|
4859
|
+
if (!ObjectHasOwn(deferredRecoveryOutput, outputIndex))
|
|
4860
|
+
continue;
|
|
4861
|
+
const output = deferredRecoveryOutput[outputIndex];
|
|
4862
|
+
if (output.kind === "sse" && !output.isTextEvent) {
|
|
4863
|
+
enqueuePrivateStream(controller, output.chunk);
|
|
4864
|
+
}
|
|
4579
4865
|
}
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
throwIfAborted(abortSignal);
|
|
4591
|
-
await this.recordToolError(persistMessage, toolCall, error instanceof Error ? error.message : String(error), { controller, encoder }, currentMessages, toolCalls);
|
|
4592
|
-
}
|
|
4593
|
-
if (this.#persistRecoveryBoundary)
|
|
4594
|
-
await pauseAtBoundary(step);
|
|
4595
|
-
}
|
|
4596
|
-
// Include the parked result delivered to this request, but exclude new same-step results.
|
|
4597
|
-
const providerObservedSkillBodies = getProviderObservedSkillBodies(currentMessages);
|
|
4598
|
-
const baseSystemPrompt = currentSystemPrompt;
|
|
4599
|
-
const assembleRequest = (messages, plan = effectiveToolExposurePlan) => assembleRuntimeStepRequest({
|
|
4600
|
-
messages,
|
|
4601
|
-
plan,
|
|
4602
|
-
toolResultContext,
|
|
4603
|
-
supportsToolCalling,
|
|
4604
|
-
agentWriteFinalResponseToolGuardEnabled,
|
|
4605
|
-
providerTools,
|
|
4606
|
-
model: effectiveModel,
|
|
4607
|
-
languageModel,
|
|
4608
|
-
toolSchemaOptions: resolveStepToolSchemaOptions(this.config, languageModel, effectiveModel),
|
|
4609
|
-
baseSystemPrompt,
|
|
4610
|
-
integrationToolDiscovery: preparedStep.integrationToolDiscovery,
|
|
4611
|
-
runRuntimeContext,
|
|
4612
|
-
bootstrapToolNames: preparedStep.configuredBootstrapToolNames,
|
|
4613
|
-
});
|
|
4614
|
-
const requestedOutputTokens = this.resolveMaxOutputTokens(effectiveModel, maxOutputTokensOverride);
|
|
4615
|
-
// An invalid thinking budget is left for the provider path to reject.
|
|
4616
|
-
const additionalOutputTokens = resolveAdditiveReasoningOutputTokens(languageModel, {
|
|
4617
|
-
reasoning,
|
|
4618
|
-
providerOptions,
|
|
4619
|
-
}) ?? 0;
|
|
4620
|
-
const fittedRequest = await fitRuntimeStepRequestToServedBudget({
|
|
4621
|
-
sourceMessages: currentMessages,
|
|
4622
|
-
modelMessages: toolResultContext
|
|
4623
|
-
? createModelToolResultContextMessages(currentMessages, toolResultContext)
|
|
4624
|
-
: currentMessages,
|
|
4625
|
-
plan: effectiveToolExposurePlan,
|
|
4626
|
-
assembleRequest,
|
|
4627
|
-
toolResultContext,
|
|
4628
|
-
supportsToolCalling,
|
|
4629
|
-
deferredToolLoading: toolLoadingResolution.mode === "deferred",
|
|
4630
|
-
languageModel,
|
|
4631
|
-
model: effectiveModel,
|
|
4632
|
-
reasoning,
|
|
4633
|
-
providerOptions,
|
|
4634
|
-
responseFormat: outputSchema?.responseFormat,
|
|
4635
|
-
requestedOutputTokens,
|
|
4636
|
-
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
4637
|
-
additionalOutputTokens,
|
|
4638
|
-
toolExposureState,
|
|
4639
|
-
persistToolExposureCheckpoint,
|
|
4640
|
-
requireToolExposureCheckpointPersistence,
|
|
4641
|
-
turnAdmission: validateProviderRequest,
|
|
4642
|
-
});
|
|
4643
|
-
const modelMessages = fittedRequest.modelMessages;
|
|
4644
|
-
effectiveToolExposurePlan = fittedRequest.plan;
|
|
4645
|
-
if (this.#persistRecoveryBoundary) {
|
|
4646
|
-
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
4647
|
-
throwIfAborted(abortSignal);
|
|
4648
|
-
}
|
|
4649
|
-
const request = assembleRequest(modelMessages);
|
|
4650
|
-
currentSystemPrompt = request.systemPrompt;
|
|
4651
|
-
const { tools, runtimeTools, providerSystemPrompt, providerMessages } = request;
|
|
4652
|
-
setOtelActiveSpanAttributes({
|
|
4653
|
-
"tool.loading.mode": resolveRuntimeToolLoading(runtimeStepConfig).mode,
|
|
4654
|
-
"tool.loading.provenance": toolLoadingResolution.provenance,
|
|
4655
|
-
"tool.catalog.authorized_count": preparedStep.toolExposurePlan.authorized.length,
|
|
4656
|
-
"tool.catalog.visible_count": tools.length,
|
|
4657
|
-
"tool.catalog.deferred_count": effectiveToolExposurePlan.deferred.length,
|
|
4658
|
-
"tool.loading.path": "framework-fallback",
|
|
4659
|
-
});
|
|
4660
|
-
const runtimeToolNames = Object.keys(runtimeTools ?? {}).sort(compareStrings);
|
|
4661
|
-
const acceptedRuntimeToolNames = collectAcceptedRuntimeToolNames(runtimeTools, effectiveToolExposurePlan);
|
|
4662
|
-
const temperature = this.resolveTemperature(temperatureModelString ?? effectiveModel, providerOptions);
|
|
4663
|
-
const genAiProviderName = resolveRuntimeGenAiProviderName(effectiveModel);
|
|
4664
|
-
// Reject an over-budget request before the turn is accepted, so a
|
|
4665
|
-
// rejected input is rolled back instead of committed to memory.
|
|
4666
|
-
const { maxOutputTokens } = enforceProviderRequestBudget({
|
|
4667
|
-
model: languageModel,
|
|
4668
|
-
...(reasoning === undefined ? {} : { reasoning }),
|
|
4669
|
-
...(providerOptions === undefined ? {} : { providerOptions }),
|
|
4670
|
-
system: providerSystemPrompt,
|
|
4671
|
-
messages: providerMessages,
|
|
4672
|
-
tools: runtimeTools,
|
|
4673
|
-
responseFormat: outputSchema?.responseFormat,
|
|
4674
|
-
limit: fittedRequest.servedLimit,
|
|
4675
|
-
requestedOutputTokens,
|
|
4676
|
-
explicitOutputTokens: isMaxOutputTokensOverride(maxOutputTokensOverride),
|
|
4677
|
-
additionalOutputTokens,
|
|
4678
|
-
});
|
|
4679
|
-
await validateProviderRequest(providerSystemPrompt, modelMessages, { beforeAcceptance: fittedRequest.commitToolExposure });
|
|
4680
|
-
const streamLifecycleMode = resolveStreamLifecycleModeFromEnv();
|
|
4681
|
-
const streamModel = withRuntimeProviderStreamErrorProvenance(languageModel);
|
|
4682
|
-
const streamSource = createRuntimeStreamSource((streamSignal) => streamTextForObservedAgentRuntime({
|
|
4683
|
-
model: streamModel,
|
|
4684
|
-
system: providerSystemPrompt,
|
|
4685
|
-
messages: providerMessages,
|
|
4686
|
-
tools: runtimeTools,
|
|
4687
|
-
experimental_repairToolCall: repairToolCall,
|
|
4688
|
-
maxOutputTokens,
|
|
4689
|
-
...(temperature === undefined ? {} : { temperature }),
|
|
4690
|
-
...(this.config.parallelToolCalls === undefined
|
|
4691
|
-
? {}
|
|
4692
|
-
: { parallelToolCalls: this.config.parallelToolCalls }),
|
|
4693
|
-
...(headers ? { headers } : {}),
|
|
4694
|
-
...(providerOptions ? { providerOptions } : {}),
|
|
4695
|
-
...(reasoning ? { reasoning } : {}),
|
|
4696
|
-
...(outputSchema ? { responseFormat: outputSchema.responseFormat } : {}),
|
|
4697
|
-
abortSignal: streamSignal,
|
|
4698
|
-
}));
|
|
4699
|
-
let state = createStreamState();
|
|
4700
|
-
// Hold a possible replay only while it remains a prefix of the text the
|
|
4701
|
-
// client already received. Once it diverges, resume live delivery.
|
|
4702
|
-
const deferInterruptedRecoveryOutput = step === interruptedLocalToolBatchRecoveryStep &&
|
|
4703
|
-
interruptedLocalToolBatchRecoveryText !== undefined;
|
|
4704
|
-
const deferredRecoveryOutput = deferInterruptedRecoveryOutput ? [] : undefined;
|
|
4705
|
-
const previousRecoveryText = interruptedLocalToolBatchRecoveryText ?? "";
|
|
4706
|
-
let deferredRecoverySseText = "";
|
|
4707
|
-
let deferredRecoveryCallbackText = "";
|
|
4708
|
-
let releasedDeferredRecoveryOutput = false;
|
|
4709
|
-
let releasedRecoveryReplacementTextPartId;
|
|
4710
|
-
let suppressedRecoveryReplayTextLength = 0;
|
|
4711
|
-
const stepTextPartId = textPartId === undefined || step === 0
|
|
4712
|
-
? textPartId
|
|
4713
|
-
: `${textPartId}:step:${step}`;
|
|
4714
|
-
const replacementRecoveryTextPartId = stepTextPartId === undefined
|
|
4715
|
-
? `recovery:step:${step}`
|
|
4716
|
-
: `${stepTextPartId}:recovery`;
|
|
4717
|
-
const remainingRecoveryReplayText = () => privateTextSlice(previousRecoveryText, suppressedRecoveryReplayTextLength);
|
|
4718
|
-
const flushDeferredRecoveryOutput = (interruptedRecoveryPrefixLength, repeatsInterruptedRecoveryText, useReplacementTextPartId) => {
|
|
4719
|
-
if (deferredRecoveryOutput === undefined)
|
|
4720
|
-
return;
|
|
4721
|
-
let remainingSsePrefixLength = interruptedRecoveryPrefixLength;
|
|
4722
|
-
let remainingCallbackPrefixLength = interruptedRecoveryPrefixLength;
|
|
4723
|
-
for (let outputIndex = 0; outputIndex < deferredRecoveryOutput.length; outputIndex++) {
|
|
4724
|
-
if (!ObjectHasOwn(deferredRecoveryOutput, outputIndex))
|
|
4725
|
-
continue;
|
|
4726
|
-
const output = deferredRecoveryOutput[outputIndex];
|
|
4727
|
-
if (repeatsInterruptedRecoveryText &&
|
|
4728
|
-
(output.kind === "callback" || output.isTextEvent)) {
|
|
4729
|
-
continue;
|
|
4866
|
+
deferredRecoveryOutput.length = 0;
|
|
4867
|
+
appendPrivateArray(deferredRecoveryOutput, retainedOutput);
|
|
4868
|
+
};
|
|
4869
|
+
const reconcileDeferredRecoveryTextSegment = (isTextEndEvent) => {
|
|
4870
|
+
if (!isTextEndEvent || deferredRecoveryOutput === undefined ||
|
|
4871
|
+
releasedDeferredRecoveryOutput ||
|
|
4872
|
+
!privateTextStartsWith(remainingRecoveryReplayText(), deferredRecoverySseText) ||
|
|
4873
|
+
(callbacks?.onChunk !== undefined &&
|
|
4874
|
+
!privateTextStartsWith(remainingRecoveryReplayText(), deferredRecoveryCallbackText))) {
|
|
4875
|
+
return;
|
|
4730
4876
|
}
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4877
|
+
// A tool or reasoning event closes the current text segment. If that
|
|
4878
|
+
// segment only replayed a prefix the client already received, discard
|
|
4879
|
+
// it now and treat later text as a distinct segment. Otherwise retained
|
|
4880
|
+
// prefix events could be released after the boundary when later text
|
|
4881
|
+
// diverges, duplicating and reordering the replay.
|
|
4882
|
+
suppressedRecoveryReplayTextLength += deferredRecoverySseText.length;
|
|
4883
|
+
deferredRecoveryOutput.length = 0;
|
|
4884
|
+
deferredRecoverySseText = "";
|
|
4885
|
+
deferredRecoveryCallbackText = "";
|
|
4886
|
+
};
|
|
4887
|
+
let sawProviderStreamEvent = false;
|
|
4888
|
+
const enqueueStepChunk = (chunk) => {
|
|
4889
|
+
sawProviderStreamEvent = true;
|
|
4890
|
+
enqueuePrivateStream(controller, chunk);
|
|
4891
|
+
};
|
|
4892
|
+
const stepController = deferredRecoveryOutput === undefined
|
|
4893
|
+
? {
|
|
4894
|
+
enqueue: enqueueStepChunk,
|
|
4895
|
+
}
|
|
4896
|
+
: {
|
|
4897
|
+
enqueue(chunk) {
|
|
4898
|
+
sawProviderStreamEvent = true;
|
|
4899
|
+
if (releasedDeferredRecoveryOutput) {
|
|
4900
|
+
enqueuePrivateStream(controller, releasedRecoveryReplacementTextPartId !== undefined
|
|
4901
|
+
? rewriteRecoveryTextSseChunkId(chunk, releasedRecoveryReplacementTextPartId, encoder)
|
|
4902
|
+
: chunk);
|
|
4903
|
+
return;
|
|
4904
|
+
}
|
|
4905
|
+
deferredRecoverySseText += textDeltaFromSseChunk(chunk) ?? "";
|
|
4906
|
+
const isTextEvent = isTextSseChunk(chunk);
|
|
4907
|
+
pushPrivateArray(deferredRecoveryOutput, {
|
|
4908
|
+
kind: "sse",
|
|
4909
|
+
chunk,
|
|
4910
|
+
isTextEvent,
|
|
4911
|
+
});
|
|
4912
|
+
releaseDeferredRecoveryOutputAfterDivergence();
|
|
4913
|
+
releaseDeferredRecoveryOutputAfterExactReplay(isTextEvent);
|
|
4914
|
+
releaseDeferredRecoveryNonTextOutput(isTextEvent);
|
|
4915
|
+
reconcileDeferredRecoveryTextSegment(isTextEndSseChunk(chunk));
|
|
4916
|
+
},
|
|
4917
|
+
};
|
|
4918
|
+
bindRuntimeStreamForwardingController(stepController, controller);
|
|
4919
|
+
let providerStreamRetryAttempts = 0;
|
|
4920
|
+
let sawProviderStreamUsage = false;
|
|
4921
|
+
while (true) {
|
|
4922
|
+
try {
|
|
4923
|
+
await processStream(streamSource, state, stepController, encoder, stepTextPartId, {
|
|
4924
|
+
onChunk: deferredRecoveryOutput === undefined ? callbacks?.onChunk : (chunk) => {
|
|
4925
|
+
if (releasedDeferredRecoveryOutput) {
|
|
4926
|
+
callbacks?.onChunk?.(chunk);
|
|
4927
|
+
return;
|
|
4928
|
+
}
|
|
4929
|
+
deferredRecoveryCallbackText += chunk;
|
|
4930
|
+
if (callbacks?.onChunk !== undefined) {
|
|
4931
|
+
pushPrivateArray(deferredRecoveryOutput, { kind: "callback", chunk });
|
|
4932
|
+
}
|
|
4933
|
+
releaseDeferredRecoveryOutputAfterDivergence();
|
|
4934
|
+
},
|
|
4935
|
+
...(runtimeObservationStepId !== undefined &&
|
|
4936
|
+
runtimeObservationMessageSpanId !== undefined
|
|
4937
|
+
? {
|
|
4938
|
+
runtimeObservationStepId,
|
|
4939
|
+
runtimeObservationMessageSpanId,
|
|
4940
|
+
}
|
|
4941
|
+
: {}),
|
|
4942
|
+
onUsage: (usage) => {
|
|
4943
|
+
sawProviderStreamUsage = true;
|
|
4944
|
+
accumulateUsage(totalUsage, usage);
|
|
4945
|
+
// Snapshot, not the live object: a later step must not mutate a total
|
|
4946
|
+
// a caller has already recorded on a span.
|
|
4947
|
+
callbacks?.onUsage?.({ ...totalUsage });
|
|
4948
|
+
},
|
|
4949
|
+
streamRequiresFinish: languageModel.runtimeCapabilities?.streamRequiresFinish === true,
|
|
4950
|
+
requireProviderFinish: languageModel.runtimeCapabilities?.toolCallStreamRequiresFinish === true,
|
|
4951
|
+
providerExecutedToolNames: getProviderExecutedToolNames(runtimeTools),
|
|
4952
|
+
availableToolNames: acceptedRuntimeToolNames,
|
|
4953
|
+
dynamicToolNames: runtimeToolNames.filter((name) => isDynamicTool(name, this.config.tools)),
|
|
4954
|
+
streamLifecycleMode,
|
|
4955
|
+
...(this.#modelStreamTimeouts
|
|
4956
|
+
? { streamLifecyclePolicy: { ...this.#modelStreamTimeouts } }
|
|
4957
|
+
: {}),
|
|
4958
|
+
traceSpanName: `chat ${effectiveModel}`,
|
|
4959
|
+
traceAttributes: {
|
|
4960
|
+
...(genAiProviderName ? { "gen_ai.provider.name": genAiProviderName } : {}),
|
|
4961
|
+
"gen_ai.request.model": effectiveModel,
|
|
4962
|
+
"gen_ai.response.model": effectiveModel,
|
|
4963
|
+
"gen_ai.request.max_tokens": maxOutputTokens,
|
|
4964
|
+
"gen_ai.output.type": "text",
|
|
4965
|
+
...(temperature === undefined ? {} : { "gen_ai.request.temperature": temperature }),
|
|
4966
|
+
},
|
|
4967
|
+
}, abortSignal);
|
|
4968
|
+
break;
|
|
4737
4969
|
}
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
:
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4970
|
+
catch (error) {
|
|
4971
|
+
if (!shouldRetryRuntimeProviderStreamFailure({
|
|
4972
|
+
error,
|
|
4973
|
+
state,
|
|
4974
|
+
attempts: providerStreamRetryAttempts,
|
|
4975
|
+
abortSignal,
|
|
4976
|
+
hasUsageProgress: sawProviderStreamUsage,
|
|
4977
|
+
hasEmittedStreamEvent: sawProviderStreamEvent,
|
|
4978
|
+
})) {
|
|
4979
|
+
throw error;
|
|
4748
4980
|
}
|
|
4981
|
+
providerStreamRetryAttempts += 1;
|
|
4982
|
+
state = createStreamState();
|
|
4749
4983
|
}
|
|
4750
4984
|
}
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
const
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
suppressedRecoveryReplayTextLength += expectedReplayText.length;
|
|
4769
|
-
}
|
|
4770
|
-
if (!extendsPreviousRecoveryText && suppressedRecoveryReplayTextLength === 0) {
|
|
4771
|
-
releasedRecoveryReplacementTextPartId = replacementRecoveryTextPartId;
|
|
4985
|
+
throwIfAborted(abortSignal);
|
|
4986
|
+
const interruptedRecoveryPrefixLength = deferredRecoveryOutput === undefined
|
|
4987
|
+
? 0
|
|
4988
|
+
: privateTextStartsWith(state.accumulatedText, previousRecoveryText)
|
|
4989
|
+
? previousRecoveryText.length
|
|
4990
|
+
: privateTextStartsWith(previousRecoveryText, state.accumulatedText)
|
|
4991
|
+
? state.accumulatedText.length
|
|
4992
|
+
: 0;
|
|
4993
|
+
const recoveryPresentationPrefixLength = suppressedRecoveryReplayTextLength > 0
|
|
4994
|
+
? suppressedRecoveryReplayTextLength
|
|
4995
|
+
: interruptedRecoveryPrefixLength;
|
|
4996
|
+
const recoveryPresentationText = privateTextSlice(state.accumulatedText, recoveryPresentationPrefixLength);
|
|
4997
|
+
const repeatsInterruptedRecoveryText = interruptedRecoveryPrefixLength > 0 &&
|
|
4998
|
+
recoveryPresentationText.length === 0;
|
|
4999
|
+
if (deferredRecoveryOutput !== undefined && !releasedDeferredRecoveryOutput) {
|
|
5000
|
+
flushDeferredRecoveryOutput(interruptedRecoveryPrefixLength, repeatsInterruptedRecoveryText, previousRecoveryText.length > 0 && interruptedRecoveryPrefixLength === 0 &&
|
|
5001
|
+
!repeatsInterruptedRecoveryText && state.accumulatedText.length > 0);
|
|
4772
5002
|
}
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
deferredRecoverySseText !== remainingRecoveryReplayText() ||
|
|
4778
|
-
(callbacks?.onChunk !== undefined &&
|
|
4779
|
-
deferredRecoveryCallbackText !== remainingRecoveryReplayText())) {
|
|
4780
|
-
return;
|
|
5003
|
+
finalFinishReason = state.finishReason ?? finalFinishReason;
|
|
5004
|
+
const streamedToolCalls = [];
|
|
5005
|
+
for (const toolCall of state.toolCalls.values()) {
|
|
5006
|
+
pushPrivateArray(streamedToolCalls, toolCall);
|
|
4781
5007
|
}
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
5008
|
+
const finalToolResults = collectFinalStreamToolResults(state);
|
|
5009
|
+
// Recovery replays the whole step, so it also re-emits this step's
|
|
5010
|
+
// reasoning — duplicating it in the live stream and in history, with a
|
|
5011
|
+
// signature that no longer matches the replayed content. Reasoning that
|
|
5012
|
+
// was persisted is reasoning the client already saw, so fail closed.
|
|
5013
|
+
// This is a stopgap: reasoning is default-on across the hosted catalog,
|
|
5014
|
+
// which makes recovery inert on most hosted paths. See #3736 for the
|
|
5015
|
+
// reconciliation protocol that would let it run again.
|
|
5016
|
+
const persistedReasoningParts = filterPrivateArray(state.reasoningParts, isPersistedReasoningPart);
|
|
5017
|
+
const hasExposedReasoning = persistedReasoningParts.length > 0;
|
|
5018
|
+
const canRecoverInterruptedLocalToolBatch = !recoveredInterruptedLocalToolBatch &&
|
|
5019
|
+
step + 1 < maxSteps &&
|
|
5020
|
+
!hasExposedReasoning;
|
|
5021
|
+
const shouldContinue = shouldContinueAfterStreamStep(state, {
|
|
5022
|
+
recoverInterruptedToolCalls: canRecoverInterruptedLocalToolBatch,
|
|
5023
|
+
});
|
|
5024
|
+
const stoppedEmptyAfterCompletedTool = state.finishReason === "stop" &&
|
|
5025
|
+
!hasSubstantiveAssistantText(state.accumulatedText) &&
|
|
5026
|
+
state.toolCalls.size === 0 &&
|
|
5027
|
+
finalToolResults.size === 0 &&
|
|
5028
|
+
(state.suppressedToolCalls?.length ?? 0) === 0 &&
|
|
5029
|
+
somePrivateArray(toolCalls, (toolCall) => toolCall.status === "completed");
|
|
5030
|
+
const shouldRecoverInterruptedLocalToolBatch = canRecoverInterruptedLocalToolBatch &&
|
|
5031
|
+
shouldContinue &&
|
|
5032
|
+
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall);
|
|
5033
|
+
const exhaustedStepBudgetDuringInterruptedLocalToolRecovery = !recoveredInterruptedLocalToolBatch &&
|
|
5034
|
+
step + 1 >= maxSteps &&
|
|
5035
|
+
!hasExposedReasoning &&
|
|
5036
|
+
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall) &&
|
|
5037
|
+
shouldContinueAfterStreamStep(state, { recoverInterruptedToolCalls: true });
|
|
5038
|
+
// Exactly `shouldRecoverInterruptedLocalToolBatch` with the reasoning
|
|
5039
|
+
// gate lifted: the batch this step would have replayed had it not
|
|
5040
|
+
// already exposed reasoning. Re-asking is what separates "recovery was
|
|
5041
|
+
// declined" from "this step merely carried reasoning";
|
|
5042
|
+
// `shouldContinueAfterStreamStep` only reads state, so asking twice has
|
|
5043
|
+
// no side effects, and the cheap conditions short-circuit ahead of it.
|
|
5044
|
+
const declinedRecoveryForExposedReasoning = hasExposedReasoning &&
|
|
5045
|
+
!recoveredInterruptedLocalToolBatch &&
|
|
5046
|
+
step + 1 < maxSteps &&
|
|
5047
|
+
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall) &&
|
|
5048
|
+
shouldContinueAfterStreamStep(state, { recoverInterruptedToolCalls: true });
|
|
5049
|
+
if (declinedRecoveryForExposedReasoning) {
|
|
5050
|
+
logger.warn("Declined interrupted local tool batch recovery after exposed reasoning", {
|
|
5051
|
+
step,
|
|
5052
|
+
toolName: filterPrivateArray(streamedToolCalls, isInterruptedClientToolCall)[0]?.name,
|
|
5053
|
+
reasoningPartCount: persistedReasoningParts.length,
|
|
5054
|
+
});
|
|
4788
5055
|
}
|
|
4789
|
-
const
|
|
4790
|
-
|
|
4791
|
-
|
|
5056
|
+
const assistantMessage = buildStreamedAssistantMessage({
|
|
5057
|
+
...state,
|
|
5058
|
+
accumulatedText: recoveryPresentationText,
|
|
5059
|
+
}, {
|
|
5060
|
+
id: `msg_${Date.now()}_${step}`,
|
|
5061
|
+
timestamp: Date.now(),
|
|
5062
|
+
}, {
|
|
5063
|
+
preserveRecoverablePlaceholderToolCalls: shouldRecoverInterruptedLocalToolBatch ||
|
|
5064
|
+
!shouldContinue,
|
|
5065
|
+
});
|
|
5066
|
+
attachProviderMetadata(assistantMessage, await reconcileSuppressedProviderMetadata(languageModel, state.providerMetadata, state.suppressedToolCalls, state.toolCalls.size > 0, abortSignal));
|
|
5067
|
+
for (const tc of state.toolCalls.values()) {
|
|
5068
|
+
const materialized = materializeStreamedToolCall(tc);
|
|
5069
|
+
if (materialized.kind === "incomplete" && isRecoverablePlaceholderToolCall(tc)) {
|
|
5070
|
+
// Provisional empty-object placeholder that never finalized. The
|
|
5071
|
+
// model never committed arguments. Preserve it when recovery or
|
|
5072
|
+
// terminalization records a matching tool result; otherwise the
|
|
5073
|
+
// assistant message builder can omit it beside final text. Surface no
|
|
5074
|
+
// input warning or error for the provisional fragment.
|
|
4792
5075
|
continue;
|
|
4793
|
-
|
|
4794
|
-
if (
|
|
4795
|
-
|
|
5076
|
+
}
|
|
5077
|
+
if (materialized.kind === "incomplete") {
|
|
5078
|
+
// Stream terminated before the provider emitted the finalizing
|
|
5079
|
+
// `tool-call` event for this block. The model never committed this
|
|
5080
|
+
// tool use. Surface the failure via SSE so the live client can
|
|
5081
|
+
// react, and leave the partial fragment under `inputText` in the
|
|
5082
|
+
// persisted part above so the history is replayable and transparent.
|
|
5083
|
+
logger.warn("Streamed tool call terminated before tool-call event", {
|
|
5084
|
+
toolCallId: tc.id,
|
|
5085
|
+
toolName: tc.name,
|
|
5086
|
+
partialArgumentsLength: materialized.partialArgumentsLength,
|
|
5087
|
+
partialArgumentsPreview: materialized.partialArgumentsPreview,
|
|
5088
|
+
});
|
|
5089
|
+
if (tc.inputAnnounced === true) {
|
|
5090
|
+
const dynamicIncomplete = isDynamicTool(tc.name, this.config.tools);
|
|
5091
|
+
sendSSE(controller, encoder, {
|
|
5092
|
+
type: "tool-input-error",
|
|
5093
|
+
toolCallId: tc.id,
|
|
5094
|
+
errorText: `Stream terminated before tool-call event fired for "${tc.name}". ` +
|
|
5095
|
+
`Received ${materialized.partialArgumentsLength} chars of partial tool-input deltas.`,
|
|
5096
|
+
...(dynamicIncomplete ? { dynamic: true } : {}),
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
else if (materialized.kind === "parse-error") {
|
|
5101
|
+
logger.warn("Failed to parse streamed tool arguments", {
|
|
5102
|
+
toolCallId: tc.id,
|
|
5103
|
+
error: materialized.parseError,
|
|
5104
|
+
});
|
|
4796
5105
|
}
|
|
4797
5106
|
}
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
if (!isTextEndEvent || deferredRecoveryOutput === undefined ||
|
|
4803
|
-
releasedDeferredRecoveryOutput ||
|
|
4804
|
-
!privateTextStartsWith(remainingRecoveryReplayText(), deferredRecoverySseText) ||
|
|
4805
|
-
(callbacks?.onChunk !== undefined &&
|
|
4806
|
-
!privateTextStartsWith(remainingRecoveryReplayText(), deferredRecoveryCallbackText))) {
|
|
4807
|
-
return;
|
|
5107
|
+
const stepAssistantText = getTextFromParts(assistantMessage.parts);
|
|
5108
|
+
if (step === interruptedLocalToolBatchRecoveryStep &&
|
|
5109
|
+
suppressedRecoveryReplayTextLength > 0) {
|
|
5110
|
+
latestAssistantText = `${previousRecoveryText}${recoveryPresentationText}`;
|
|
4808
5111
|
}
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
// diverges, duplicating and reordering the replay.
|
|
4814
|
-
suppressedRecoveryReplayTextLength += deferredRecoverySseText.length;
|
|
4815
|
-
deferredRecoveryOutput.length = 0;
|
|
4816
|
-
deferredRecoverySseText = "";
|
|
4817
|
-
deferredRecoveryCallbackText = "";
|
|
4818
|
-
};
|
|
4819
|
-
let sawProviderStreamEvent = false;
|
|
4820
|
-
const enqueueStepChunk = (chunk) => {
|
|
4821
|
-
sawProviderStreamEvent = true;
|
|
4822
|
-
enqueuePrivateStream(controller, chunk);
|
|
4823
|
-
};
|
|
4824
|
-
const stepController = deferredRecoveryOutput === undefined
|
|
4825
|
-
? {
|
|
4826
|
-
enqueue: enqueueStepChunk,
|
|
5112
|
+
else if (step === interruptedLocalToolBatchRecoveryStep && interruptedRecoveryPrefixLength > 0) {
|
|
5113
|
+
latestAssistantText = privateTextStartsWith(previousRecoveryText, state.accumulatedText)
|
|
5114
|
+
? previousRecoveryText
|
|
5115
|
+
: state.accumulatedText;
|
|
4827
5116
|
}
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
5117
|
+
else if (hasSubstantiveAssistantText(stepAssistantText) ||
|
|
5118
|
+
step !== interruptedLocalToolBatchRecoveryStep) {
|
|
5119
|
+
latestAssistantText = stepAssistantText;
|
|
5120
|
+
}
|
|
5121
|
+
const streamedInvokeAgentBatch = providerReplayCheckpointEmission.complete
|
|
5122
|
+
? collectStreamedParallelInvokeAgentToolCalls(streamedToolCalls, finalToolResults, providerReplayCheckpointEmission.invokeAgentToolNames, shouldContinue, effectiveToolExposurePlan, {
|
|
5123
|
+
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
5124
|
+
toolsConfig: this.config.tools,
|
|
5125
|
+
agentId: this.id,
|
|
5126
|
+
hasToolReplacements: false,
|
|
5127
|
+
})
|
|
5128
|
+
: undefined;
|
|
5129
|
+
const streamedSkillDelegationOrder = streamedInvokeAgentBatch
|
|
5130
|
+
? streamedSameTurnSkillDelegationOrder(streamedToolCalls, finalToolResults, providerReplayCheckpointEmission.invokeAgentToolNames)
|
|
5131
|
+
: undefined;
|
|
5132
|
+
let streamedBatchCompletionDeferred = streamedSkillDelegationOrder === "prefix";
|
|
5133
|
+
const admittedTurn = snapshotAdmittedToolTurn(assistantMessage, currentMessages.length);
|
|
5134
|
+
pushPrivateArray(currentMessages, assistantMessage);
|
|
5135
|
+
await persistMessage(assistantMessage);
|
|
5136
|
+
await persistProviderReplayCheckpointAfterTurn({
|
|
5137
|
+
emission: providerReplayCheckpointEmission,
|
|
5138
|
+
providerMetadata: readAttachedProviderMetadata(assistantMessage),
|
|
5139
|
+
invokeAgentToolCalls: streamedSkillDelegationOrder === "interleaved"
|
|
5140
|
+
? undefined
|
|
5141
|
+
: streamedInvokeAgentBatch,
|
|
5142
|
+
deferCompletion: streamedBatchCompletionDeferred,
|
|
5143
|
+
});
|
|
5144
|
+
if (this.#persistRecoveryBoundary) {
|
|
5145
|
+
// Recovery snapshots require an authenticated admission for every local
|
|
5146
|
+
// call in this turn, including calls interrupted before input finalized.
|
|
5147
|
+
// Announce them before waiting for persistence so both retry and terminal
|
|
5148
|
+
// paths can acknowledge the matching error without an admission cycle.
|
|
5149
|
+
for (const toolCall of streamedToolCalls) {
|
|
5150
|
+
if (toolCall.providerExecuted !== true && isStreamedToolCallIncomplete(toolCall) &&
|
|
5151
|
+
!finalToolResults.has(toolCall.id)) {
|
|
5152
|
+
announceStreamedToolCallInput(controller, encoder, toolCall, (name) => isDynamicTool(name, this.config.tools));
|
|
4836
5153
|
}
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
releaseDeferredRecoveryOutputAfterDivergence();
|
|
4845
|
-
releaseDeferredRecoveryOutputAfterExactReplay(isTextEvent);
|
|
4846
|
-
releaseDeferredRecoveryNonTextOutput(isTextEvent);
|
|
4847
|
-
reconcileDeferredRecoveryTextSegment(isTextEndSseChunk(chunk));
|
|
4848
|
-
},
|
|
4849
|
-
};
|
|
4850
|
-
bindRuntimeStreamForwardingController(stepController, controller);
|
|
4851
|
-
let providerStreamRetryAttempts = 0;
|
|
4852
|
-
let sawProviderStreamUsage = false;
|
|
4853
|
-
while (true) {
|
|
4854
|
-
try {
|
|
4855
|
-
await processStream(streamSource, state, stepController, encoder, stepTextPartId, {
|
|
4856
|
-
onChunk: deferredRecoveryOutput === undefined ? callbacks?.onChunk : (chunk) => {
|
|
4857
|
-
if (releasedDeferredRecoveryOutput) {
|
|
4858
|
-
callbacks?.onChunk?.(chunk);
|
|
4859
|
-
return;
|
|
4860
|
-
}
|
|
4861
|
-
deferredRecoveryCallbackText += chunk;
|
|
4862
|
-
if (callbacks?.onChunk !== undefined) {
|
|
4863
|
-
pushPrivateArray(deferredRecoveryOutput, { kind: "callback", chunk });
|
|
4864
|
-
}
|
|
4865
|
-
releaseDeferredRecoveryOutputAfterDivergence();
|
|
4866
|
-
},
|
|
4867
|
-
...(runtimeObservationStepId !== undefined &&
|
|
4868
|
-
runtimeObservationMessageSpanId !== undefined
|
|
5154
|
+
}
|
|
5155
|
+
await this.#persistRecoveryBoundary(createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)));
|
|
5156
|
+
}
|
|
5157
|
+
if (stoppedEmptyAfterCompletedTool) {
|
|
5158
|
+
sendSSE(controller, encoder, {
|
|
5159
|
+
type: "step-end",
|
|
5160
|
+
...(runtimeObservationStepId
|
|
4869
5161
|
? {
|
|
4870
|
-
|
|
4871
|
-
|
|
5162
|
+
privateRuntimeObservation: {
|
|
5163
|
+
version: 1,
|
|
5164
|
+
kind: "step_ended",
|
|
5165
|
+
stepId: runtimeObservationStepId,
|
|
5166
|
+
},
|
|
4872
5167
|
}
|
|
4873
5168
|
: {}),
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
// Snapshot, not the live object: a later step must not mutate a total
|
|
4878
|
-
// a caller has already recorded on a span.
|
|
4879
|
-
callbacks?.onUsage?.({ ...totalUsage });
|
|
4880
|
-
},
|
|
4881
|
-
streamRequiresFinish: languageModel.runtimeCapabilities?.streamRequiresFinish === true,
|
|
4882
|
-
requireProviderFinish: languageModel.runtimeCapabilities?.toolCallStreamRequiresFinish === true,
|
|
4883
|
-
providerExecutedToolNames: getProviderExecutedToolNames(runtimeTools),
|
|
4884
|
-
availableToolNames: acceptedRuntimeToolNames,
|
|
4885
|
-
dynamicToolNames: runtimeToolNames.filter((name) => isDynamicTool(name, this.config.tools)),
|
|
4886
|
-
streamLifecycleMode,
|
|
4887
|
-
...(this.#modelStreamTimeouts
|
|
4888
|
-
? { streamLifecyclePolicy: { ...this.#modelStreamTimeouts } }
|
|
4889
|
-
: {}),
|
|
4890
|
-
traceSpanName: `chat ${effectiveModel}`,
|
|
4891
|
-
traceAttributes: {
|
|
4892
|
-
...(genAiProviderName ? { "gen_ai.provider.name": genAiProviderName } : {}),
|
|
4893
|
-
"gen_ai.request.model": effectiveModel,
|
|
4894
|
-
"gen_ai.response.model": effectiveModel,
|
|
4895
|
-
"gen_ai.request.max_tokens": maxOutputTokens,
|
|
4896
|
-
"gen_ai.output.type": "text",
|
|
4897
|
-
...(temperature === undefined ? {} : { "gen_ai.request.temperature": temperature }),
|
|
4898
|
-
},
|
|
4899
|
-
}, abortSignal);
|
|
4900
|
-
break;
|
|
4901
|
-
}
|
|
4902
|
-
catch (error) {
|
|
4903
|
-
if (!shouldRetryRuntimeProviderStreamFailure({
|
|
4904
|
-
error,
|
|
4905
|
-
state,
|
|
4906
|
-
attempts: providerStreamRetryAttempts,
|
|
4907
|
-
abortSignal,
|
|
4908
|
-
hasUsageProgress: sawProviderStreamUsage,
|
|
4909
|
-
hasEmittedStreamEvent: sawProviderStreamEvent,
|
|
4910
|
-
})) {
|
|
4911
|
-
throw error;
|
|
5169
|
+
});
|
|
5170
|
+
if (recoveredEmptyResponse || step + 1 >= maxSteps) {
|
|
5171
|
+
throw new RuntimeEmptyResponseError();
|
|
4912
5172
|
}
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
const recoveryPresentationPrefixLength = suppressedRecoveryReplayTextLength > 0
|
|
4926
|
-
? suppressedRecoveryReplayTextLength
|
|
4927
|
-
: interruptedRecoveryPrefixLength;
|
|
4928
|
-
const recoveryPresentationText = privateTextSlice(state.accumulatedText, recoveryPresentationPrefixLength);
|
|
4929
|
-
const repeatsInterruptedRecoveryText = interruptedRecoveryPrefixLength > 0 &&
|
|
4930
|
-
recoveryPresentationText.length === 0;
|
|
4931
|
-
if (deferredRecoveryOutput !== undefined && !releasedDeferredRecoveryOutput) {
|
|
4932
|
-
flushDeferredRecoveryOutput(interruptedRecoveryPrefixLength, repeatsInterruptedRecoveryText, previousRecoveryText.length > 0 && interruptedRecoveryPrefixLength === 0 &&
|
|
4933
|
-
!repeatsInterruptedRecoveryText && state.accumulatedText.length > 0);
|
|
4934
|
-
}
|
|
4935
|
-
finalFinishReason = state.finishReason ?? finalFinishReason;
|
|
4936
|
-
const streamedToolCalls = [];
|
|
4937
|
-
for (const toolCall of state.toolCalls.values()) {
|
|
4938
|
-
pushPrivateArray(streamedToolCalls, toolCall);
|
|
4939
|
-
}
|
|
4940
|
-
const finalToolResults = collectFinalStreamToolResults(state);
|
|
4941
|
-
// Recovery replays the whole step, so it also re-emits this step's
|
|
4942
|
-
// reasoning — duplicating it in the live stream and in history, with a
|
|
4943
|
-
// signature that no longer matches the replayed content. Reasoning that
|
|
4944
|
-
// was persisted is reasoning the client already saw, so fail closed.
|
|
4945
|
-
// This is a stopgap: reasoning is default-on across the hosted catalog,
|
|
4946
|
-
// which makes recovery inert on most hosted paths. See #3736 for the
|
|
4947
|
-
// reconciliation protocol that would let it run again.
|
|
4948
|
-
const persistedReasoningParts = filterPrivateArray(state.reasoningParts, isPersistedReasoningPart);
|
|
4949
|
-
const hasExposedReasoning = persistedReasoningParts.length > 0;
|
|
4950
|
-
const canRecoverInterruptedLocalToolBatch = !recoveredInterruptedLocalToolBatch &&
|
|
4951
|
-
step + 1 < maxSteps &&
|
|
4952
|
-
!hasExposedReasoning;
|
|
4953
|
-
const shouldContinue = shouldContinueAfterStreamStep(state, {
|
|
4954
|
-
recoverInterruptedToolCalls: canRecoverInterruptedLocalToolBatch,
|
|
4955
|
-
});
|
|
4956
|
-
const stoppedEmptyAfterCompletedTool = state.finishReason === "stop" &&
|
|
4957
|
-
!hasSubstantiveAssistantText(state.accumulatedText) &&
|
|
4958
|
-
state.toolCalls.size === 0 &&
|
|
4959
|
-
finalToolResults.size === 0 &&
|
|
4960
|
-
(state.suppressedToolCalls?.length ?? 0) === 0 &&
|
|
4961
|
-
somePrivateArray(toolCalls, (toolCall) => toolCall.status === "completed");
|
|
4962
|
-
const shouldRecoverInterruptedLocalToolBatch = canRecoverInterruptedLocalToolBatch &&
|
|
4963
|
-
shouldContinue &&
|
|
4964
|
-
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall);
|
|
4965
|
-
const exhaustedStepBudgetDuringInterruptedLocalToolRecovery = !recoveredInterruptedLocalToolBatch &&
|
|
4966
|
-
step + 1 >= maxSteps &&
|
|
4967
|
-
!hasExposedReasoning &&
|
|
4968
|
-
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall) &&
|
|
4969
|
-
shouldContinueAfterStreamStep(state, { recoverInterruptedToolCalls: true });
|
|
4970
|
-
// Exactly `shouldRecoverInterruptedLocalToolBatch` with the reasoning
|
|
4971
|
-
// gate lifted: the batch this step would have replayed had it not
|
|
4972
|
-
// already exposed reasoning. Re-asking is what separates "recovery was
|
|
4973
|
-
// declined" from "this step merely carried reasoning";
|
|
4974
|
-
// `shouldContinueAfterStreamStep` only reads state, so asking twice has
|
|
4975
|
-
// no side effects, and the cheap conditions short-circuit ahead of it.
|
|
4976
|
-
const declinedRecoveryForExposedReasoning = hasExposedReasoning &&
|
|
4977
|
-
!recoveredInterruptedLocalToolBatch &&
|
|
4978
|
-
step + 1 < maxSteps &&
|
|
4979
|
-
somePrivateArray(streamedToolCalls, isInterruptedClientToolCall) &&
|
|
4980
|
-
shouldContinueAfterStreamStep(state, { recoverInterruptedToolCalls: true });
|
|
4981
|
-
if (declinedRecoveryForExposedReasoning) {
|
|
4982
|
-
logger.warn("Declined interrupted local tool batch recovery after exposed reasoning", {
|
|
4983
|
-
step,
|
|
4984
|
-
toolName: filterPrivateArray(streamedToolCalls, isInterruptedClientToolCall)[0]?.name,
|
|
4985
|
-
reasoningPartCount: persistedReasoningParts.length,
|
|
4986
|
-
});
|
|
4987
|
-
}
|
|
4988
|
-
const assistantMessage = buildStreamedAssistantMessage({
|
|
4989
|
-
...state,
|
|
4990
|
-
accumulatedText: recoveryPresentationText,
|
|
4991
|
-
}, {
|
|
4992
|
-
id: `msg_${Date.now()}_${step}`,
|
|
4993
|
-
timestamp: Date.now(),
|
|
4994
|
-
}, {
|
|
4995
|
-
preserveRecoverablePlaceholderToolCalls: shouldRecoverInterruptedLocalToolBatch ||
|
|
4996
|
-
!shouldContinue,
|
|
4997
|
-
});
|
|
4998
|
-
attachProviderMetadata(assistantMessage, await reconcileSuppressedProviderMetadata(languageModel, state.providerMetadata, state.suppressedToolCalls, state.toolCalls.size > 0, abortSignal));
|
|
4999
|
-
for (const tc of state.toolCalls.values()) {
|
|
5000
|
-
const materialized = materializeStreamedToolCall(tc);
|
|
5001
|
-
if (materialized.kind === "incomplete" && isRecoverablePlaceholderToolCall(tc)) {
|
|
5002
|
-
// Provisional empty-object placeholder that never finalized. The
|
|
5003
|
-
// model never committed arguments. Preserve it when recovery or
|
|
5004
|
-
// terminalization records a matching tool result; otherwise the
|
|
5005
|
-
// assistant message builder can omit it beside final text. Surface no
|
|
5006
|
-
// input warning or error for the provisional fragment.
|
|
5173
|
+
recoveredEmptyResponse = true;
|
|
5174
|
+
pushPrivateArray(currentMessages, markRuntimeGeneratedUserMessage({
|
|
5175
|
+
id: `runtime_empty_response_${Date.now()}_${step}`,
|
|
5176
|
+
role: "user",
|
|
5177
|
+
parts: [{
|
|
5178
|
+
type: "text",
|
|
5179
|
+
text: EMPTY_RESPONSE_RECOVERY_PROMPT,
|
|
5180
|
+
}],
|
|
5181
|
+
timestamp: Date.now(),
|
|
5182
|
+
}));
|
|
5183
|
+
await pauseAtBoundary(step + 1);
|
|
5184
|
+
this.status = "thinking";
|
|
5007
5185
|
continue;
|
|
5008
5186
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
// tool use. Surface the failure via SSE so the live client can
|
|
5013
|
-
// react, and leave the partial fragment under `inputText` in the
|
|
5014
|
-
// persisted part above so the history is replayable and transparent.
|
|
5015
|
-
logger.warn("Streamed tool call terminated before tool-call event", {
|
|
5016
|
-
toolCallId: tc.id,
|
|
5017
|
-
toolName: tc.name,
|
|
5018
|
-
partialArgumentsLength: materialized.partialArgumentsLength,
|
|
5019
|
-
partialArgumentsPreview: materialized.partialArgumentsPreview,
|
|
5020
|
-
});
|
|
5021
|
-
if (tc.inputAnnounced === true) {
|
|
5022
|
-
const dynamicIncomplete = isDynamicTool(tc.name, this.config.tools);
|
|
5023
|
-
sendSSE(controller, encoder, {
|
|
5024
|
-
type: "tool-input-error",
|
|
5025
|
-
toolCallId: tc.id,
|
|
5026
|
-
errorText: `Stream terminated before tool-call event fired for "${tc.name}". ` +
|
|
5027
|
-
`Received ${materialized.partialArgumentsLength} chars of partial tool-input deltas.`,
|
|
5028
|
-
...(dynamicIncomplete ? { dynamic: true } : {}),
|
|
5029
|
-
});
|
|
5187
|
+
const persistToolResult = async (toolResult) => {
|
|
5188
|
+
if (currentStepToolResults.has(toolResult.toolCallId)) {
|
|
5189
|
+
return;
|
|
5030
5190
|
}
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
toolCallId: tc.id,
|
|
5035
|
-
error: materialized.parseError,
|
|
5191
|
+
const executionAuthority = resolveToolExecutionAuthority({
|
|
5192
|
+
toolName: toolResult.toolName,
|
|
5193
|
+
plan: effectiveToolExposurePlan,
|
|
5036
5194
|
});
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
await persistProviderReplayCheckpointAfterTurn({
|
|
5069
|
-
emission: providerReplayCheckpointEmission,
|
|
5070
|
-
providerMetadata: readAttachedProviderMetadata(assistantMessage),
|
|
5071
|
-
invokeAgentToolCalls: streamedSkillDelegationOrder === "interleaved"
|
|
5072
|
-
? undefined
|
|
5073
|
-
: streamedInvokeAgentBatch,
|
|
5074
|
-
deferCompletion: streamedBatchCompletionDeferred,
|
|
5075
|
-
});
|
|
5076
|
-
if (this.#persistRecoveryBoundary) {
|
|
5077
|
-
// Recovery snapshots require an authenticated admission for every local
|
|
5078
|
-
// call in this turn, including calls interrupted before input finalized.
|
|
5079
|
-
// Announce them before waiting for persistence so both retry and terminal
|
|
5080
|
-
// paths can acknowledge the matching error without an admission cycle.
|
|
5081
|
-
for (const toolCall of streamedToolCalls) {
|
|
5082
|
-
if (toolCall.providerExecuted !== true && isStreamedToolCallIncomplete(toolCall) &&
|
|
5083
|
-
!finalToolResults.has(toolCall.id)) {
|
|
5195
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(toolResult.toolCallId, toolResult.toolName, toolResult.error === undefined
|
|
5196
|
+
? toolResult.output
|
|
5197
|
+
: { error: stringifyToolError(toolResult.error) }, executionAuthority?.toolDefinition, toolResult.providerExecuted === true);
|
|
5198
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5199
|
+
await persistMessage(toolResultMessage);
|
|
5200
|
+
currentStepToolResults.set(toolResult.toolCallId, toolResultMessage.parts[0]);
|
|
5201
|
+
};
|
|
5202
|
+
const recordIncompleteLocalToolError = async (toolCall, options = {}) => {
|
|
5203
|
+
if (toolCall.providerExecuted === true ||
|
|
5204
|
+
!isStreamedToolCallIncomplete(toolCall) ||
|
|
5205
|
+
finalToolResults.has(toolCall.id)) {
|
|
5206
|
+
return false;
|
|
5207
|
+
}
|
|
5208
|
+
if (options.announceInput === true) {
|
|
5209
|
+
// An interrupted call never reached `tool-input-end`, so its
|
|
5210
|
+
// `tool-input-start` is still buffered and `inputAnnounced` is false
|
|
5211
|
+
// — which would suppress the `tool-output-error` below. Every
|
|
5212
|
+
// terminal path passes `announceInput`, because on all of them the
|
|
5213
|
+
// run stops here and the client would otherwise be left with
|
|
5214
|
+
// whatever preceded the truncation and then nothing at all. Which
|
|
5215
|
+
// path declined recovery — exposed reasoning, a spent step budget, a
|
|
5216
|
+
// second interruption, an exposed sibling — is invisible to the
|
|
5217
|
+
// user, so it must not decide whether the failure renders (#3737).
|
|
5218
|
+
//
|
|
5219
|
+
// The name is safe to publish here. `tool-call` is what can supersede
|
|
5220
|
+
// a name, and it also sets `inputAvailable`, which fails the guard
|
|
5221
|
+
// above — so reaching this line means no such event arrived and the
|
|
5222
|
+
// buffered name is the only one this call will ever have. It is the
|
|
5223
|
+
// same name recorded below and in the persisted assistant message,
|
|
5224
|
+
// so the card matches a reload. Announcing is idempotent, so a call
|
|
5225
|
+
// surfaced upstream is not reported twice.
|
|
5084
5226
|
announceStreamedToolCallInput(controller, encoder, toolCall, (name) => isDynamicTool(name, this.config.tools));
|
|
5085
5227
|
}
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
}
|
|
5100
|
-
: {}),
|
|
5101
|
-
});
|
|
5102
|
-
if (recoveredEmptyResponse || step + 1 >= maxSteps) {
|
|
5103
|
-
throw new RuntimeEmptyResponseError();
|
|
5104
|
-
}
|
|
5105
|
-
recoveredEmptyResponse = true;
|
|
5106
|
-
pushPrivateArray(currentMessages, markRuntimeGeneratedUserMessage({
|
|
5107
|
-
id: `runtime_empty_response_${Date.now()}_${step}`,
|
|
5108
|
-
role: "user",
|
|
5109
|
-
parts: [{
|
|
5110
|
-
type: "text",
|
|
5111
|
-
text: EMPTY_RESPONSE_RECOVERY_PROMPT,
|
|
5112
|
-
}],
|
|
5113
|
-
timestamp: Date.now(),
|
|
5114
|
-
}));
|
|
5115
|
-
await pauseAtBoundary(step + 1);
|
|
5116
|
-
this.status = "thinking";
|
|
5117
|
-
continue;
|
|
5118
|
-
}
|
|
5119
|
-
const persistToolResult = async (toolResult) => {
|
|
5120
|
-
if (currentStepToolResults.has(toolResult.toolCallId)) {
|
|
5121
|
-
return;
|
|
5122
|
-
}
|
|
5123
|
-
const executionAuthority = resolveToolExecutionAuthority({
|
|
5124
|
-
toolName: toolResult.toolName,
|
|
5125
|
-
plan: effectiveToolExposurePlan,
|
|
5126
|
-
});
|
|
5127
|
-
const toolResultMessage = createPolicyAwareToolResultMessage(toolResult.toolCallId, toolResult.toolName, toolResult.error === undefined
|
|
5128
|
-
? toolResult.output
|
|
5129
|
-
: { error: stringifyToolError(toolResult.error) }, executionAuthority?.toolDefinition, toolResult.providerExecuted === true);
|
|
5130
|
-
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5131
|
-
await persistMessage(toolResultMessage);
|
|
5132
|
-
currentStepToolResults.set(toolResult.toolCallId, toolResultMessage.parts[0]);
|
|
5133
|
-
};
|
|
5134
|
-
const recordIncompleteLocalToolError = async (toolCall, options = {}) => {
|
|
5135
|
-
if (toolCall.providerExecuted === true ||
|
|
5136
|
-
!isStreamedToolCallIncomplete(toolCall) ||
|
|
5137
|
-
finalToolResults.has(toolCall.id)) {
|
|
5138
|
-
return false;
|
|
5139
|
-
}
|
|
5140
|
-
if (options.announceInput === true) {
|
|
5141
|
-
// An interrupted call never reached `tool-input-end`, so its
|
|
5142
|
-
// `tool-input-start` is still buffered and `inputAnnounced` is false
|
|
5143
|
-
// — which would suppress the `tool-output-error` below. Every
|
|
5144
|
-
// terminal path passes `announceInput`, because on all of them the
|
|
5145
|
-
// run stops here and the client would otherwise be left with
|
|
5146
|
-
// whatever preceded the truncation and then nothing at all. Which
|
|
5147
|
-
// path declined recovery — exposed reasoning, a spent step budget, a
|
|
5148
|
-
// second interruption, an exposed sibling — is invisible to the
|
|
5149
|
-
// user, so it must not decide whether the failure renders (#3737).
|
|
5150
|
-
//
|
|
5151
|
-
// The name is safe to publish here. `tool-call` is what can supersede
|
|
5152
|
-
// a name, and it also sets `inputAvailable`, which fails the guard
|
|
5153
|
-
// above — so reaching this line means no such event arrived and the
|
|
5154
|
-
// buffered name is the only one this call will ever have. It is the
|
|
5155
|
-
// same name recorded below and in the persisted assistant message,
|
|
5156
|
-
// so the card matches a reload. Announcing is idempotent, so a call
|
|
5157
|
-
// surfaced upstream is not reported twice.
|
|
5158
|
-
announceStreamedToolCallInput(controller, encoder, toolCall, (name) => isDynamicTool(name, this.config.tools));
|
|
5159
|
-
}
|
|
5160
|
-
const incompleteToolCall = {
|
|
5161
|
-
id: toolCall.id,
|
|
5162
|
-
name: toolCall.name,
|
|
5163
|
-
args: {},
|
|
5164
|
-
...(toolCall.arguments.length > 0 ? { inputText: toolCall.arguments } : {}),
|
|
5165
|
-
status: "pending",
|
|
5228
|
+
const incompleteToolCall = {
|
|
5229
|
+
id: toolCall.id,
|
|
5230
|
+
name: toolCall.name,
|
|
5231
|
+
args: {},
|
|
5232
|
+
...(toolCall.arguments.length > 0 ? { inputText: toolCall.arguments } : {}),
|
|
5233
|
+
status: "pending",
|
|
5234
|
+
};
|
|
5235
|
+
await this.recordToolError(persistMessage, incompleteToolCall, `Stream terminated before tool-call event fired for "${toolCall.name}". ` +
|
|
5236
|
+
`Received ${toolCall.arguments.length} chars of partial tool-input deltas.`, { controller, encoder }, currentMessages, toolCalls, {
|
|
5237
|
+
emitSse: toolCall.inputAnnounced === true,
|
|
5238
|
+
includeInResponse: options.includeInResponse,
|
|
5239
|
+
});
|
|
5240
|
+
return true;
|
|
5166
5241
|
};
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
includeInResponse: options.includeInResponse,
|
|
5171
|
-
});
|
|
5172
|
-
return true;
|
|
5173
|
-
};
|
|
5174
|
-
if (!shouldContinue) {
|
|
5175
|
-
for (const toolResult of finalToolResults.values()) {
|
|
5176
|
-
await persistToolResult(toolResult);
|
|
5177
|
-
}
|
|
5178
|
-
for (let toolCallIndex = 0; toolCallIndex < streamedToolCalls.length; toolCallIndex++) {
|
|
5179
|
-
// A rejected batch must close committed local calls too, without running
|
|
5180
|
-
// side effects or inventing a result for a provider-owned sibling.
|
|
5181
|
-
if (!ObjectHasOwn(streamedToolCalls, toolCallIndex))
|
|
5182
|
-
continue;
|
|
5183
|
-
const toolCall = streamedToolCalls[toolCallIndex];
|
|
5184
|
-
if (toolCall.providerExecuted !== true && toolCall.inputAvailable === true &&
|
|
5185
|
-
!finalToolResults.has(toolCall.id) && !currentStepToolResults.has(toolCall.id)) {
|
|
5186
|
-
const capturedInput = captureStreamedToolCallInput(toolCall);
|
|
5187
|
-
await this.recordToolError(persistMessage, {
|
|
5188
|
-
id: toolCall.id,
|
|
5189
|
-
name: toolCall.name,
|
|
5190
|
-
args: capturedInput.args,
|
|
5191
|
-
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
5192
|
-
status: "pending",
|
|
5193
|
-
}, "Tool execution skipped because the model turn ended before local execution could continue.", { controller, encoder }, currentMessages, toolCalls);
|
|
5194
|
-
continue;
|
|
5242
|
+
if (!shouldContinue) {
|
|
5243
|
+
for (const toolResult of finalToolResults.values()) {
|
|
5244
|
+
await persistToolResult(toolResult);
|
|
5195
5245
|
}
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
},
|
|
5246
|
+
for (let toolCallIndex = 0; toolCallIndex < streamedToolCalls.length; toolCallIndex++) {
|
|
5247
|
+
// A rejected batch must close committed local calls too, without running
|
|
5248
|
+
// side effects or inventing a result for a provider-owned sibling.
|
|
5249
|
+
if (!ObjectHasOwn(streamedToolCalls, toolCallIndex))
|
|
5250
|
+
continue;
|
|
5251
|
+
const toolCall = streamedToolCalls[toolCallIndex];
|
|
5252
|
+
if (toolCall.providerExecuted !== true && toolCall.inputAvailable === true &&
|
|
5253
|
+
!finalToolResults.has(toolCall.id) && !currentStepToolResults.has(toolCall.id)) {
|
|
5254
|
+
const capturedInput = captureStreamedToolCallInput(toolCall);
|
|
5255
|
+
await this.recordToolError(persistMessage, {
|
|
5256
|
+
id: toolCall.id,
|
|
5257
|
+
name: toolCall.name,
|
|
5258
|
+
args: capturedInput.args,
|
|
5259
|
+
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
5260
|
+
status: "pending",
|
|
5261
|
+
}, "Tool execution skipped because the model turn ended before local execution could continue.", { controller, encoder }, currentMessages, toolCalls);
|
|
5262
|
+
continue;
|
|
5214
5263
|
}
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5264
|
+
// Terminal. Every incomplete local call recorded here is also
|
|
5265
|
+
// terminalized into history, so announce unconditionally and let the
|
|
5266
|
+
// wire carry the same failure. `recordIncompleteLocalToolError`
|
|
5267
|
+
// guards on `providerExecuted`, completeness and a final result, so
|
|
5268
|
+
// only genuinely truncated local calls are announced, and
|
|
5269
|
+
// `announceStreamedToolCallInput` is idempotent for any already
|
|
5270
|
+
// surfaced upstream.
|
|
5271
|
+
await recordIncompleteLocalToolError(toolCall, { announceInput: true });
|
|
5272
|
+
}
|
|
5273
|
+
sendSSE(controller, encoder, {
|
|
5274
|
+
type: "step-end",
|
|
5275
|
+
...(runtimeObservationStepId
|
|
5276
|
+
? {
|
|
5277
|
+
privateRuntimeObservation: {
|
|
5278
|
+
version: 1,
|
|
5279
|
+
kind: "step_ended",
|
|
5280
|
+
stepId: runtimeObservationStepId,
|
|
5281
|
+
},
|
|
5282
|
+
}
|
|
5283
|
+
: {}),
|
|
5223
5284
|
});
|
|
5285
|
+
completedWithinStepBudget = !exhaustedStepBudgetDuringInterruptedLocalToolRecovery;
|
|
5286
|
+
if (completedWithinStepBudget && this.#persistRecoveryBoundary) {
|
|
5287
|
+
const runtimeContinuation = captureRecoveryContinuation(step + 1);
|
|
5288
|
+
await this.#persistRecoveryBoundary({
|
|
5289
|
+
...createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)),
|
|
5290
|
+
runtimeContinuation,
|
|
5291
|
+
});
|
|
5292
|
+
}
|
|
5293
|
+
if (!completedWithinStepBudget)
|
|
5294
|
+
await pauseAtBoundary(step + 1);
|
|
5295
|
+
break;
|
|
5224
5296
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5297
|
+
this.status = "tool_execution";
|
|
5298
|
+
if (shouldRecoverInterruptedLocalToolBatch) {
|
|
5299
|
+
// Treat parallel local calls as one batch. Executing the finalized
|
|
5300
|
+
// prefix here could apply only part of the model's intended mutation.
|
|
5301
|
+
recoveredInterruptedLocalToolBatch = true;
|
|
5302
|
+
interruptedLocalToolBatchRecoveryStep = step + 1;
|
|
5303
|
+
interruptedLocalToolBatchRecoveryText = hasSubstantiveAssistantText(stepAssistantText)
|
|
5304
|
+
? stepAssistantText
|
|
5305
|
+
: undefined;
|
|
5306
|
+
}
|
|
5307
|
+
for (let toolCallIndex = 0; toolCallIndex < streamedToolCalls.length; toolCallIndex++) {
|
|
5308
|
+
if (!ObjectHasOwn(streamedToolCalls, toolCallIndex))
|
|
5309
|
+
continue;
|
|
5310
|
+
const tc = streamedToolCalls[toolCallIndex];
|
|
5311
|
+
throwIfAborted(abortSignal);
|
|
5312
|
+
if (shouldRecoverInterruptedLocalToolBatch && tc.providerExecuted !== true) {
|
|
5313
|
+
if (await recordIncompleteLocalToolError(tc, { includeInResponse: false })) {
|
|
5314
|
+
continue;
|
|
5315
|
+
}
|
|
5316
|
+
const capturedInput = captureStreamedToolCallInput(tc);
|
|
5317
|
+
const interruptedBatchToolCall = {
|
|
5318
|
+
id: tc.id,
|
|
5319
|
+
name: tc.name,
|
|
5320
|
+
args: capturedInput.args,
|
|
5321
|
+
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
5322
|
+
status: "pending",
|
|
5323
|
+
};
|
|
5324
|
+
await this.recordToolError(persistMessage, interruptedBatchToolCall, "Tool execution skipped because another tool call in the same model step " +
|
|
5325
|
+
"was interrupted before its input completed.", { controller, encoder }, currentMessages, toolCalls);
|
|
5326
|
+
continue;
|
|
5327
|
+
}
|
|
5328
|
+
if (isRecoverablePlaceholderToolCall(tc)) {
|
|
5329
|
+
// Provisional empty-object placeholder that never finalized. If the
|
|
5330
|
+
// bounded recovery path was unavailable, do not execute or surface
|
|
5331
|
+
// it as a committed call.
|
|
5332
|
+
continue;
|
|
5333
|
+
}
|
|
5334
|
+
if (await recordIncompleteLocalToolError(tc)) {
|
|
5335
|
+
// Stream ended before the provider finalized this tool call. We
|
|
5336
|
+
// cannot execute it, so record a distinct stream-termination error
|
|
5337
|
+
// (not a tool-argument parse error) so the parent step and any
|
|
5338
|
+
// upstream orchestrator (e.g. the child-fork watchdog) see a
|
|
5339
|
+
// completed step with a clearly-labelled failure and can recover.
|
|
5246
5340
|
continue;
|
|
5247
5341
|
}
|
|
5248
5342
|
const capturedInput = captureStreamedToolCallInput(tc);
|
|
5249
|
-
const
|
|
5343
|
+
const toolCall = {
|
|
5250
5344
|
id: tc.id,
|
|
5251
5345
|
name: tc.name,
|
|
5252
5346
|
args: capturedInput.args,
|
|
5253
5347
|
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
5254
5348
|
status: "pending",
|
|
5255
5349
|
};
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
continue;
|
|
5273
|
-
}
|
|
5274
|
-
const capturedInput = captureStreamedToolCallInput(tc);
|
|
5275
|
-
const toolCall = {
|
|
5276
|
-
id: tc.id,
|
|
5277
|
-
name: tc.name,
|
|
5278
|
-
args: capturedInput.args,
|
|
5279
|
-
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
5280
|
-
status: "pending",
|
|
5281
|
-
};
|
|
5282
|
-
const matchingResult = finalToolResults.get(tc.id);
|
|
5283
|
-
const persistedResult = currentStepToolResults.get(tc.id);
|
|
5284
|
-
const executionAuthority = resolveToolExecutionAuthority({
|
|
5285
|
-
toolName: tc.name,
|
|
5286
|
-
plan: effectiveToolExposurePlan,
|
|
5287
|
-
});
|
|
5288
|
-
if (streamedBatchCompletionDeferred && tc.providerExecuted !== true && !matchingResult &&
|
|
5289
|
-
!persistedResult &&
|
|
5290
|
-
IntrinsicReflectApply(IntrinsicSetHas, providerReplayCheckpointEmission.invokeAgentToolNames, [tc.name])) {
|
|
5291
|
-
await completeDeferredProviderReplayCheckpointTurn(providerReplayCheckpointEmission, collectStreamedParallelInvokeAgentToolCalls(streamedToolCalls, finalToolResults, providerReplayCheckpointEmission.invokeAgentToolNames, shouldContinue, effectiveToolExposurePlan, {
|
|
5292
|
-
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
5293
|
-
toolsConfig: this.config.tools,
|
|
5294
|
-
agentId: this.id,
|
|
5295
|
-
hasToolReplacements: false,
|
|
5296
|
-
}));
|
|
5297
|
-
streamedBatchCompletionDeferred = false;
|
|
5298
|
-
}
|
|
5299
|
-
if (matchingResult) {
|
|
5300
|
-
await persistToolResult(matchingResult);
|
|
5301
|
-
const trustedPlatformPolicyResult = (() => {
|
|
5302
|
-
const part = currentStepToolResults.get(tc.id);
|
|
5303
|
-
return part !== undefined && hasTrustedPlatformPolicyToolResultPart(part);
|
|
5304
|
-
})();
|
|
5305
|
-
toolCall.status = matchingResult.error === undefined ? "completed" : "error";
|
|
5306
|
-
toolCall.result = matchingResult.output;
|
|
5307
|
-
toolCall.error = matchingResult.error === undefined
|
|
5308
|
-
? undefined
|
|
5309
|
-
: stringifyToolError(matchingResult.error);
|
|
5310
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
5311
|
-
if (matchingResult.error === undefined) {
|
|
5312
|
-
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.name)) {
|
|
5313
|
-
agentWriteFinalResponseToolGuardEnabled = true;
|
|
5314
|
-
}
|
|
5315
|
-
if (isLoadSkillToolName(tc.name) &&
|
|
5316
|
-
hasTrustedPlatformPolicyToolDefinition(executionAuthority?.toolDefinition) &&
|
|
5317
|
-
trustedPlatformPolicyResult) {
|
|
5318
|
-
skillState.applySuccessfulResult(matchingResult.output);
|
|
5319
|
-
}
|
|
5320
|
-
const submittedFormInput = trustedPlatformPolicyResult &&
|
|
5321
|
-
isSubmittedFormInputExecutionResult(tc.name, matchingResult.output, executionAuthority?.toolDefinition);
|
|
5322
|
-
skillState.markFormInputSubmitted(submittedFormInput);
|
|
5323
|
-
if (submittedFormInput) {
|
|
5324
|
-
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
5325
|
-
}
|
|
5350
|
+
const matchingResult = finalToolResults.get(tc.id);
|
|
5351
|
+
const persistedResult = currentStepToolResults.get(tc.id);
|
|
5352
|
+
const executionAuthority = resolveToolExecutionAuthority({
|
|
5353
|
+
toolName: tc.name,
|
|
5354
|
+
plan: effectiveToolExposurePlan,
|
|
5355
|
+
});
|
|
5356
|
+
if (streamedBatchCompletionDeferred && tc.providerExecuted !== true && !matchingResult &&
|
|
5357
|
+
!persistedResult &&
|
|
5358
|
+
IntrinsicReflectApply(IntrinsicSetHas, providerReplayCheckpointEmission.invokeAgentToolNames, [tc.name])) {
|
|
5359
|
+
await completeDeferredProviderReplayCheckpointTurn(providerReplayCheckpointEmission, collectStreamedParallelInvokeAgentToolCalls(streamedToolCalls, finalToolResults, providerReplayCheckpointEmission.invokeAgentToolNames, shouldContinue, effectiveToolExposurePlan, {
|
|
5360
|
+
activeSkillDelegationOverrides: skillState.activeSkillDelegationOverrides,
|
|
5361
|
+
toolsConfig: this.config.tools,
|
|
5362
|
+
agentId: this.id,
|
|
5363
|
+
hasToolReplacements: false,
|
|
5364
|
+
}));
|
|
5365
|
+
streamedBatchCompletionDeferred = false;
|
|
5326
5366
|
}
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5367
|
+
if (matchingResult) {
|
|
5368
|
+
await persistToolResult(matchingResult);
|
|
5369
|
+
const trustedPlatformPolicyResult = (() => {
|
|
5370
|
+
const part = currentStepToolResults.get(tc.id);
|
|
5371
|
+
return part !== undefined && hasTrustedPlatformPolicyToolResultPart(part);
|
|
5372
|
+
})();
|
|
5373
|
+
toolCall.status = matchingResult.error === undefined ? "completed" : "error";
|
|
5374
|
+
toolCall.result = matchingResult.output;
|
|
5375
|
+
toolCall.error = matchingResult.error === undefined
|
|
5376
|
+
? undefined
|
|
5377
|
+
: stringifyToolError(matchingResult.error);
|
|
5378
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
5379
|
+
if (matchingResult.error === undefined) {
|
|
5380
|
+
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.name)) {
|
|
5381
|
+
agentWriteFinalResponseToolGuardEnabled = true;
|
|
5382
|
+
}
|
|
5383
|
+
if (isLoadSkillToolName(tc.name) &&
|
|
5384
|
+
hasTrustedPlatformPolicyToolDefinition(executionAuthority?.toolDefinition) &&
|
|
5385
|
+
trustedPlatformPolicyResult) {
|
|
5386
|
+
skillState.applySuccessfulResult(matchingResult.output);
|
|
5387
|
+
}
|
|
5388
|
+
const submittedFormInput = trustedPlatformPolicyResult &&
|
|
5389
|
+
isSubmittedFormInputExecutionResult(tc.name, matchingResult.output, executionAuthority?.toolDefinition);
|
|
5390
|
+
skillState.markFormInputSubmitted(submittedFormInput);
|
|
5391
|
+
if (submittedFormInput) {
|
|
5392
|
+
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
5393
|
+
}
|
|
5344
5394
|
}
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5395
|
+
continue;
|
|
5396
|
+
}
|
|
5397
|
+
if (persistedResult) {
|
|
5398
|
+
const trustedPlatformPolicyResult = hasTrustedPlatformPolicyToolResultPart(persistedResult);
|
|
5399
|
+
const persistedError = getToolResultError(persistedResult.result);
|
|
5400
|
+
toolCall.status = persistedError === undefined ? "completed" : "error";
|
|
5401
|
+
toolCall.result = persistedResult.result;
|
|
5402
|
+
toolCall.error = persistedError;
|
|
5403
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
5404
|
+
if (persistedError === undefined) {
|
|
5405
|
+
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.name)) {
|
|
5406
|
+
agentWriteFinalResponseToolGuardEnabled = true;
|
|
5407
|
+
}
|
|
5408
|
+
if (isLoadSkillToolName(tc.name) &&
|
|
5409
|
+
hasTrustedPlatformPolicyToolDefinition(executionAuthority?.toolDefinition) &&
|
|
5410
|
+
trustedPlatformPolicyResult) {
|
|
5411
|
+
skillState.applySuccessfulResult(persistedResult.result);
|
|
5412
|
+
}
|
|
5413
|
+
const submittedFormInput = trustedPlatformPolicyResult &&
|
|
5414
|
+
isSubmittedFormInputExecutionResult(tc.name, persistedResult.result, executionAuthority?.toolDefinition);
|
|
5415
|
+
skillState.markFormInputSubmitted(submittedFormInput);
|
|
5416
|
+
if (submittedFormInput) {
|
|
5417
|
+
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
5418
|
+
}
|
|
5350
5419
|
}
|
|
5420
|
+
continue;
|
|
5351
5421
|
}
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
await traceProviderExecutedTool({
|
|
5356
|
-
mode: "stream",
|
|
5357
|
-
agentId: this.id,
|
|
5358
|
-
toolName: tc.name,
|
|
5359
|
-
toolCallId: tc.id,
|
|
5360
|
-
context: {
|
|
5361
|
-
toolCallId: tc.id,
|
|
5362
|
-
...toolContext,
|
|
5422
|
+
if (tc.providerExecuted === true) {
|
|
5423
|
+
await traceProviderExecutedTool({
|
|
5424
|
+
mode: "stream",
|
|
5363
5425
|
agentId: this.id,
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
if (capturedInput.parseError) {
|
|
5372
|
-
logger.warn("Invalid streamed tool arguments", {
|
|
5373
|
-
toolCallId: tc.id,
|
|
5374
|
-
error: capturedInput.parseError,
|
|
5375
|
-
});
|
|
5376
|
-
const dynamic = isDynamicTool(tc.name, this.config.tools);
|
|
5377
|
-
sendSSE(controller, encoder, {
|
|
5378
|
-
type: "tool-input-error",
|
|
5379
|
-
toolCallId: tc.id,
|
|
5380
|
-
errorText: `Invalid tool arguments: ${capturedInput.parseError}`,
|
|
5381
|
-
...(dynamic ? { dynamic: true } : {}),
|
|
5382
|
-
});
|
|
5383
|
-
await this.recordToolError(persistMessage, toolCall, `Invalid tool arguments: ${capturedInput.parseError}`, { controller, encoder }, currentMessages, toolCalls);
|
|
5384
|
-
continue;
|
|
5385
|
-
}
|
|
5386
|
-
if (isFrameworkToolSearch(tc.name, effectiveToolExposurePlan)) {
|
|
5387
|
-
let checkpoint;
|
|
5388
|
-
try {
|
|
5389
|
-
callbacks?.onToolCall?.(toolCall);
|
|
5390
|
-
const search = executeFrameworkToolSearch({
|
|
5426
|
+
toolName: tc.name,
|
|
5427
|
+
toolCallId: tc.id,
|
|
5428
|
+
context: {
|
|
5429
|
+
toolCallId: tc.id,
|
|
5430
|
+
...toolContext,
|
|
5431
|
+
agentId: this.id,
|
|
5432
|
+
},
|
|
5391
5433
|
args: toolCall.args,
|
|
5392
|
-
plan: effectiveToolExposurePlan,
|
|
5393
|
-
state: toolExposureState,
|
|
5394
5434
|
});
|
|
5395
|
-
if (didReloadProjectAgentWriteTool(search.result)) {
|
|
5396
|
-
agentWriteFinalResponseToolGuardEnabled = false;
|
|
5397
|
-
}
|
|
5398
5435
|
toolCall.status = "completed";
|
|
5399
|
-
toolCall.result = search.result;
|
|
5400
5436
|
pushPrivateArray(toolCalls, toolCall);
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5437
|
+
continue;
|
|
5438
|
+
}
|
|
5439
|
+
if (capturedInput.parseError) {
|
|
5440
|
+
logger.warn("Invalid streamed tool arguments", {
|
|
5441
|
+
toolCallId: tc.id,
|
|
5442
|
+
error: capturedInput.parseError,
|
|
5405
5443
|
});
|
|
5444
|
+
const dynamic = isDynamicTool(tc.name, this.config.tools);
|
|
5406
5445
|
sendSSE(controller, encoder, {
|
|
5407
|
-
type: "tool-
|
|
5408
|
-
toolCallId:
|
|
5409
|
-
|
|
5446
|
+
type: "tool-input-error",
|
|
5447
|
+
toolCallId: tc.id,
|
|
5448
|
+
errorText: `Invalid tool arguments: ${capturedInput.parseError}`,
|
|
5449
|
+
...(dynamic ? { dynamic: true } : {}),
|
|
5410
5450
|
});
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
await persistMessage(toolResultMessage);
|
|
5414
|
-
checkpoint = search.checkpoint;
|
|
5415
|
-
currentStepToolResults.set(tc.id, toolResultMessage.parts[0]);
|
|
5451
|
+
await this.recordToolError(persistMessage, toolCall, `Invalid tool arguments: ${capturedInput.parseError}`, { controller, encoder }, currentMessages, toolCalls);
|
|
5452
|
+
continue;
|
|
5416
5453
|
}
|
|
5417
|
-
|
|
5418
|
-
|
|
5454
|
+
if (isFrameworkToolSearch(tc.name, effectiveToolExposurePlan)) {
|
|
5455
|
+
let checkpoint;
|
|
5456
|
+
try {
|
|
5457
|
+
callbacks?.onToolCall?.(toolCall);
|
|
5458
|
+
const search = executeFrameworkToolSearch({
|
|
5459
|
+
args: toolCall.args,
|
|
5460
|
+
plan: effectiveToolExposurePlan,
|
|
5461
|
+
state: toolExposureState,
|
|
5462
|
+
});
|
|
5463
|
+
if (didReloadProjectAgentWriteTool(search.result)) {
|
|
5464
|
+
agentWriteFinalResponseToolGuardEnabled = false;
|
|
5465
|
+
}
|
|
5466
|
+
toolCall.status = "completed";
|
|
5467
|
+
toolCall.result = search.result;
|
|
5468
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
5469
|
+
setOtelActiveSpanAttributes({
|
|
5470
|
+
"tool.search.result_count": search.result.resultCount,
|
|
5471
|
+
"tool.search.loaded_count": search.result.loadedCount,
|
|
5472
|
+
"tool.search.miss": search.result.miss,
|
|
5473
|
+
});
|
|
5474
|
+
sendSSE(controller, encoder, {
|
|
5475
|
+
type: "tool-output-available",
|
|
5476
|
+
toolCallId: toolCall.id,
|
|
5477
|
+
output: search.result,
|
|
5478
|
+
});
|
|
5479
|
+
const toolResultMessage = createToolResultMessage(tc.id, tc.name, search.result);
|
|
5480
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5481
|
+
await persistMessage(toolResultMessage);
|
|
5482
|
+
checkpoint = search.checkpoint;
|
|
5483
|
+
currentStepToolResults.set(tc.id, toolResultMessage.parts[0]);
|
|
5484
|
+
}
|
|
5485
|
+
catch (error) {
|
|
5486
|
+
await this.recordToolError(persistMessage, toolCall, error instanceof Error ? error.message : String(error), { controller, encoder }, currentMessages, toolCalls);
|
|
5487
|
+
continue;
|
|
5488
|
+
}
|
|
5489
|
+
await persistToolExposureCheckpointBeforeContinuation({
|
|
5490
|
+
checkpoint,
|
|
5491
|
+
persist: persistToolExposureCheckpoint,
|
|
5492
|
+
required: requireToolExposureCheckpointPersistence,
|
|
5493
|
+
});
|
|
5494
|
+
continue;
|
|
5495
|
+
}
|
|
5496
|
+
if (shouldHandleToolResultRead({
|
|
5497
|
+
toolName: tc.name,
|
|
5498
|
+
plan: effectiveToolExposurePlan,
|
|
5499
|
+
context: toolResultContext,
|
|
5500
|
+
})) {
|
|
5501
|
+
try {
|
|
5502
|
+
callbacks?.onToolCall?.(toolCall);
|
|
5503
|
+
if (toolResultContext === undefined) {
|
|
5504
|
+
throw new ReferenceError("Tool result context is not available");
|
|
5505
|
+
}
|
|
5506
|
+
const result = readToolResultContext(toolResultContext, toolCall.args);
|
|
5507
|
+
toolCall.status = "completed";
|
|
5508
|
+
toolCall.result = result;
|
|
5509
|
+
pushPrivateArray(toolCalls, toolCall);
|
|
5510
|
+
sendSSE(controller, encoder, {
|
|
5511
|
+
type: "tool-output-available",
|
|
5512
|
+
toolCallId: toolCall.id,
|
|
5513
|
+
output: result,
|
|
5514
|
+
});
|
|
5515
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(tc.id, tc.name, result, executionAuthority?.toolDefinition);
|
|
5516
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5517
|
+
await persistMessage(toolResultMessage);
|
|
5518
|
+
currentStepToolResults.set(tc.id, toolResultMessage.parts[0]);
|
|
5519
|
+
}
|
|
5520
|
+
catch (error) {
|
|
5521
|
+
await this.recordToolError(persistMessage, toolCall, error instanceof Error ? error.message : String(error), { controller, encoder }, currentMessages, toolCalls);
|
|
5522
|
+
}
|
|
5523
|
+
continue;
|
|
5524
|
+
}
|
|
5525
|
+
if (shouldBlockToolResultReadName({
|
|
5526
|
+
toolName: tc.name,
|
|
5527
|
+
context: toolResultContext,
|
|
5528
|
+
})) {
|
|
5529
|
+
await this.recordToolError(persistMessage, toolCall, toolResultReaderUnavailableError(), { controller, encoder }, currentMessages, toolCalls);
|
|
5530
|
+
continue;
|
|
5531
|
+
}
|
|
5532
|
+
if (executionAuthority === undefined) {
|
|
5533
|
+
await this.recordToolError(persistMessage, toolCall, toolNotVisibleError(tc.name), { controller, encoder }, currentMessages, toolCalls);
|
|
5419
5534
|
continue;
|
|
5420
5535
|
}
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5536
|
+
const policyCheck = enforceSkillPolicy(tc.name, {
|
|
5537
|
+
activeSkillId: skillState.activeSkillId,
|
|
5538
|
+
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
5539
|
+
skillToolAvailability: skillState.activeSkillToolAvailability,
|
|
5540
|
+
toolInput: toolCall.args,
|
|
5541
|
+
toolDefinition: executionAuthority.toolDefinition,
|
|
5425
5542
|
});
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
plan: effectiveToolExposurePlan,
|
|
5431
|
-
context: toolResultContext,
|
|
5432
|
-
})) {
|
|
5543
|
+
if (!policyCheck.allowed) {
|
|
5544
|
+
await this.recordToolError(persistMessage, toolCall, policyCheck.error, { controller, encoder }, currentMessages, toolCalls);
|
|
5545
|
+
continue;
|
|
5546
|
+
}
|
|
5433
5547
|
try {
|
|
5548
|
+
toolCall.status = "executing";
|
|
5549
|
+
const startTime = Date.now();
|
|
5550
|
+
toolCall.args = applySkillDelegationOverridesToToolInput(tc.name, toolCall.args, skillState.activeSkillDelegationOverrides, resolveConfiguredTool(this.config.tools, tc.name, { agentId: this.id }) ?? undefined);
|
|
5434
5551
|
callbacks?.onToolCall?.(toolCall);
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5552
|
+
const executionContext = applicationExecutionContext(toolContext, providerObservedSkillBodies);
|
|
5553
|
+
const result = await runWithToolCallOccurrenceDispatch(tc, () => traceConfiguredToolExecution({
|
|
5554
|
+
mode: "stream",
|
|
5555
|
+
agentId: this.id,
|
|
5556
|
+
toolName: tc.name,
|
|
5557
|
+
toolCallId: tc.id,
|
|
5558
|
+
args: toolCall.args,
|
|
5559
|
+
admittedTurn,
|
|
5560
|
+
owner: currentMessages,
|
|
5561
|
+
prepareTerminalDispatch,
|
|
5562
|
+
toolsConfig: this.config.tools,
|
|
5563
|
+
context: executionContext,
|
|
5564
|
+
allowedRemoteToolNames,
|
|
5565
|
+
remoteToolSources,
|
|
5566
|
+
sourceIntegrationPolicy,
|
|
5567
|
+
frameworkLocalTools,
|
|
5568
|
+
}));
|
|
5569
|
+
throwIfAborted(abortSignal);
|
|
5570
|
+
await this.notifyToolResult({
|
|
5571
|
+
mode: "stream",
|
|
5572
|
+
toolName: tc.name,
|
|
5573
|
+
toolCallId: tc.id,
|
|
5574
|
+
input: toolCall.args,
|
|
5575
|
+
result,
|
|
5576
|
+
context: executionContext,
|
|
5577
|
+
});
|
|
5578
|
+
const resultError = getToolResultError(result);
|
|
5579
|
+
toolCall.status = resultError === undefined ? "completed" : "error";
|
|
5440
5580
|
toolCall.result = result;
|
|
5581
|
+
toolCall.error = resultError;
|
|
5582
|
+
toolCall.executionTime = Date.now() - startTime;
|
|
5441
5583
|
pushPrivateArray(toolCalls, toolCall);
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5584
|
+
if (resultError === undefined) {
|
|
5585
|
+
// Track skill policy from successful load_skill results
|
|
5586
|
+
if (isLoadSkillToolName(tc.name) &&
|
|
5587
|
+
hasTrustedPlatformPolicyToolDefinition(executionAuthority.toolDefinition)) {
|
|
5588
|
+
skillState.applySuccessfulResult(result);
|
|
5589
|
+
}
|
|
5590
|
+
const submittedFormInput = isSubmittedFormInputExecutionResult(tc.name, result, executionAuthority.toolDefinition);
|
|
5591
|
+
skillState.markFormInputSubmitted(submittedFormInput);
|
|
5592
|
+
if (submittedFormInput) {
|
|
5593
|
+
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
5594
|
+
}
|
|
5595
|
+
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.name)) {
|
|
5596
|
+
agentWriteFinalResponseToolGuardEnabled = true;
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
const dynamic = isDynamicTool(tc.name, this.config.tools);
|
|
5600
|
+
if (resultError === undefined) {
|
|
5601
|
+
sendSSE(controller, encoder, {
|
|
5602
|
+
type: "tool-output-available",
|
|
5603
|
+
toolCallId: toolCall.id,
|
|
5604
|
+
output: result,
|
|
5605
|
+
...(dynamic ? { dynamic: true } : {}),
|
|
5606
|
+
});
|
|
5607
|
+
}
|
|
5608
|
+
else {
|
|
5609
|
+
sendSSE(controller, encoder, {
|
|
5610
|
+
type: "tool-output-error",
|
|
5611
|
+
toolCallId: toolCall.id,
|
|
5612
|
+
errorText: resultError,
|
|
5613
|
+
...(dynamic ? { dynamic: true } : {}),
|
|
5614
|
+
});
|
|
5615
|
+
}
|
|
5616
|
+
const toolResultMessage = createPolicyAwareToolResultMessage(tc.id, tc.name, result, executionAuthority.toolDefinition, false, true);
|
|
5617
|
+
if (!currentStepToolResults.has(tc.id)) {
|
|
5618
|
+
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5619
|
+
await persistMessage(toolResultMessage);
|
|
5620
|
+
currentStepToolResults.set(tc.id, toolResultMessage.parts[0]);
|
|
5621
|
+
}
|
|
5451
5622
|
}
|
|
5452
5623
|
catch (error) {
|
|
5453
|
-
await this.recordToolError(persistMessage, toolCall,
|
|
5624
|
+
await this.recordToolError(persistMessage, toolCall, undefined, { controller, encoder }, currentMessages, toolCalls, { terminal: { error, usage: totalUsage, abortSignal } });
|
|
5454
5625
|
}
|
|
5455
|
-
continue;
|
|
5456
|
-
}
|
|
5457
|
-
if (shouldBlockToolResultReadName({
|
|
5458
|
-
toolName: tc.name,
|
|
5459
|
-
context: toolResultContext,
|
|
5460
|
-
})) {
|
|
5461
|
-
await this.recordToolError(persistMessage, toolCall, toolResultReaderUnavailableError(), { controller, encoder }, currentMessages, toolCalls);
|
|
5462
|
-
continue;
|
|
5463
5626
|
}
|
|
5464
|
-
|
|
5465
|
-
await
|
|
5466
|
-
continue;
|
|
5467
|
-
}
|
|
5468
|
-
const policyCheck = enforceSkillPolicy(tc.name, {
|
|
5469
|
-
activeSkillId: skillState.activeSkillId,
|
|
5470
|
-
hasSubmittedFormInput: skillState.hasSubmittedFormInput,
|
|
5471
|
-
skillToolAvailability: skillState.activeSkillToolAvailability,
|
|
5472
|
-
toolInput: toolCall.args,
|
|
5473
|
-
toolDefinition: executionAuthority.toolDefinition,
|
|
5474
|
-
});
|
|
5475
|
-
if (!policyCheck.allowed) {
|
|
5476
|
-
await this.recordToolError(persistMessage, toolCall, policyCheck.error, { controller, encoder }, currentMessages, toolCalls);
|
|
5477
|
-
continue;
|
|
5627
|
+
for (const toolResult of finalToolResults.values()) {
|
|
5628
|
+
await persistToolResult(toolResult);
|
|
5478
5629
|
}
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
admittedTurn,
|
|
5492
|
-
owner: currentMessages,
|
|
5493
|
-
prepareTerminalDispatch,
|
|
5494
|
-
toolsConfig: this.config.tools,
|
|
5495
|
-
context: executionContext,
|
|
5496
|
-
allowedRemoteToolNames,
|
|
5497
|
-
remoteToolSources,
|
|
5498
|
-
sourceIntegrationPolicy,
|
|
5499
|
-
frameworkLocalTools,
|
|
5630
|
+
if (state.suppressedToolCalls.length > 0) {
|
|
5631
|
+
const unavailableNames = [
|
|
5632
|
+
...new Set(state.suppressedToolCalls.map((toolCall) => toolCall.name)),
|
|
5633
|
+
];
|
|
5634
|
+
pushPrivateArray(currentMessages, markRuntimeGeneratedUserMessage({
|
|
5635
|
+
id: `runtime_note_${Date.now()}_${step}`,
|
|
5636
|
+
role: "user",
|
|
5637
|
+
parts: [{
|
|
5638
|
+
type: "text",
|
|
5639
|
+
text: `Runtime recovery: ignored unavailable tool call(s): ${unavailableNames.join(", ")}. Continue using only currently available tools: ${runtimeToolNames.join(", ")}.`,
|
|
5640
|
+
}],
|
|
5641
|
+
timestamp: Date.now(),
|
|
5500
5642
|
}));
|
|
5501
|
-
throwIfAborted(abortSignal);
|
|
5502
|
-
await this.notifyToolResult({
|
|
5503
|
-
mode: "stream",
|
|
5504
|
-
toolName: tc.name,
|
|
5505
|
-
toolCallId: tc.id,
|
|
5506
|
-
input: toolCall.args,
|
|
5507
|
-
result,
|
|
5508
|
-
context: executionContext,
|
|
5509
|
-
});
|
|
5510
|
-
const resultError = getToolResultError(result);
|
|
5511
|
-
toolCall.status = resultError === undefined ? "completed" : "error";
|
|
5512
|
-
toolCall.result = result;
|
|
5513
|
-
toolCall.error = resultError;
|
|
5514
|
-
toolCall.executionTime = Date.now() - startTime;
|
|
5515
|
-
pushPrivateArray(toolCalls, toolCall);
|
|
5516
|
-
if (resultError === undefined) {
|
|
5517
|
-
// Track skill policy from successful load_skill results
|
|
5518
|
-
if (isLoadSkillToolName(tc.name) &&
|
|
5519
|
-
hasTrustedPlatformPolicyToolDefinition(executionAuthority.toolDefinition)) {
|
|
5520
|
-
skillState.applySuccessfulResult(result);
|
|
5521
|
-
}
|
|
5522
|
-
const submittedFormInput = isSubmittedFormInputExecutionResult(tc.name, result, executionAuthority.toolDefinition);
|
|
5523
|
-
skillState.markFormInputSubmitted(submittedFormInput);
|
|
5524
|
-
if (submittedFormInput) {
|
|
5525
|
-
currentRuntimeContext = markSubmittedFormInputRuntimeContext(currentRuntimeContext);
|
|
5526
|
-
}
|
|
5527
|
-
if (shouldHideProjectToolAfterAgentWriteSuccess(tc.name)) {
|
|
5528
|
-
agentWriteFinalResponseToolGuardEnabled = true;
|
|
5529
|
-
}
|
|
5530
|
-
}
|
|
5531
|
-
const dynamic = isDynamicTool(tc.name, this.config.tools);
|
|
5532
|
-
if (resultError === undefined) {
|
|
5533
|
-
sendSSE(controller, encoder, {
|
|
5534
|
-
type: "tool-output-available",
|
|
5535
|
-
toolCallId: toolCall.id,
|
|
5536
|
-
output: result,
|
|
5537
|
-
...(dynamic ? { dynamic: true } : {}),
|
|
5538
|
-
});
|
|
5539
|
-
}
|
|
5540
|
-
else {
|
|
5541
|
-
sendSSE(controller, encoder, {
|
|
5542
|
-
type: "tool-output-error",
|
|
5543
|
-
toolCallId: toolCall.id,
|
|
5544
|
-
errorText: resultError,
|
|
5545
|
-
...(dynamic ? { dynamic: true } : {}),
|
|
5546
|
-
});
|
|
5547
|
-
}
|
|
5548
|
-
const toolResultMessage = createPolicyAwareToolResultMessage(tc.id, tc.name, result, executionAuthority.toolDefinition, false, true);
|
|
5549
|
-
if (!currentStepToolResults.has(tc.id)) {
|
|
5550
|
-
pushPrivateArray(currentMessages, toolResultMessage);
|
|
5551
|
-
await persistMessage(toolResultMessage);
|
|
5552
|
-
currentStepToolResults.set(tc.id, toolResultMessage.parts[0]);
|
|
5553
|
-
}
|
|
5554
5643
|
}
|
|
5555
|
-
|
|
5556
|
-
|
|
5644
|
+
if (this.#persistRecoveryBoundary) {
|
|
5645
|
+
const runtimeContinuation = captureRecoveryContinuation(step + 1);
|
|
5646
|
+
await this.#persistRecoveryBoundary({
|
|
5647
|
+
...createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)),
|
|
5648
|
+
runtimeContinuation,
|
|
5649
|
+
});
|
|
5557
5650
|
}
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
type: "text",
|
|
5571
|
-
text: `Runtime recovery: ignored unavailable tool call(s): ${unavailableNames.join(", ")}. Continue using only currently available tools: ${runtimeToolNames.join(", ")}.`,
|
|
5572
|
-
}],
|
|
5573
|
-
timestamp: Date.now(),
|
|
5574
|
-
}));
|
|
5575
|
-
}
|
|
5576
|
-
if (this.#persistRecoveryBoundary) {
|
|
5577
|
-
const runtimeContinuation = captureRecoveryContinuation(step + 1);
|
|
5578
|
-
await this.#persistRecoveryBoundary({
|
|
5579
|
-
...createRecoveryBoundary(currentMessages, createToolExposureCheckpoint(effectiveToolExposurePlan.authorized, toolExposureState)),
|
|
5580
|
-
runtimeContinuation,
|
|
5651
|
+
throwIfAborted(abortSignal);
|
|
5652
|
+
sendSSE(controller, encoder, {
|
|
5653
|
+
type: "step-end",
|
|
5654
|
+
...(runtimeObservationStepId
|
|
5655
|
+
? {
|
|
5656
|
+
privateRuntimeObservation: {
|
|
5657
|
+
version: 1,
|
|
5658
|
+
kind: "step_ended",
|
|
5659
|
+
stepId: runtimeObservationStepId,
|
|
5660
|
+
},
|
|
5661
|
+
}
|
|
5662
|
+
: {}),
|
|
5581
5663
|
});
|
|
5664
|
+
await pauseAtBoundary(step + 1);
|
|
5665
|
+
this.status = "thinking";
|
|
5666
|
+
}
|
|
5667
|
+
if (!completedWithinStepBudget) {
|
|
5668
|
+
// Step-budget exhaustion mirrors the generate loop's max-steps exit: the
|
|
5669
|
+
// partial result is still returned, so the structured-output parse is
|
|
5670
|
+
// best effort and a failure is surfaced in metadata instead of thrown.
|
|
5671
|
+
const parsedOutput = await tryParseMaxStepsOutput(latestAssistantText, outputSchema);
|
|
5672
|
+
return attachOutputSchemaParser({
|
|
5673
|
+
text: latestAssistantText,
|
|
5674
|
+
...(parsedOutput.parsed ? { data: parsedOutput.object, object: parsedOutput.object } : {}),
|
|
5675
|
+
messages: currentMessages,
|
|
5676
|
+
toolCalls,
|
|
5677
|
+
status: "completed",
|
|
5678
|
+
usage: totalUsage,
|
|
5679
|
+
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, {
|
|
5680
|
+
warning: `Max steps (${maxSteps}) reached`,
|
|
5681
|
+
...(finalFinishReason ? { finishReason: finalFinishReason } : {}),
|
|
5682
|
+
...(!parsedOutput.parsed && parsedOutput.outputSchemaError !== undefined
|
|
5683
|
+
? { outputSchemaError: parsedOutput.outputSchemaError }
|
|
5684
|
+
: {}),
|
|
5685
|
+
}),
|
|
5686
|
+
}, outputSchema);
|
|
5582
5687
|
}
|
|
5583
|
-
throwIfAborted(abortSignal);
|
|
5584
|
-
sendSSE(controller, encoder, {
|
|
5585
|
-
type: "step-end",
|
|
5586
|
-
...(runtimeObservationStepId
|
|
5587
|
-
? {
|
|
5588
|
-
privateRuntimeObservation: {
|
|
5589
|
-
version: 1,
|
|
5590
|
-
kind: "step_ended",
|
|
5591
|
-
stepId: runtimeObservationStepId,
|
|
5592
|
-
},
|
|
5593
|
-
}
|
|
5594
|
-
: {}),
|
|
5595
|
-
});
|
|
5596
|
-
await pauseAtBoundary(step + 1);
|
|
5597
|
-
this.status = "thinking";
|
|
5598
|
-
}
|
|
5599
|
-
if (!completedWithinStepBudget) {
|
|
5600
|
-
// Step-budget exhaustion mirrors the generate loop's max-steps exit: the
|
|
5601
|
-
// partial result is still returned, so the structured-output parse is
|
|
5602
|
-
// best effort and a failure is surfaced in metadata instead of thrown.
|
|
5603
|
-
const parsedOutput = await tryParseMaxStepsOutput(latestAssistantText, outputSchema);
|
|
5604
5688
|
return attachOutputSchemaParser({
|
|
5605
5689
|
text: latestAssistantText,
|
|
5606
|
-
...(
|
|
5690
|
+
...(outputSchema ? await parseStructuredOutput(outputSchema, latestAssistantText) : {}),
|
|
5607
5691
|
messages: currentMessages,
|
|
5608
5692
|
toolCalls,
|
|
5609
5693
|
status: "completed",
|
|
5610
5694
|
usage: totalUsage,
|
|
5611
|
-
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, {
|
|
5612
|
-
warning: `Max steps (${maxSteps}) reached`,
|
|
5613
|
-
...(finalFinishReason ? { finishReason: finalFinishReason } : {}),
|
|
5614
|
-
...(!parsedOutput.parsed && parsedOutput.outputSchemaError !== undefined
|
|
5615
|
-
? { outputSchemaError: parsedOutput.outputSchemaError }
|
|
5616
|
-
: {}),
|
|
5617
|
-
}),
|
|
5695
|
+
metadata: withAgentRunRuntimeContextMetadata(runRuntimeContext, finalFinishReason ? { finishReason: finalFinishReason } : undefined),
|
|
5618
5696
|
}, outputSchema);
|
|
5619
5697
|
}
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
}
|
|
5698
|
+
catch (e_2) {
|
|
5699
|
+
env_2.error = e_2;
|
|
5700
|
+
env_2.hasError = true;
|
|
5701
|
+
}
|
|
5702
|
+
finally {
|
|
5703
|
+
const result_2 = __disposeResources(env_2);
|
|
5704
|
+
if (result_2)
|
|
5705
|
+
await result_2;
|
|
5706
|
+
}
|
|
5629
5707
|
}
|
|
5630
5708
|
/**
|
|
5631
5709
|
* Record a tool error and send SSE event.
|