@evalops/maestro 0.10.26 → 0.10.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/transport/reusable-tool-results.d.ts +38 -0
- package/dist/agent/transport/reusable-tool-results.d.ts.map +1 -0
- package/dist/agent/transport/reusable-tool-results.js +432 -0
- package/dist/agent/transport/reusable-tool-results.js.map +1 -0
- package/dist/agent/transport.d.ts +1 -1
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/transport.js +1 -430
- package/dist/agent/transport.js.map +1 -1
- package/dist/audit/logger.d.ts +2 -2
- package/dist/cli/commands/run.d.ts +15 -0
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +35 -0
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli.js +750 -632
- package/dist/node_modules/@evalops/contracts/package.json +1 -1
- package/dist/node_modules/@evalops/tui/package.json +1 -1
- package/dist/platform/agent-registry-client.d.ts +6 -0
- package/dist/platform/agent-registry-client.d.ts.map +1 -1
- package/dist/platform/agent-registry-client.js +6 -0
- package/dist/platform/agent-registry-client.js.map +1 -1
- package/dist/server/agent-runtime-ledger.d.ts +5 -0
- package/dist/server/agent-runtime-ledger.d.ts.map +1 -1
- package/dist/server/agent-runtime-ledger.js +63 -0
- package/dist/server/agent-runtime-ledger.js.map +1 -1
- package/dist/server/headless-runtime/broker.d.ts +81 -0
- package/dist/server/headless-runtime/broker.d.ts.map +1 -0
- package/dist/server/headless-runtime/broker.js +201 -0
- package/dist/server/headless-runtime/broker.js.map +1 -0
- package/dist/server/headless-runtime-service.d.ts +3 -37
- package/dist/server/headless-runtime-service.d.ts.map +1 -1
- package/dist/server/headless-runtime-service.js +1 -199
- package/dist/server/headless-runtime-service.js.map +1 -1
- package/dist/session/manager.d.ts +1 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js.map +1 -1
- package/dist/tools/parallel-ripgrep.d.ts.map +1 -1
- package/dist/tools/parallel-ripgrep.js +11 -2
- package/dist/tools/parallel-ripgrep.js.map +1 -1
- package/dist/version.json +2 -2
- package/package.json +1 -1
package/dist/agent/transport.js
CHANGED
|
@@ -44,10 +44,6 @@
|
|
|
44
44
|
*
|
|
45
45
|
* @module agent/transport
|
|
46
46
|
*/
|
|
47
|
-
import { execFileSync } from "node:child_process";
|
|
48
|
-
import { createHash } from "node:crypto";
|
|
49
|
-
import { readFileSync } from "node:fs";
|
|
50
|
-
import { resolve as resolvePath } from "node:path";
|
|
51
47
|
import { isContextFirewallBlockingEnabled } from "../config/env-vars.js";
|
|
52
48
|
import { createToolHookService } from "../hooks/index.js";
|
|
53
49
|
import { getProviderNetworkConfig } from "../providers/network-config.js";
|
|
@@ -55,7 +51,6 @@ import { isStreamIdleTimeoutError } from "../providers/stream-idle-timeout.js";
|
|
|
55
51
|
import { systemClock } from "../utils/clock.js";
|
|
56
52
|
import { createLogger } from "../utils/logger.js";
|
|
57
53
|
const logger = createLogger("transport");
|
|
58
|
-
const REUSABLE_TOOL_RESULT_GIT_SNAPSHOT_TIMEOUT_MS = 5_000;
|
|
59
54
|
import { defaultActionFirewall } from "../safety/action-firewall.js";
|
|
60
55
|
import { AdaptiveThresholds } from "../safety/adaptive-thresholds.js";
|
|
61
56
|
import { checkSessionLimits } from "../safety/policy.js";
|
|
@@ -68,435 +63,11 @@ import { trackUsage } from "../tracking/cost-tracker.js";
|
|
|
68
63
|
import { getTrainingHeaders } from "../training.js";
|
|
69
64
|
import { getStoredCredentials } from "./keys.js";
|
|
70
65
|
import { createProviderStream } from "./transport/create-provider-stream.js";
|
|
71
|
-
import {
|
|
72
|
-
import { buildObservedResultMetadata, getDefaultPlatformToolExecutionBridge, } from "./transport/tool-execution-bridge.js";
|
|
66
|
+
import { clearReusableToolResultState, clearRunScopedReusableToolResultState, cloneToolOutcomeForCall, computeReusableToolResultSnapshot, createToolMetadataCache, decrementPendingReusableToolSafetyCheck, getReusableToolRegistrySignature, getReusableToolResultCacheKey, hasPendingMutatingToolExecution, hasReusableToolResultState, incrementPendingReusableToolSafetyCheck, invalidateReusableToolResultsAfterMutation, recordReusableToolExecutionBridgeOutput, resolvePlatformToolExecutionBridge, trackReusableToolResult, } from "./transport/reusable-tool-results.js";
|
|
73
67
|
import { createToolExecutionPromise } from "./transport/tool-execution.js";
|
|
74
68
|
import { evaluateToolSafety, } from "./transport/tool-safety-pipeline.js";
|
|
75
69
|
import { createToolUpdateQueue, waitForNextExecutionOrUpdate, } from "./transport/tool-update-queue.js";
|
|
76
70
|
import { calculateCost, resolveEnvCredential, } from "./transport/transport-utils.js";
|
|
77
|
-
function hashReusableToolResultSnapshot(value) {
|
|
78
|
-
return createHash("sha256").update(value).digest("hex");
|
|
79
|
-
}
|
|
80
|
-
function readGitSnapshotBuffer(cwd, args) {
|
|
81
|
-
return execFileSync("git", args, {
|
|
82
|
-
cwd,
|
|
83
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
84
|
-
timeout: REUSABLE_TOOL_RESULT_GIT_SNAPSHOT_TIMEOUT_MS,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
function readGitSnapshotPart(cwd, args) {
|
|
88
|
-
return readGitSnapshotBuffer(cwd, args).toString("utf8").trim();
|
|
89
|
-
}
|
|
90
|
-
function hashGitSnapshotPart(cwd, args) {
|
|
91
|
-
return hashReusableToolResultSnapshot(readGitSnapshotBuffer(cwd, args));
|
|
92
|
-
}
|
|
93
|
-
function hashUntrackedGitFiles(root) {
|
|
94
|
-
const files = readGitSnapshotBuffer(root, [
|
|
95
|
-
"ls-files",
|
|
96
|
-
"--others",
|
|
97
|
-
"--exclude-standard",
|
|
98
|
-
"-z",
|
|
99
|
-
])
|
|
100
|
-
.toString("utf8")
|
|
101
|
-
.split("\0")
|
|
102
|
-
.filter(Boolean)
|
|
103
|
-
.sort();
|
|
104
|
-
const hash = createHash("sha256");
|
|
105
|
-
for (const file of files) {
|
|
106
|
-
hash.update(file);
|
|
107
|
-
hash.update("\0");
|
|
108
|
-
hash.update(readFileSync(resolvePath(root, file)));
|
|
109
|
-
hash.update("\0");
|
|
110
|
-
}
|
|
111
|
-
return hash.digest("hex");
|
|
112
|
-
}
|
|
113
|
-
function hasDirtyGitSubmodules(root) {
|
|
114
|
-
try {
|
|
115
|
-
return (readGitSnapshotPart(root, [
|
|
116
|
-
"submodule",
|
|
117
|
-
"foreach",
|
|
118
|
-
"--recursive",
|
|
119
|
-
"--quiet",
|
|
120
|
-
"git status --porcelain=v1 --untracked-files=all",
|
|
121
|
-
]).length > 0);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function computeReusableToolResultSnapshot(cwd) {
|
|
128
|
-
try {
|
|
129
|
-
const root = readGitSnapshotPart(cwd, ["rev-parse", "--show-toplevel"]);
|
|
130
|
-
const head = readGitSnapshotPart(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
131
|
-
const status = readGitSnapshotPart(root, [
|
|
132
|
-
"status",
|
|
133
|
-
"--porcelain=v1",
|
|
134
|
-
"--untracked-files=all",
|
|
135
|
-
]);
|
|
136
|
-
if (hasDirtyGitSubmodules(root)) {
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
const unstagedDiff = hashGitSnapshotPart(root, [
|
|
140
|
-
"diff",
|
|
141
|
-
"--no-ext-diff",
|
|
142
|
-
"--binary",
|
|
143
|
-
]);
|
|
144
|
-
const stagedDiff = hashGitSnapshotPart(root, [
|
|
145
|
-
"diff",
|
|
146
|
-
"--cached",
|
|
147
|
-
"--no-ext-diff",
|
|
148
|
-
"--binary",
|
|
149
|
-
]);
|
|
150
|
-
const untracked = hashUntrackedGitFiles(root);
|
|
151
|
-
return `git:${hashReusableToolResultSnapshot(`${root}\n${head}\n${status}\n${unstagedDiff}\n${stagedDiff}\n${untracked}`)}`;
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
const GIT_SNAPSHOT_REUSABLE_TOOL_NAMES = new Set([
|
|
158
|
-
"read",
|
|
159
|
-
"ls",
|
|
160
|
-
"list",
|
|
161
|
-
"glob",
|
|
162
|
-
"find",
|
|
163
|
-
"grep",
|
|
164
|
-
"search",
|
|
165
|
-
"parallel_ripgrep",
|
|
166
|
-
"diff",
|
|
167
|
-
"status",
|
|
168
|
-
]);
|
|
169
|
-
const REPO_PATH_ARGUMENTS_BY_TOOL = new Map([
|
|
170
|
-
["read", ["path", "file_path"]],
|
|
171
|
-
["ls", ["path", "dir", "directory"]],
|
|
172
|
-
["list", ["path", "dir", "directory"]],
|
|
173
|
-
["glob", ["path", "cwd", "root", "glob"]],
|
|
174
|
-
["find", ["path", "cwd", "root", "glob"]],
|
|
175
|
-
["grep", ["path", "paths", "glob"]],
|
|
176
|
-
["search", ["path", "paths", "glob"]],
|
|
177
|
-
["parallel_ripgrep", ["path", "paths", "glob"]],
|
|
178
|
-
["diff", ["path", "paths", "cwd"]],
|
|
179
|
-
["status", ["path", "paths", "cwd"]],
|
|
180
|
-
]);
|
|
181
|
-
const GIT_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX = "git:";
|
|
182
|
-
const RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX = "run:";
|
|
183
|
-
// Keep network-backed READ_ONLY_TOOLS out of reusable caches even if metadata is absent.
|
|
184
|
-
const NETWORK_BACKED_REUSABLE_TOOL_DENYLIST = new Set(["extract_document"]);
|
|
185
|
-
const REQUIRED_REPO_PATH_ARGUMENT_TOOLS = new Set(["read", "ls", "list"]);
|
|
186
|
-
function collectStringValues(value) {
|
|
187
|
-
if (typeof value === "string") {
|
|
188
|
-
return [value];
|
|
189
|
-
}
|
|
190
|
-
if (Array.isArray(value)) {
|
|
191
|
-
return value.flatMap((item) => collectStringValues(item));
|
|
192
|
-
}
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
function isRepoRelativePathArgument(value) {
|
|
196
|
-
const trimmed = value.trim();
|
|
197
|
-
if (trimmed.length === 0) {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
if (trimmed.startsWith("/") ||
|
|
201
|
-
trimmed.startsWith("~") ||
|
|
202
|
-
trimmed.startsWith("\\\\") ||
|
|
203
|
-
/^[A-Za-z]:[\\/]/.test(trimmed)) {
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
return !trimmed.split(/[\\/]+/).includes("..");
|
|
207
|
-
}
|
|
208
|
-
function isGitIgnoredRepoPath(cwd, repoPath) {
|
|
209
|
-
try {
|
|
210
|
-
readGitSnapshotPart(cwd, ["check-ignore", "--quiet", "--", repoPath]);
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
if (typeof error === "object" &&
|
|
215
|
-
error !== null &&
|
|
216
|
-
"status" in error &&
|
|
217
|
-
error.status === 1) {
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
function isToolCallIncludingIgnoredPaths(toolCall) {
|
|
224
|
-
const toolName = toolCall.name.toLowerCase();
|
|
225
|
-
const args = toolCall.arguments;
|
|
226
|
-
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const record = args;
|
|
230
|
-
if (toolName === "status") {
|
|
231
|
-
return record.includeIgnored === true || record.include_ignored === true;
|
|
232
|
-
}
|
|
233
|
-
if (toolName === "search" || toolName === "parallel_ripgrep") {
|
|
234
|
-
return record.useGitIgnore === false || record.use_git_ignore === false;
|
|
235
|
-
}
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
function hasRepoScopedReusableArguments(toolCall, cwd) {
|
|
239
|
-
const toolName = toolCall.name.toLowerCase();
|
|
240
|
-
if (isToolCallIncludingIgnoredPaths(toolCall)) {
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
const argumentNames = REPO_PATH_ARGUMENTS_BY_TOOL.get(toolName) ?? [];
|
|
244
|
-
if (argumentNames.length === 0) {
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
const args = toolCall.arguments;
|
|
248
|
-
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
249
|
-
return !REQUIRED_REPO_PATH_ARGUMENT_TOOLS.has(toolName);
|
|
250
|
-
}
|
|
251
|
-
const pathValues = argumentNames.flatMap((name) => collectStringValues(args[name]));
|
|
252
|
-
if (pathValues.length === 0 &&
|
|
253
|
-
REQUIRED_REPO_PATH_ARGUMENT_TOOLS.has(toolName)) {
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
return pathValues.every((pathValue) => isRepoRelativePathArgument(pathValue) &&
|
|
257
|
-
!isGitIgnoredRepoPath(cwd, pathValue));
|
|
258
|
-
}
|
|
259
|
-
function isGitSnapshotReusableToolCall(tool, toolCall, cwd) {
|
|
260
|
-
if (tool.source !== undefined ||
|
|
261
|
-
tool.annotations?.openWorldHint === true ||
|
|
262
|
-
tool.executionLocation === "client") {
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
if (!GIT_SNAPSHOT_REUSABLE_TOOL_NAMES.has(tool.name.toLowerCase())) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
if (!isReadOnlyTool(tool.name, tool.annotations, tool.source)) {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
return hasRepoScopedReusableArguments(toolCall, cwd);
|
|
272
|
-
}
|
|
273
|
-
const reusableToolDefinitionIdentities = new WeakMap();
|
|
274
|
-
let reusableToolDefinitionIdentityCounter = 0;
|
|
275
|
-
function getReusableToolDefinitionIdentity(value) {
|
|
276
|
-
const existing = reusableToolDefinitionIdentities.get(value);
|
|
277
|
-
if (existing !== undefined) {
|
|
278
|
-
return existing;
|
|
279
|
-
}
|
|
280
|
-
reusableToolDefinitionIdentityCounter += 1;
|
|
281
|
-
reusableToolDefinitionIdentities.set(value, reusableToolDefinitionIdentityCounter);
|
|
282
|
-
return reusableToolDefinitionIdentityCounter;
|
|
283
|
-
}
|
|
284
|
-
function getReusableToolFunctionIdentity(value) {
|
|
285
|
-
return typeof value === "function"
|
|
286
|
-
? getReusableToolDefinitionIdentity(value)
|
|
287
|
-
: undefined;
|
|
288
|
-
}
|
|
289
|
-
function getReusableToolRegistrySignature(tools) {
|
|
290
|
-
return stableStringify(tools.map((tool) => ({
|
|
291
|
-
allowedCallers: tool.allowedCallers,
|
|
292
|
-
annotations: tool.annotations,
|
|
293
|
-
deferApiDefinition: tool.deferApiDefinition,
|
|
294
|
-
description: tool.description,
|
|
295
|
-
executeIdentity: getReusableToolFunctionIdentity(tool.execute),
|
|
296
|
-
executionLocation: tool.executionLocation,
|
|
297
|
-
getActivityDescriptionIdentity: getReusableToolFunctionIdentity(tool.getActivityDescription),
|
|
298
|
-
getDisplayNameIdentity: getReusableToolFunctionIdentity(tool.getDisplayName),
|
|
299
|
-
getToolUseSummaryIdentity: getReusableToolFunctionIdentity(tool.getToolUseSummary),
|
|
300
|
-
inputExamples: tool.inputExamples,
|
|
301
|
-
label: tool.label,
|
|
302
|
-
maxRetries: tool.maxRetries,
|
|
303
|
-
name: tool.name,
|
|
304
|
-
parameters: tool.parameters,
|
|
305
|
-
retryDelayMs: tool.retryDelayMs,
|
|
306
|
-
shouldRetryIdentity: getReusableToolFunctionIdentity(tool.shouldRetry),
|
|
307
|
-
source: tool.source,
|
|
308
|
-
toolIdentity: getReusableToolDefinitionIdentity(tool),
|
|
309
|
-
toolType: tool.toolType,
|
|
310
|
-
})));
|
|
311
|
-
}
|
|
312
|
-
function createToolMetadataCache(tools, reusableToolResultCwd = process.cwd()) {
|
|
313
|
-
const definitions = new Map(tools.map((tool) => [tool.name, tool]));
|
|
314
|
-
return {
|
|
315
|
-
definitions,
|
|
316
|
-
reusableToolResultCwd,
|
|
317
|
-
lookupCount: 0,
|
|
318
|
-
get(toolName) {
|
|
319
|
-
this.lookupCount += 1;
|
|
320
|
-
return definitions.get(toolName);
|
|
321
|
-
},
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
function getToolDefinition(lookup, toolName) {
|
|
325
|
-
if ("get" in lookup && !Array.isArray(lookup)) {
|
|
326
|
-
return lookup.get(toolName);
|
|
327
|
-
}
|
|
328
|
-
return lookup.find((candidate) => candidate.name === toolName);
|
|
329
|
-
}
|
|
330
|
-
function getReusableToolResultCacheKey(toolCall, tools) {
|
|
331
|
-
const tool = getToolDefinition(tools, toolCall.name);
|
|
332
|
-
const cwd = "reusableToolResultCwd" in tools
|
|
333
|
-
? tools.reusableToolResultCwd
|
|
334
|
-
: process.cwd();
|
|
335
|
-
if (!tool || tool.annotations?.destructiveHint === true) {
|
|
336
|
-
return undefined;
|
|
337
|
-
}
|
|
338
|
-
if (!isReadOnlyTool(tool.name, tool.annotations, tool.source)) {
|
|
339
|
-
return undefined;
|
|
340
|
-
}
|
|
341
|
-
const toolName = tool.name.toLowerCase();
|
|
342
|
-
const cacheKey = `${toolCall.name}:${stableStringify(toolCall.arguments)}`;
|
|
343
|
-
if (NETWORK_BACKED_REUSABLE_TOOL_DENYLIST.has(toolName)) {
|
|
344
|
-
return undefined;
|
|
345
|
-
}
|
|
346
|
-
if (isGitSnapshotReusableToolCall(tool, toolCall, cwd)) {
|
|
347
|
-
return `${GIT_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX}${cacheKey}`;
|
|
348
|
-
}
|
|
349
|
-
if (tool.source !== undefined ||
|
|
350
|
-
GIT_SNAPSHOT_REUSABLE_TOOL_NAMES.has(toolName) ||
|
|
351
|
-
tool.annotations?.openWorldHint === true ||
|
|
352
|
-
tool.executionLocation === "client") {
|
|
353
|
-
return undefined;
|
|
354
|
-
}
|
|
355
|
-
return `${RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX}${cacheKey}`;
|
|
356
|
-
}
|
|
357
|
-
function isReadOnlyToolCallForCacheInvalidation(toolCall, tools) {
|
|
358
|
-
const tool = getToolDefinition(tools, toolCall.name);
|
|
359
|
-
return tool
|
|
360
|
-
? isReadOnlyTool(tool.name, tool.annotations, tool.source)
|
|
361
|
-
: false;
|
|
362
|
-
}
|
|
363
|
-
function cloneToolResultForCache(message) {
|
|
364
|
-
return {
|
|
365
|
-
...message,
|
|
366
|
-
content: message.content.map((item) => ({ ...item })),
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function cloneToolOutcomeForCall(outcome, toolCall, timestamp) {
|
|
370
|
-
return {
|
|
371
|
-
message: {
|
|
372
|
-
...outcome.message,
|
|
373
|
-
toolCallId: toolCall.id,
|
|
374
|
-
toolName: toolCall.name,
|
|
375
|
-
content: outcome.message.content.map((item) => ({ ...item })),
|
|
376
|
-
timestamp,
|
|
377
|
-
},
|
|
378
|
-
isError: outcome.isError,
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
function resolvePlatformToolExecutionBridge(option) {
|
|
382
|
-
if (option === false) {
|
|
383
|
-
return undefined;
|
|
384
|
-
}
|
|
385
|
-
return option ?? getDefaultPlatformToolExecutionBridge();
|
|
386
|
-
}
|
|
387
|
-
async function recordReusableToolExecutionBridgeOutput({ bridge, plan, outcome, durationMs, signal, }) {
|
|
388
|
-
if (!bridge || !plan) {
|
|
389
|
-
return outcome;
|
|
390
|
-
}
|
|
391
|
-
const observed = plan.kind === "observe"
|
|
392
|
-
? await bridge.recordObservation(plan, outcome.message, signal)
|
|
393
|
-
: undefined;
|
|
394
|
-
const governedOutput = plan.kind === "governed"
|
|
395
|
-
? await bridge.recordGovernedOutput(plan, outcome.message, durationMs, signal)
|
|
396
|
-
: undefined;
|
|
397
|
-
return {
|
|
398
|
-
...outcome,
|
|
399
|
-
...buildObservedResultMetadata(plan, observed ?? governedOutput),
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
function hasReusableToolResultState(cacheKey, cache, pending, policyCheckedKeys, pendingSafetyChecks) {
|
|
403
|
-
return (cache.has(cacheKey) ||
|
|
404
|
-
pending.has(cacheKey) ||
|
|
405
|
-
policyCheckedKeys.has(cacheKey) ||
|
|
406
|
-
(pendingSafetyChecks.get(cacheKey) ?? 0) > 0);
|
|
407
|
-
}
|
|
408
|
-
function incrementPendingReusableToolSafetyCheck(cacheKey, pendingSafetyChecks) {
|
|
409
|
-
if (!cacheKey) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
pendingSafetyChecks.set(cacheKey, (pendingSafetyChecks.get(cacheKey) ?? 0) + 1);
|
|
413
|
-
}
|
|
414
|
-
function decrementPendingReusableToolSafetyCheck(cacheKey, pendingSafetyChecks) {
|
|
415
|
-
if (!cacheKey) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
const nextCount = (pendingSafetyChecks.get(cacheKey) ?? 0) - 1;
|
|
419
|
-
if (nextCount <= 0) {
|
|
420
|
-
pendingSafetyChecks.delete(cacheKey);
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
pendingSafetyChecks.set(cacheKey, nextCount);
|
|
424
|
-
}
|
|
425
|
-
function clearReusableToolResultState(cache, pending, policyCheckedKeys, pendingSafetyChecks, cacheGeneration) {
|
|
426
|
-
cache.clear();
|
|
427
|
-
pending.clear();
|
|
428
|
-
policyCheckedKeys.clear();
|
|
429
|
-
pendingSafetyChecks.clear();
|
|
430
|
-
cacheGeneration.value += 1;
|
|
431
|
-
}
|
|
432
|
-
function clearRunScopedReusableToolResultState(cache, pending, policyCheckedKeys, pendingSafetyChecks, cacheGeneration) {
|
|
433
|
-
let cleared = false;
|
|
434
|
-
for (const key of cache.keys()) {
|
|
435
|
-
if (key.startsWith(RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX)) {
|
|
436
|
-
cache.delete(key);
|
|
437
|
-
cleared = true;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
for (const key of pending.keys()) {
|
|
441
|
-
if (key.startsWith(RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX)) {
|
|
442
|
-
pending.delete(key);
|
|
443
|
-
cleared = true;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
for (const key of policyCheckedKeys) {
|
|
447
|
-
if (key.startsWith(RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX)) {
|
|
448
|
-
policyCheckedKeys.delete(key);
|
|
449
|
-
cleared = true;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
for (const key of pendingSafetyChecks.keys()) {
|
|
453
|
-
if (key.startsWith(RUN_SCOPED_REUSABLE_TOOL_RESULT_KEY_PREFIX)) {
|
|
454
|
-
pendingSafetyChecks.delete(key);
|
|
455
|
-
cleared = true;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
if (cleared) {
|
|
459
|
-
cacheGeneration.value += 1;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
function invalidateReusableToolResultsAfterMutation(toolCall, tools, cache, pending, policyCheckedKeys, pendingSafetyChecks, cacheGeneration) {
|
|
463
|
-
if (isReadOnlyToolCallForCacheInvalidation(toolCall, tools)) {
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
clearReusableToolResultState(cache, pending, policyCheckedKeys, pendingSafetyChecks, cacheGeneration);
|
|
467
|
-
}
|
|
468
|
-
function hasPendingMutatingToolExecution(pendingExecutions, tools) {
|
|
469
|
-
return pendingExecutions.some((execution) => !isReadOnlyToolCallForCacheInvalidation(execution.toolCall, tools));
|
|
470
|
-
}
|
|
471
|
-
function trackReusableToolResult(cacheKey, executionPromise, cache, pending, policyCheckedKeys, cacheGeneration) {
|
|
472
|
-
const trackedGeneration = cacheGeneration?.value;
|
|
473
|
-
const trackedPromise = executionPromise
|
|
474
|
-
.then((outcome) => {
|
|
475
|
-
if (!outcome.isError &&
|
|
476
|
-
outcome.message.isError !== true &&
|
|
477
|
-
(cacheGeneration === undefined ||
|
|
478
|
-
cacheGeneration.value === trackedGeneration)) {
|
|
479
|
-
cache.set(cacheKey, {
|
|
480
|
-
message: cloneToolResultForCache(outcome.message),
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
policyCheckedKeys?.delete(cacheKey);
|
|
485
|
-
}
|
|
486
|
-
return outcome;
|
|
487
|
-
})
|
|
488
|
-
.catch((error) => {
|
|
489
|
-
policyCheckedKeys?.delete(cacheKey);
|
|
490
|
-
throw error;
|
|
491
|
-
})
|
|
492
|
-
.finally(() => {
|
|
493
|
-
if (pending.get(cacheKey) === trackedPromise) {
|
|
494
|
-
pending.delete(cacheKey);
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
pending.set(cacheKey, trackedPromise);
|
|
498
|
-
return trackedPromise;
|
|
499
|
-
}
|
|
500
71
|
class AgentEventQueue {
|
|
501
72
|
constructor() {
|
|
502
73
|
this.events = [];
|