@sema-agent/core 5.10.0 → 5.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/agents/subagent.d.ts +1 -1
- package/dist/agents/subagent.js +6 -2
- package/dist/brain/anthropic.js +1 -1
- package/dist/brain/openai.js +1 -1
- package/dist/core/auto-compaction.d.ts +12 -1
- package/dist/core/auto-compaction.js +3 -1
- package/dist/core/background-agent-store.d.ts +2 -1
- package/dist/core/background-agent-store.js +1 -0
- package/dist/core/checkpoint-store.d.ts +2 -0
- package/dist/core/checkpoint-store.js +1 -0
- package/dist/core/exec-gate.js +12 -1
- package/dist/core/runner/assemble-result.js +9 -0
- package/dist/core/runner/compaction-call-options.d.ts +13 -1
- package/dist/core/runner/compaction-call-options.js +85 -0
- package/dist/core/runner/prepare-task.d.ts +6 -0
- package/dist/core/runner/prepare-task.js +118 -36
- package/dist/core/runner/runtask.js +71 -21
- package/dist/core/runner/tool-disclosure.d.ts +1 -0
- package/dist/core/runner/tool-disclosure.js +24 -9
- package/dist/core/runner/turn-attachments.d.ts +2 -0
- package/dist/core/runner/turn-attachments.js +17 -6
- package/dist/core/task-registry-agent.d.ts +3 -0
- package/dist/core/task-registry-agent.js +9 -2
- package/dist/core/task-registry-shared.d.ts +1 -0
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/trace.d.ts +1 -0
- package/dist/core/types.d.ts +9 -1
- package/dist/engine/compaction/compaction.d.ts +11 -2
- package/dist/engine/compaction/compaction.js +87 -9
- package/dist/index.d.ts +1 -1
- package/dist/prompt-assembly/event-registry.js +1 -1
- package/dist/prompts/default.d.ts +1 -0
- package/dist/prompts/default.js +3 -0
- package/dist/tools/fs/fs-bash.js +4 -1
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +7 -4
- package/dist/tools/web.d.ts +21 -1
- package/dist/tools/web.js +126 -10
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import { resolve as resolveFsPath } from "node:path";
|
|
|
4
4
|
import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, readCompactionActiveTools, summaryOutputBudgetTokens } from "../../internal/harness.js";
|
|
5
5
|
const PROMPT_HASH_SALT = randomBytes(16);
|
|
6
6
|
import { sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
7
|
+
import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
|
|
7
8
|
import { createAutoModeDecider } from "../auto-mode.js";
|
|
8
9
|
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
9
10
|
import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
|
|
@@ -319,6 +320,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
319
320
|
throw e;
|
|
320
321
|
}
|
|
321
322
|
}
|
|
323
|
+
if (spec.toolMaterializeStrategy !== undefined && spec.toolMaterializeStrategy !== "swap" && spec.toolMaterializeStrategy !== "static") {
|
|
324
|
+
const e = new Error(`toolMaterializeStrategy must be "swap" or "static" (got ${JSON.stringify(spec.toolMaterializeStrategy)}).`);
|
|
325
|
+
e.code = "config.tool_materialize_invalid";
|
|
326
|
+
throw e;
|
|
327
|
+
}
|
|
328
|
+
if (spec.toolMaterializeStrategy === "static" && spec.deferSelfResolve === false) {
|
|
329
|
+
const e = new Error(`toolMaterializeStrategy "static" cannot be combined with deferSelfResolve: false — with the direct-call ` +
|
|
330
|
+
`lane disabled a placeholder is never swapped and never self-resolves, so no deferred tool could ever be ` +
|
|
331
|
+
`called. Use "swap", or leave deferSelfResolve on.`);
|
|
332
|
+
e.code = "config.tool_materialize_unreachable";
|
|
333
|
+
throw e;
|
|
334
|
+
}
|
|
322
335
|
if (resume === undefined && spec.objective.trim().length === 0) {
|
|
323
336
|
const e = new Error("TaskSpec.objective is empty — a task needs an instruction (an empty user message is rejected by strict model endpoints and would fail every later request of the session).");
|
|
324
337
|
e.code = "config.empty_objective";
|
|
@@ -977,6 +990,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
977
990
|
alwaysLoadTools: toolFaceSnapshot.alwaysLoad,
|
|
978
991
|
promptProfile,
|
|
979
992
|
...(spec.additionalDirectories !== undefined ? { additionalDirectories: Object.freeze([...spec.additionalDirectories]) } : {}),
|
|
993
|
+
...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
|
|
980
994
|
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
981
995
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
982
996
|
parentCwd: taskRootPath,
|
|
@@ -1151,6 +1165,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1151
1165
|
gates: resume?.priorHumanReview?.gates ? [...resume.priorHumanReview.gates] : [],
|
|
1152
1166
|
};
|
|
1153
1167
|
const priorSuspendCount = resume?.priorSuspendCount ?? 0;
|
|
1168
|
+
const suspendProgressRef = { executedApproved: false };
|
|
1169
|
+
const suspendChainBase = () => (suspendProgressRef.executedApproved ? 0 : priorSuspendCount);
|
|
1154
1170
|
const maxSuspends = spec.maxSuspends ?? deps.maxSuspends ?? DEFAULT_MAX_SUSPENDS;
|
|
1155
1171
|
const maxSlices = spec.resourceSuspend?.maxSlices;
|
|
1156
1172
|
const priorLedger = resume?.priorLedger;
|
|
@@ -1334,34 +1350,40 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1334
1350
|
const callIssuedAtRef = {};
|
|
1335
1351
|
const memoryWriteGateRef = {};
|
|
1336
1352
|
const additionalRootsCanonical = [];
|
|
1353
|
+
const additionalReadRootsCanonical = [];
|
|
1337
1354
|
let attachmentRootCanonical;
|
|
1338
1355
|
if (handsEnabled) {
|
|
1339
1356
|
const rootRaw = taskRootPath;
|
|
1340
1357
|
const canon = await executionEnv.canonicalPath(rootRaw);
|
|
1341
1358
|
const rootCanonical = canon.ok ? canon.value : rootRaw;
|
|
1342
1359
|
attachmentRootCanonical = rootCanonical;
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1360
|
+
const canonicalizeExtraDirs = async (dirs, field, sink) => {
|
|
1361
|
+
for (const dir of dirs ?? []) {
|
|
1362
|
+
if (!dir || !dir.trim())
|
|
1363
|
+
continue;
|
|
1364
|
+
const c = await executionEnv.canonicalPath(dir);
|
|
1365
|
+
if (c.ok) {
|
|
1366
|
+
sink.push(c.value);
|
|
1367
|
+
}
|
|
1368
|
+
else {
|
|
1369
|
+
deps.onError?.(new Error(`${field} entry skipped (cannot canonicalize): ${dir}`), {
|
|
1370
|
+
phase: "config",
|
|
1371
|
+
sessionId,
|
|
1372
|
+
});
|
|
1373
|
+
emitTrace(deps.tracer, () => ({
|
|
1374
|
+
kind: "config.additional_directory_skipped",
|
|
1375
|
+
version: 1,
|
|
1376
|
+
taskId: hostTaskId,
|
|
1377
|
+
entry: dir,
|
|
1378
|
+
...(field !== "additionalDirectories" ? { field } : {}),
|
|
1379
|
+
reason: `${c.error.code}: ${c.error.message}`,
|
|
1380
|
+
ts: Date.now(),
|
|
1381
|
+
}));
|
|
1382
|
+
}
|
|
1363
1383
|
}
|
|
1364
|
-
}
|
|
1384
|
+
};
|
|
1385
|
+
await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
|
|
1386
|
+
await canonicalizeExtraDirs(spec.additionalReadDirectories, "additionalReadDirectories", additionalReadRootsCanonical);
|
|
1365
1387
|
if (spec.envFacts?.scratchpadDir) {
|
|
1366
1388
|
let c = await executionEnv.canonicalPath(spec.envFacts.scratchpadDir);
|
|
1367
1389
|
if (!c.ok && c.error.code === "not_found") {
|
|
@@ -1412,6 +1434,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1412
1434
|
}
|
|
1413
1435
|
const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
|
|
1414
1436
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
1437
|
+
...(additionalReadRootsCanonical.length > 0 ? { additionalReadRoots: additionalReadRootsCanonical } : {}),
|
|
1415
1438
|
includeShell: handsIncludeShell,
|
|
1416
1439
|
readOnly: handsReadOnly,
|
|
1417
1440
|
...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
|
|
@@ -1453,7 +1476,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1453
1476
|
irreversibilityTier.set("Bash", shellGate === "always" ? "always" : "maybe");
|
|
1454
1477
|
irreversibleTools.add("Bash");
|
|
1455
1478
|
const shellReadBoundary = () => ({
|
|
1456
|
-
roots: [rootCanonical, ...additionalRootsCanonical],
|
|
1479
|
+
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
1457
1480
|
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
1458
1481
|
});
|
|
1459
1482
|
if (shellGate === "classify")
|
|
@@ -1777,6 +1800,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1777
1800
|
if (additionalRootsCanonical.length > 0) {
|
|
1778
1801
|
envFacts.additionalDirectories = [...additionalRootsCanonical];
|
|
1779
1802
|
}
|
|
1803
|
+
if (additionalReadRootsCanonical.length > 0) {
|
|
1804
|
+
envFacts.additionalReadDirectories = [...additionalReadRootsCanonical];
|
|
1805
|
+
}
|
|
1780
1806
|
try {
|
|
1781
1807
|
const probe = await executionEnv.exec('uname -s; uname -r; (git rev-parse --is-inside-work-tree 2>/dev/null || echo false); (git symbolic-ref --short -q HEAD 2>/dev/null || echo "HEAD (detached)"); (git rev-parse --show-toplevel 2>/dev/null || echo); (test -n "$(git status --porcelain 2>/dev/null | head -1)" && echo dirty || echo clean); (s=$(ps -p $$ -o comm= 2>/dev/null); s=${s##*/}; echo "${s#-}"); (test "$(git rev-parse --git-dir 2>/dev/null)" != "$(git rev-parse --git-common-dir 2>/dev/null)" && echo linked || echo main); (pwd -P 2>/dev/null || pwd)', { cwd: envFacts.cwd, timeout: 10 });
|
|
1782
1808
|
if (probe.ok && probe.value.exitCode === 0) {
|
|
@@ -2021,6 +2047,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2021
2047
|
.filter((s) => s.status === "failed")
|
|
2022
2048
|
.map((s) => ({ name: inlineUntrusted(s.name, 160), ...(s.error !== undefined ? { error: inlineUntrusted(s.error, 240) } : {}) }));
|
|
2023
2049
|
let toolsDeltaRef;
|
|
2050
|
+
let toolMaterializeStatic = false;
|
|
2024
2051
|
if (deferred.size > 0 || failedMcpServers.length > 0) {
|
|
2025
2052
|
toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
|
|
2026
2053
|
}
|
|
@@ -2097,8 +2124,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2097
2124
|
};
|
|
2098
2125
|
offloadReachableToolsRef.current = callableToolNames;
|
|
2099
2126
|
let toolSearch;
|
|
2127
|
+
const envStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
2128
|
+
if (envStrategy !== undefined && envStrategy !== "swap" && envStrategy !== "static") {
|
|
2129
|
+
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(envStrategy)}).`);
|
|
2130
|
+
e.code = "config.tool_materialize_invalid";
|
|
2131
|
+
throw e;
|
|
2132
|
+
}
|
|
2133
|
+
const materializeStatic = (spec.toolMaterializeStrategy ?? envStrategy ?? "static") === "static" && spec.deferSelfResolve !== false;
|
|
2134
|
+
toolMaterializeStatic = materializeStatic;
|
|
2100
2135
|
const buildToolList = (active) => {
|
|
2101
|
-
const list = tools.map((t) => (deferred.has(t.name) && !active.has(t.name) ? placeholders.get(t.name) : t));
|
|
2136
|
+
const list = tools.map((t) => (deferred.has(t.name) && (materializeStatic || !active.has(t.name)) ? placeholders.get(t.name) : t));
|
|
2102
2137
|
list.push(toolSearch);
|
|
2103
2138
|
return list;
|
|
2104
2139
|
};
|
|
@@ -2139,6 +2174,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2139
2174
|
listingRide: (newly) => listingRideRef.current?.(newly),
|
|
2140
2175
|
mountedNames: callableToolNames,
|
|
2141
2176
|
directCallEnabled: spec.deferSelfResolve !== false,
|
|
2177
|
+
...(materializeStatic
|
|
2178
|
+
? { staticSchemaFor: (name) => tools.find((t) => t.name === name)?.parameters }
|
|
2179
|
+
: {}),
|
|
2142
2180
|
serializeActivation,
|
|
2143
2181
|
});
|
|
2144
2182
|
harnessTools = buildToolList(activeTools);
|
|
@@ -2238,6 +2276,34 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2238
2276
|
const dateChange = renderWithDate
|
|
2239
2277
|
? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
|
|
2240
2278
|
: undefined;
|
|
2279
|
+
const lastBrainContextRef = {};
|
|
2280
|
+
const requestLossyRef = { current: false };
|
|
2281
|
+
const staleOffloadWrittenRefs = new Set();
|
|
2282
|
+
const lastBrainContext = () => lastBrainContextRef.current;
|
|
2283
|
+
const recordBrainContext = (servedModelId, c) => {
|
|
2284
|
+
if (requestLossyRef.current) {
|
|
2285
|
+
lastBrainContextRef.current = undefined;
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
lastBrainContextRef.current = {
|
|
2289
|
+
...(c.systemPrompt !== undefined ? { systemPrompt: c.systemPrompt } : {}),
|
|
2290
|
+
...(c.systemBlocks !== undefined ? { systemBlocks: [...c.systemBlocks] } : {}),
|
|
2291
|
+
messages: [...c.messages],
|
|
2292
|
+
...(c.tools !== undefined ? { tools: [...c.tools] } : {}),
|
|
2293
|
+
modelId: servedModelId,
|
|
2294
|
+
};
|
|
2295
|
+
};
|
|
2296
|
+
const staleOffloadCfg = resolveStaleToolResultOffload(spec.compaction?.staleToolResultOffload);
|
|
2297
|
+
if (staleOffloadCfg !== undefined && offloadStore === undefined) {
|
|
2298
|
+
deps.onError?.(new Error("compaction.staleToolResultOffload is set but the tool-result offload store is disabled (toolResultThresholdChars ≤ 0/∞) — the knob is inert this run; re-enable offloading or drop the knob"), { phase: "config", sessionId });
|
|
2299
|
+
}
|
|
2300
|
+
const staleOffload = staleOffloadCfg !== undefined && offloadStore !== undefined ? { cfg: staleOffloadCfg, store: offloadStore } : undefined;
|
|
2301
|
+
const guardedBrain = brainCallGuardrailMs === undefined
|
|
2302
|
+
? deps.brain
|
|
2303
|
+
: {
|
|
2304
|
+
...deps.brain,
|
|
2305
|
+
stream: withBrainCallGuardrail((m, c, o) => deps.brain.stream(m, c, o), brainCallGuardrailMs, brainCallGuardrailRef),
|
|
2306
|
+
};
|
|
2241
2307
|
const harness = new AgentHarness({
|
|
2242
2308
|
abortResultDetails: () => suspendRef.token !== undefined || reviewRef.token !== undefined ? { code: "gate.parked" } : undefined,
|
|
2243
2309
|
...(spec.limits?.maxOutputTokens !== undefined && spec.limits.maxOutputTokens > 0
|
|
@@ -2271,12 +2337,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2271
2337
|
systemPrompt,
|
|
2272
2338
|
...(assembled.systemBlocks ? { systemBlocks: assembled.systemBlocks } : {}),
|
|
2273
2339
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
2274
|
-
runtime: brainToRuntime(
|
|
2275
|
-
|
|
2276
|
-
: {
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2340
|
+
runtime: brainToRuntime({
|
|
2341
|
+
...guardedBrain,
|
|
2342
|
+
stream: (m, c, o) => {
|
|
2343
|
+
if (staleOffload === undefined) {
|
|
2344
|
+
recordBrainContext(m.id, c);
|
|
2345
|
+
return guardedBrain.stream(m, c, o);
|
|
2346
|
+
}
|
|
2347
|
+
return (async () => {
|
|
2348
|
+
const projected = await projectStaleToolResults(c, staleOffload.cfg, staleOffload.store, sessionId, staleOffloadWrittenRefs);
|
|
2349
|
+
recordBrainContext(m.id, projected);
|
|
2350
|
+
return await guardedBrain.stream(m, projected, o);
|
|
2351
|
+
})();
|
|
2352
|
+
},
|
|
2353
|
+
}),
|
|
2280
2354
|
});
|
|
2281
2355
|
harnessRef.current = harness;
|
|
2282
2356
|
let releaseSignal = () => undefined;
|
|
@@ -3083,7 +3157,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3083
3157
|
}
|
|
3084
3158
|
return false;
|
|
3085
3159
|
}
|
|
3086
|
-
if (suspendLoopCapHit(
|
|
3160
|
+
if (suspendLoopCapHit(suspendChainBase(), maxSuspends, " for a plan_review (likely a resume/restart loop)."))
|
|
3087
3161
|
return false;
|
|
3088
3162
|
const leafId = await session.getLeafId();
|
|
3089
3163
|
if (!leafId) {
|
|
@@ -3131,7 +3205,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3131
3205
|
createdAt: mintedAt,
|
|
3132
3206
|
suspendedAt: now(),
|
|
3133
3207
|
deadline: mintedAt + (sanitizedTtlMs(spec.durableApproval?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS),
|
|
3134
|
-
suspendCount:
|
|
3208
|
+
suspendCount: suspendChainBase() + 1,
|
|
3135
3209
|
humanReview: humanReviewRef.count > 0
|
|
3136
3210
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
3137
3211
|
: undefined,
|
|
@@ -3192,7 +3266,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3192
3266
|
if (abortController.signal.aborted) {
|
|
3193
3267
|
return undefined;
|
|
3194
3268
|
}
|
|
3195
|
-
if (suspendLoopCapHit(
|
|
3269
|
+
if (suspendLoopCapHit(suspendChainBase(), maxSuspends, ` for tool "${req.toolName}" — likely a resume/restart loop.`))
|
|
3196
3270
|
return undefined;
|
|
3197
3271
|
if (remoteEnv !== undefined) {
|
|
3198
3272
|
if (hasBackgroundShell(remoteEnv))
|
|
@@ -3215,6 +3289,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3215
3289
|
args: postHookArgs,
|
|
3216
3290
|
safety,
|
|
3217
3291
|
shellGated: (req.toolName === "Bash" && shellGatedBash) || (req.toolName === "Monitor" && shellGatedMonitor),
|
|
3292
|
+
...(effectiveShellGate !== "off" ? { shellGateDoctrine: effectiveShellGate } : {}),
|
|
3218
3293
|
});
|
|
3219
3294
|
gate =
|
|
3220
3295
|
safety !== undefined
|
|
@@ -3275,7 +3350,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3275
3350
|
humanReview: humanReviewRef.count > 0
|
|
3276
3351
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
3277
3352
|
: undefined,
|
|
3278
|
-
suspendCount:
|
|
3353
|
+
suspendCount: suspendChainBase() + 1,
|
|
3279
3354
|
resourceLedger: approvalLedger,
|
|
3280
3355
|
sourceTaskId: sessionId,
|
|
3281
3356
|
...(spec.principal ? { principal: spec.principal } : {}),
|
|
@@ -3448,7 +3523,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3448
3523
|
: {}),
|
|
3449
3524
|
});
|
|
3450
3525
|
let trimmed = trimToBudget(edited, guardAt, estimateContextTokens(edited, charsPerToken).tokens, charsPerToken);
|
|
3451
|
-
|
|
3526
|
+
const trimDroppedMessages = trimmed.length < edited.length;
|
|
3527
|
+
if (trimDroppedMessages) {
|
|
3452
3528
|
trimPressureRef.droppedMessages = true;
|
|
3453
3529
|
const droppedCount = edited.length - trimmed.length;
|
|
3454
3530
|
trimmed = insertTrimNotice(trimmed);
|
|
@@ -3471,6 +3547,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3471
3547
|
catch {
|
|
3472
3548
|
}
|
|
3473
3549
|
}
|
|
3550
|
+
requestLossyRef.current =
|
|
3551
|
+
capped !== healed ||
|
|
3552
|
+
mediaCapped !== capped ||
|
|
3553
|
+
edited !== mediaCapped ||
|
|
3554
|
+
trimDroppedMessages ||
|
|
3555
|
+
swept.dropped.length > 0;
|
|
3474
3556
|
return { messages: swept.messages };
|
|
3475
3557
|
});
|
|
3476
3558
|
const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
|
|
@@ -3584,7 +3666,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3584
3666
|
: undefined;
|
|
3585
3667
|
const listBackgroundTasks = () => defaultTaskRegistry
|
|
3586
3668
|
.list({ owner: hostTaskId, scope: taskScope, sessionId })
|
|
3587
|
-
.filter((t) => t.status === "pending" || t.status === "running")
|
|
3669
|
+
.filter((t) => t.status === "pending" || t.status === "running" || t.status === "parked")
|
|
3588
3670
|
.map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}), status: t.status }));
|
|
3589
3671
|
const overheadState = { promptChars: 0 };
|
|
3590
3672
|
const centerCompactionCandidate = deps.promptSource !== undefined && centerAdoption !== undefined
|
|
@@ -3636,7 +3718,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3636
3718
|
: undefined;
|
|
3637
3719
|
overheadState.promptChars = systemPrompt.length;
|
|
3638
3720
|
const preparedHolder = {};
|
|
3639
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), ownedEnv, suspendRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3721
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3640
3722
|
const prepared = buildPrepared();
|
|
3641
3723
|
preparedHolder.current = prepared;
|
|
3642
3724
|
return prepared;
|
|
@@ -3,7 +3,7 @@ import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, MAX_S
|
|
|
3
3
|
import { engineVersion } from "../version.js";
|
|
4
4
|
import { CONFIG_CATALOG_VERSION, declarationReasons, resolveEffectiveConfig } from "../../config/catalog.js";
|
|
5
5
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
6
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, isCompactionManualCancel, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
6
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, isCompactionManualCancel, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill, sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
7
7
|
import { ASK_USER_QUESTION_TOOL_NAME, QUESTION_AWAITS_RESUME } from "../ask-question.js";
|
|
8
8
|
import { computeCostMicroUsd, modelCostToPricing } from "../pricing.js";
|
|
9
9
|
import { emitTrace } from "../trace.js";
|
|
@@ -23,7 +23,7 @@ import { OUTPUT_TOOL_NAME, SKILLS_LISTING_PROBE_HEADER, resolveOutputRetries } f
|
|
|
23
23
|
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
24
24
|
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
25
25
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
26
|
-
import { buildWorkingFileAttachments, centerAdoptionOption, emitInputTruncated } from "./compaction-call-options.js";
|
|
26
|
+
import { buildWorkingFileAttachments, centerAdoptionOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
27
27
|
import { prepareTask, resolveCheckpointStore } from "./prepare-task.js";
|
|
28
28
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
29
29
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
@@ -426,6 +426,12 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
426
426
|
if (declaredMaxTurns !== undefined && declaredMaxTurns > 0) {
|
|
427
427
|
ratios.push({ axis: "turn budget", ratio: (stats.turns + 1) / declaredMaxTurns });
|
|
428
428
|
}
|
|
429
|
+
if (walltimeMonotonicDeadline !== undefined) {
|
|
430
|
+
const walltimeWindowMs = walltimeMonotonicDeadline - rs.telemetry.taskStartMonotonic;
|
|
431
|
+
if (walltimeWindowMs > 0) {
|
|
432
|
+
ratios.push({ axis: "walltime budget", ratio: (performance.now() - rs.telemetry.taskStartMonotonic) / walltimeWindowMs });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
429
435
|
let tightest;
|
|
430
436
|
for (const r of ratios)
|
|
431
437
|
if (tightest === undefined || r.ratio > tightest.ratio)
|
|
@@ -490,7 +496,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
490
496
|
rs.attach.attachState.surfacedMtime.delete(p);
|
|
491
497
|
changed = scan.changed;
|
|
492
498
|
}
|
|
493
|
-
const backgroundTasksOn = rs.attach.attachmentsCfg?.backgroundTasks === true;
|
|
499
|
+
const backgroundTasksOn = (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true;
|
|
494
500
|
const bgTasks = backgroundTasksOn && rs.attach.attachState.postCompactPending ? prepared.listBackgroundTasks() : undefined;
|
|
495
501
|
const toolsDeltaOn = rs.attach.attachmentsCfg?.toolsDelta === true;
|
|
496
502
|
const tdRef = toolsDeltaOn ? prepared.toolsDeltaRef : undefined;
|
|
@@ -541,6 +547,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
541
547
|
: {}),
|
|
542
548
|
...(bgTasks !== undefined ? { backgroundTasks: bgTasks } : {}),
|
|
543
549
|
...(pendingTools !== undefined ? { newTools: pendingTools } : {}),
|
|
550
|
+
...(prepared.toolMaterializeStatic ? { newToolsStaticFace: true } : {}),
|
|
544
551
|
...(mcpToolsDelta !== undefined ? { mcpToolsDelta } : {}),
|
|
545
552
|
...(rs.attach.agentListingOn && prepared.agentListing !== undefined
|
|
546
553
|
? {
|
|
@@ -556,7 +563,11 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
556
563
|
...(mcpDropped !== undefined ? { mcpDroppedTools: mcpDropped } : {}),
|
|
557
564
|
}));
|
|
558
565
|
if (pendingTools !== undefined || mcpToolsDelta !== undefined) {
|
|
559
|
-
const exact = renderToolsDelta({
|
|
566
|
+
const exact = renderToolsDelta({
|
|
567
|
+
...(pendingTools !== undefined ? { added: pendingTools } : {}),
|
|
568
|
+
...(prepared.toolMaterializeStatic ? { staticFace: true } : {}),
|
|
569
|
+
...(mcpToolsDelta ?? {}),
|
|
570
|
+
});
|
|
560
571
|
if (exact !== undefined && due.some((a) => a.source === "tools_delta" && a.body === exact)) {
|
|
561
572
|
const ref = prepared.toolsDeltaRef;
|
|
562
573
|
if (pendingTools !== undefined)
|
|
@@ -755,6 +766,7 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
755
766
|
...centerAdoptionOption(prepared),
|
|
756
767
|
model: event.model,
|
|
757
768
|
compactionModel: prepared.compModel,
|
|
769
|
+
...forkContextOption(prepared, false),
|
|
758
770
|
brain: compactionBrain,
|
|
759
771
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
760
772
|
thinking: prepared.thinking,
|
|
@@ -1338,6 +1350,7 @@ export class Runner {
|
|
|
1338
1350
|
return Promise.race([p, timeout]).finally(() => clearTimeout(t));
|
|
1339
1351
|
};
|
|
1340
1352
|
let reapHandle;
|
|
1353
|
+
let steerChain = Promise.resolve();
|
|
1341
1354
|
const notifyRef = {};
|
|
1342
1355
|
const manualCompactRef = { requested: false, waiters: [] };
|
|
1343
1356
|
const drainManualCompactWaiters = (outcome) => {
|
|
@@ -1501,24 +1514,41 @@ export class Runner {
|
|
|
1501
1514
|
return suggestionsDone.catch(() => []);
|
|
1502
1515
|
},
|
|
1503
1516
|
steer: async (text, options) => {
|
|
1504
|
-
if (resultValue)
|
|
1505
|
-
throw steeringError("the task has already finished");
|
|
1506
|
-
const h = handle ?? (await orTimeout(ready));
|
|
1507
|
-
if (!h)
|
|
1508
|
-
throw steeringError("the task is not running");
|
|
1509
1517
|
if (options?.trusted && sanitizeUntrustedText(text) !== text) {
|
|
1510
1518
|
throw steeringError("trusted steering text must not contain a </system-reminder> tag", "steering.invalid_content");
|
|
1511
1519
|
}
|
|
1512
1520
|
const payload = options?.trusted ? formatHookFeedback(text) : text;
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
if (
|
|
1518
|
-
throw steeringError("the task is
|
|
1521
|
+
const deliver = async () => {
|
|
1522
|
+
if (resultValue)
|
|
1523
|
+
throw steeringError("the task has already finished");
|
|
1524
|
+
const h = handle ?? (await orTimeout(ready));
|
|
1525
|
+
if (!h)
|
|
1526
|
+
throw steeringError("the task is not running");
|
|
1527
|
+
try {
|
|
1528
|
+
await h.harness.steer(payload, { provenance: "engine-note" });
|
|
1529
|
+
return;
|
|
1519
1530
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1531
|
+
catch (e) {
|
|
1532
|
+
if (!(e instanceof Error && e.code === "invalid_state"))
|
|
1533
|
+
throw e;
|
|
1534
|
+
}
|
|
1535
|
+
const birthDeadline = Date.now() + READY_TIMEOUT_MS;
|
|
1536
|
+
while (resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
|
|
1537
|
+
try {
|
|
1538
|
+
await h.harness.steer(payload, { provenance: "engine-note" });
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
catch (e2) {
|
|
1542
|
+
if (!(e2 instanceof Error && e2.code === "invalid_state"))
|
|
1543
|
+
throw e2;
|
|
1544
|
+
}
|
|
1545
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
1546
|
+
}
|
|
1547
|
+
throw steeringError("the task is no longer running");
|
|
1548
|
+
};
|
|
1549
|
+
const p = steerChain.then(deliver);
|
|
1550
|
+
steerChain = p.then(() => undefined, () => undefined);
|
|
1551
|
+
return p;
|
|
1522
1552
|
},
|
|
1523
1553
|
notify: async (input, opts) => {
|
|
1524
1554
|
const notifyError = (msg, code) => {
|
|
@@ -1753,7 +1783,8 @@ export class Runner {
|
|
|
1753
1783
|
}
|
|
1754
1784
|
}
|
|
1755
1785
|
}
|
|
1756
|
-
|
|
1786
|
+
const loopLatch = { ended: false };
|
|
1787
|
+
onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch });
|
|
1757
1788
|
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
|
|
1758
1789
|
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
|
|
1759
1790
|
if (resume &&
|
|
@@ -1903,7 +1934,8 @@ export class Runner {
|
|
|
1903
1934
|
rs.attach.agentListingOn = rs.attach.attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
|
|
1904
1935
|
rs.attach.skillsListingOn = rs.attach.attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
|
|
1905
1936
|
const listingsLive = (rs.attach.agentListingOn && prepared.agentListing !== undefined) || (rs.attach.skillsListingOn && prepared.skillsListing !== undefined);
|
|
1906
|
-
|
|
1937
|
+
const backgroundTasksLive = (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true;
|
|
1938
|
+
rs.attach.attachState = rs.attach.attachmentsCfg !== undefined || listingsLive || backgroundTasksLive ? createAttachmentState() : undefined;
|
|
1907
1939
|
rs.attach.dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
|
|
1908
1940
|
rs.attach.instrProbe = this.deps.probeInstructionSources;
|
|
1909
1941
|
rs.attach.instrState =
|
|
@@ -1916,7 +1948,7 @@ export class Runner {
|
|
|
1916
1948
|
: undefined;
|
|
1917
1949
|
rs.counters.cadenceTurns = 0;
|
|
1918
1950
|
rs.turn.lastTurnHadToolCalls = false;
|
|
1919
|
-
if (rs.attach.attachState !== undefined && rs.attach.attachmentsCfg?.backgroundTasks === true) {
|
|
1951
|
+
if (rs.attach.attachState !== undefined && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
1920
1952
|
try {
|
|
1921
1953
|
const branch = await prepared.session.getBranch();
|
|
1922
1954
|
for (let i = branch.length - 1; i >= 0; i--) {
|
|
@@ -2240,6 +2272,19 @@ export class Runner {
|
|
|
2240
2272
|
};
|
|
2241
2273
|
const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
|
|
2242
2274
|
const compactionBreaker = { failures: 0 };
|
|
2275
|
+
if (spec.compaction?.enabled ?? true) {
|
|
2276
|
+
const prefixWindow = prepared.model.autoCompactTokens ?? prepared.model.contextTokens ?? prepared.model.contextWindow;
|
|
2277
|
+
if (Number.isFinite(prefixWindow) && prefixWindow > 0) {
|
|
2278
|
+
const prefixSettings = sanitizeCompactionSettings({ ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction }, prefixWindow);
|
|
2279
|
+
const prefixCompactAt = prefixWindow - prefixSettings.reserveTokens;
|
|
2280
|
+
if (prepared.promptOverheadTokens >= prefixCompactAt) {
|
|
2281
|
+
this.deps.onError?.(new Error(`compaction cannot help: the fixed request prefix (system prompt + tool schemas, ≈${prepared.promptOverheadTokens} tokens) ` +
|
|
2282
|
+
`already meets or exceeds the compaction threshold (${prefixCompactAt} of a ${prefixWindow}-token window). ` +
|
|
2283
|
+
`Compaction only shrinks conversation history, so this run will re-trigger or overflow regardless — ` +
|
|
2284
|
+
`shrink the system prompt/tool surface or use a larger-window model.`), { phase: "config", sessionId: prepared.sessionId });
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2243
2288
|
const windowSafetyOptions = (mainModel) => ({
|
|
2244
2289
|
...(rs.budget.maxCostMicroUsd !== undefined
|
|
2245
2290
|
? {
|
|
@@ -2409,6 +2454,7 @@ export class Runner {
|
|
|
2409
2454
|
...centerAdoptionOption(prepared),
|
|
2410
2455
|
model: prepared.harness.getModel(),
|
|
2411
2456
|
compactionModel: prepared.compModel,
|
|
2457
|
+
...forkContextOption(prepared, true),
|
|
2412
2458
|
brain: compactionBrain,
|
|
2413
2459
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
2414
2460
|
thinking: prepared.thinking,
|
|
@@ -2705,9 +2751,11 @@ export class Runner {
|
|
|
2705
2751
|
}));
|
|
2706
2752
|
}
|
|
2707
2753
|
}
|
|
2754
|
+
loopLatch.ended = true;
|
|
2708
2755
|
abortedLive = prepared.abortController.signal.aborted;
|
|
2709
2756
|
}
|
|
2710
2757
|
catch (err) {
|
|
2758
|
+
loopLatch.ended = true;
|
|
2711
2759
|
if (errorCodeOf(err) === "resume.tool_unavailable") {
|
|
2712
2760
|
await settleTeardownLeg(() => prepared.mcp.dispose(), "mcp.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
2713
2761
|
await settleTeardownLeg(() => prepared.a2a?.dispose(), "a2a.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
@@ -2775,7 +2823,7 @@ export class Runner {
|
|
|
2775
2823
|
});
|
|
2776
2824
|
}
|
|
2777
2825
|
}
|
|
2778
|
-
if (rs.attach.attachState?.postCompactPending === true && rs.attach.attachmentsCfg?.backgroundTasks === true) {
|
|
2826
|
+
if (rs.attach.attachState?.postCompactPending === true && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
2779
2827
|
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
2780
2828
|
}
|
|
2781
2829
|
const comp = await this.finish(spec, prepared, {
|
|
@@ -3512,6 +3560,7 @@ export class Runner {
|
|
|
3512
3560
|
}
|
|
3513
3561
|
const args = resolvedArgs;
|
|
3514
3562
|
onExecuteStart?.(pendingAction.toolCallId);
|
|
3563
|
+
prepared.suspendProgressRef.executedApproved = true;
|
|
3515
3564
|
let res;
|
|
3516
3565
|
try {
|
|
3517
3566
|
res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
|
|
@@ -3622,6 +3671,7 @@ export class Runner {
|
|
|
3622
3671
|
...centerAdoptionOption(prepared),
|
|
3623
3672
|
model: prepared.model,
|
|
3624
3673
|
compactionModel: prepared.compModel,
|
|
3674
|
+
...forkContextOption(prepared, false),
|
|
3625
3675
|
brain: opts?.brain ?? this.deps.brain,
|
|
3626
3676
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
3627
3677
|
thinking: prepared.thinking,
|
|
@@ -54,4 +54,5 @@ export declare function createToolSearchTool(opts: {
|
|
|
54
54
|
mountedNames?: () => ReadonlySet<string>;
|
|
55
55
|
directCallEnabled?: boolean;
|
|
56
56
|
serializeActivation?: <T>(section: () => Promise<T>) => Promise<T>;
|
|
57
|
+
staticSchemaFor?: (name: string) => TSchema | undefined;
|
|
57
58
|
}): AgentTool;
|
|
@@ -80,7 +80,7 @@ export function createPlaceholderTool(info, direct) {
|
|
|
80
80
|
};
|
|
81
81
|
const invalidArgumentsRejection = (target, params, schemaJson, ride) => {
|
|
82
82
|
const text = `Invalid arguments for \`${sn}\`: ${formatZodValidationError(target.parameters, params)} ` +
|
|
83
|
-
`\`${sn}\` is now active — its full parameter schema is below
|
|
83
|
+
`\`${sn}\` is now active — its full parameter schema is below; use it for this and later calls. ` +
|
|
84
84
|
`Call \`${sn}\` again with arguments matching it.\nParameter schema: ${schemaJson}`;
|
|
85
85
|
return {
|
|
86
86
|
content: ride === undefined || ride === "" ? [{ type: "text", text }] : [{ type: "text", text }, { type: "text", text: ride }],
|
|
@@ -236,11 +236,15 @@ export function extractDiscoveredToolNames(messages, registry) {
|
|
|
236
236
|
return [...names];
|
|
237
237
|
}
|
|
238
238
|
export function createToolSearchTool(opts) {
|
|
239
|
-
const { registry, active, rematerialize, listingRide, mountedNames } = opts;
|
|
239
|
+
const { registry, active, rematerialize, listingRide, mountedNames, staticSchemaFor } = opts;
|
|
240
240
|
const directCallEnabled = opts.directCallEnabled !== false;
|
|
241
241
|
const activationPosture = directCallEnabled
|
|
242
|
-
?
|
|
243
|
-
"
|
|
242
|
+
? (staticSchemaFor !== undefined
|
|
243
|
+
? "Most tools start as name-only placeholders to keep requests small; activating one here returns its full " +
|
|
244
|
+
"parameter schema in the result (the tools list keeps the compact placeholder entry). "
|
|
245
|
+
: "Most tools start as name-only placeholders to keep requests small; activating one here loads its full " +
|
|
246
|
+
"parameter schema. ") +
|
|
247
|
+
"Until you have that schema you cannot reliably form a call, so activate a tool rather " +
|
|
244
248
|
"than guessing its arguments — a call that does match the real schema executes and activates the tool. " +
|
|
245
249
|
"When any instruction, reminder, or another tool's description names a deferred tool, activate it here " +
|
|
246
250
|
'with query "select:<name>". '
|
|
@@ -265,7 +269,9 @@ export function createToolSearchTool(opts) {
|
|
|
265
269
|
"a bare tool name — activates that tool directly. " +
|
|
266
270
|
"Activate every tool you expect to need in one call (select accepts a comma-separated list) " +
|
|
267
271
|
"rather than one at a time. " +
|
|
268
|
-
|
|
272
|
+
(staticSchemaFor !== undefined
|
|
273
|
+
? "Activation returns each tool's full parameter schema in this result — call the tool directly with arguments matching it."
|
|
274
|
+
: "Activated tools become callable with their full parameters on your next turn."),
|
|
269
275
|
parameters: Type.Object({
|
|
270
276
|
query: Type.Optional(Type.String({
|
|
271
277
|
description: 'Query to find deferred tools. Use "select:<tool_name>" for direct selection, or keywords to search.',
|
|
@@ -329,11 +335,20 @@ export function createToolSearchTool(opts) {
|
|
|
329
335
|
const lines = matched.map((n) => {
|
|
330
336
|
const info = registry.get(n);
|
|
331
337
|
const tag = newly.includes(n) ? "activated" : "already active";
|
|
332
|
-
|
|
338
|
+
const base = `- ${safeName(n)} (${tag})${info ? ` — ${info.hint}` : ""}`;
|
|
339
|
+
if (staticSchemaFor === undefined)
|
|
340
|
+
return base;
|
|
341
|
+
const schema = staticSchemaFor(n);
|
|
342
|
+
const json = schema === undefined ? undefined : renderSchemaForModel(schema);
|
|
343
|
+
return json === undefined ? base : `${base}\n parameters: ${json}`;
|
|
333
344
|
});
|
|
334
|
-
const head =
|
|
335
|
-
?
|
|
336
|
-
|
|
345
|
+
const head = staticSchemaFor !== undefined
|
|
346
|
+
? newly.length > 0
|
|
347
|
+
? `Activated ${newly.length} tool(s) — call them directly with arguments matching the parameter schemas below (the tools list keeps compact placeholder entries):`
|
|
348
|
+
: "These tools are already active — call them directly; their parameter schemas are repeated below:"
|
|
349
|
+
: newly.length > 0
|
|
350
|
+
? `Activated ${newly.length} tool(s); they are now available with full parameters — call them directly:`
|
|
351
|
+
: "These tools are already active — call them directly:";
|
|
337
352
|
return {
|
|
338
353
|
content: `${head}\n${lines.join("\n")}${missingNote}${ride !== undefined ? `\n\n${ride}` : ""}`,
|
|
339
354
|
details: {
|
|
@@ -100,6 +100,7 @@ export interface AttachmentInputs {
|
|
|
100
100
|
}>;
|
|
101
101
|
backgroundTasks?: ReadonlyArray<BackgroundTaskSnapshot>;
|
|
102
102
|
newTools?: readonly string[];
|
|
103
|
+
newToolsStaticFace?: boolean;
|
|
103
104
|
mcpToolsDelta?: McpToolsDeltaFacts;
|
|
104
105
|
agentListing?: ReadonlyArray<AgentListingEntry>;
|
|
105
106
|
agentToolName?: string;
|
|
@@ -148,6 +149,7 @@ export interface McpToolsDeltaFacts {
|
|
|
148
149
|
}
|
|
149
150
|
export declare function renderToolsDelta(input: {
|
|
150
151
|
added?: readonly string[];
|
|
152
|
+
staticFace?: boolean;
|
|
151
153
|
} & McpToolsDeltaFacts): string | undefined;
|
|
152
154
|
export declare const AGENT_TOOLS_NOTE_DEFAULT = "All tools";
|
|
153
155
|
export declare const AGENT_CONCURRENCY_NOTE = "When you launch multiple agents for independent work, send them in a single message with multiple tool uses so they run concurrently.";
|