@sema-agent/core 5.9.0 → 5.11.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 +63 -0
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/send-message-tool.js +6 -0
- package/dist/agents/subagent.d.ts +31 -1
- package/dist/agents/subagent.js +67 -21
- package/dist/agents/teacher.js +15 -3
- package/dist/agents/team.js +10 -0
- package/dist/agents/verify.js +7 -0
- package/dist/brain/anthropic.js +27 -10
- package/dist/brain/open-responses.js +19 -4
- package/dist/brain/openai.js +32 -5
- package/dist/core/a2a.js +1 -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/memory-recall.js +8 -3
- package/dist/core/memory.d.ts +5 -0
- package/dist/core/memory.js +6 -4
- package/dist/core/runner/assemble-result.js +9 -0
- package/dist/core/runner/prepare-task.d.ts +12 -1
- package/dist/core/runner/prepare-task.js +110 -38
- package/dist/core/runner/runtask.d.ts +12 -0
- package/dist/core/runner/runtask.js +169 -37
- package/dist/core/runner/session-file-state-replay.d.ts +7 -0
- package/dist/core/runner/session-file-state-replay.js +56 -0
- package/dist/core/runner/synthetic-tools.js +1 -1
- package/dist/core/runner/tool-disclosure.d.ts +1 -0
- package/dist/core/runner/tool-disclosure.js +24 -9
- package/dist/core/runner/tool-output-projection.js +5 -4
- package/dist/core/runner/turn-attachments.d.ts +2 -0
- package/dist/core/runner/turn-attachments.js +14 -5
- package/dist/core/session-reconcile.d.ts +7 -3
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/strategy-store.d.ts +1 -1
- package/dist/core/strategy-store.js +27 -4
- 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/tools.js +9 -1
- package/dist/core/trace.d.ts +1 -0
- package/dist/core/types.d.ts +8 -1
- package/dist/engine/loop/agent-loop.js +168 -22
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/orchestration/workflow-governance.js +19 -0
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-primitives.js +4 -1
- package/dist/orchestration/workflow.js +1 -1
- package/dist/prompts/coordinator.d.ts +1 -1
- package/dist/prompts/coordinator.js +1 -1
- package/dist/prompts/default.d.ts +1 -0
- package/dist/prompts/default.js +3 -0
- package/dist/stores/file/memory-store.js +3 -7
- package/dist/tools/fs/fs-bash.js +7 -4
- package/dist/tools/fs/fs-shared.d.ts +1 -0
- package/dist/tools/fs/fs-shared.js +4 -0
- 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 -11
- package/package.json +2 -2
|
@@ -57,7 +57,7 @@ import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.j
|
|
|
57
57
|
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
58
58
|
import { createMonitorTool } from "../../tools/monitor.js";
|
|
59
59
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
60
|
-
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
60
|
+
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, seedReadFileStateFromTranscript, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
61
61
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
62
62
|
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool } from "../ask-question.js";
|
|
63
63
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
@@ -67,6 +67,7 @@ import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestrati
|
|
|
67
67
|
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
68
68
|
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
69
69
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
70
|
+
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
70
71
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
|
|
71
72
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
72
73
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
@@ -171,6 +172,11 @@ export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
|
171
172
|
export function checkpointScopeOf(spec) {
|
|
172
173
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
173
174
|
}
|
|
175
|
+
export function resolveCheckpointStore(spec, deps) {
|
|
176
|
+
if (spec.checkpointStore === null)
|
|
177
|
+
return undefined;
|
|
178
|
+
return spec.checkpointStore ?? deps.checkpointStore;
|
|
179
|
+
}
|
|
174
180
|
export function isFableFamilyModelId(id) {
|
|
175
181
|
const tail = id.toLowerCase().split("/").pop() ?? "";
|
|
176
182
|
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
|
|
@@ -313,12 +319,38 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
313
319
|
throw e;
|
|
314
320
|
}
|
|
315
321
|
}
|
|
322
|
+
if (spec.toolMaterializeStrategy !== undefined && spec.toolMaterializeStrategy !== "swap" && spec.toolMaterializeStrategy !== "static") {
|
|
323
|
+
const e = new Error(`toolMaterializeStrategy must be "swap" or "static" (got ${JSON.stringify(spec.toolMaterializeStrategy)}).`);
|
|
324
|
+
e.code = "config.tool_materialize_invalid";
|
|
325
|
+
throw e;
|
|
326
|
+
}
|
|
327
|
+
if (spec.toolMaterializeStrategy === "static" && spec.deferSelfResolve === false) {
|
|
328
|
+
const e = new Error(`toolMaterializeStrategy "static" cannot be combined with deferSelfResolve: false — with the direct-call ` +
|
|
329
|
+
`lane disabled a placeholder is never swapped and never self-resolves, so no deferred tool could ever be ` +
|
|
330
|
+
`called. Use "swap", or leave deferSelfResolve on.`);
|
|
331
|
+
e.code = "config.tool_materialize_unreachable";
|
|
332
|
+
throw e;
|
|
333
|
+
}
|
|
316
334
|
if (resume === undefined && spec.objective.trim().length === 0) {
|
|
317
335
|
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).");
|
|
318
336
|
e.code = "config.empty_objective";
|
|
319
337
|
throw e;
|
|
320
338
|
}
|
|
321
339
|
resolveTaskLimits(spec.limits);
|
|
340
|
+
if (spec.resourceSuspend !== undefined) {
|
|
341
|
+
const rsus = spec.resourceSuspend;
|
|
342
|
+
if (typeof rsus.scope !== "string" || rsus.scope === "") {
|
|
343
|
+
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.scope must be a non-empty string (got ${String(rsus.scope)}) — it is the multi-tenant isolation key every resource checkpoint is filed under.`);
|
|
344
|
+
}
|
|
345
|
+
for (const key of ["totalBudgetUsd", "totalTokens", "maxSlices", "ttlMs"]) {
|
|
346
|
+
const value = rsus[key];
|
|
347
|
+
if (value === undefined)
|
|
348
|
+
continue;
|
|
349
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
350
|
+
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable allocation is not an allocation, and a NaN here blinds even the validated per-slice window.`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
322
354
|
const usageWindows = resolveUsageWindows(deps.usageWindows);
|
|
323
355
|
const brainCallGuardrailRef = {};
|
|
324
356
|
const brainCallGuardrailMs = resolveBrainCallGuardrailMs(spec.limits?.brainCallGuardrailMs ?? deps.brainCallGuardrailMs);
|
|
@@ -411,6 +443,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
411
443
|
let acquired;
|
|
412
444
|
let session;
|
|
413
445
|
let conflictRef;
|
|
446
|
+
let wakeRecovered = [];
|
|
414
447
|
let resumeAtBeforeParentId = null;
|
|
415
448
|
for (let attempt = 0;; attempt++) {
|
|
416
449
|
try {
|
|
@@ -487,7 +520,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
487
520
|
}
|
|
488
521
|
await session.getStorage().setLeafId(spec.resumeAtMode === "before" ? entry.parentId : spec.resumeAt);
|
|
489
522
|
}
|
|
490
|
-
await reconcileInterruptedSession(session, toolEffects, resume?.suspendedBatch);
|
|
523
|
+
wakeRecovered = (await reconcileInterruptedSession(session, toolEffects, resume?.suspendedBatch)).recovered;
|
|
491
524
|
break;
|
|
492
525
|
}
|
|
493
526
|
catch (err) {
|
|
@@ -910,6 +943,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
910
943
|
const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
|
|
911
944
|
const ownSessionRulesRef = {};
|
|
912
945
|
const frozenOnAsk = spec.onAsk ?? deps.onAsk;
|
|
946
|
+
const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
|
|
913
947
|
const inheritedGateForChildren = () => {
|
|
914
948
|
const ancestorRules = [
|
|
915
949
|
...(inheritedAncestorRules ?? []),
|
|
@@ -945,6 +979,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
945
979
|
thinkingLevel: harnessRef.current?.getThinkingLevel(),
|
|
946
980
|
principal: spec.principal,
|
|
947
981
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
982
|
+
...(frozenOnQuestion !== undefined ? { onQuestion: frozenOnQuestion } : {}),
|
|
983
|
+
...(spec.handsReadOnly === true ? { handsReadOnly: true } : {}),
|
|
984
|
+
...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
948
985
|
oneShot: spec.oneShot,
|
|
949
986
|
clientContext: spec.clientContext,
|
|
950
987
|
excludeTools: toolFaceSnapshot.exclude,
|
|
@@ -952,6 +989,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
952
989
|
alwaysLoadTools: toolFaceSnapshot.alwaysLoad,
|
|
953
990
|
promptProfile,
|
|
954
991
|
...(spec.additionalDirectories !== undefined ? { additionalDirectories: Object.freeze([...spec.additionalDirectories]) } : {}),
|
|
992
|
+
...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
|
|
955
993
|
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
956
994
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
957
995
|
parentCwd: taskRootPath,
|
|
@@ -961,6 +999,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
961
999
|
inheritedGateForChildren,
|
|
962
1000
|
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
963
1001
|
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
1002
|
+
...(spec.checkpointStore === null ? { checkpointStoreDisabledForChildren: true } : {}),
|
|
964
1003
|
taskId: hostTaskId,
|
|
965
1004
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
966
1005
|
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
@@ -1014,7 +1053,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1014
1053
|
tools.push(createReportFindingsTool());
|
|
1015
1054
|
}
|
|
1016
1055
|
if (spec.enablePlanMode === true && spec.interactiveTools !== false) {
|
|
1017
|
-
const planReviewFace = (spec
|
|
1056
|
+
const planReviewFace = resolveCheckpointStore(spec, deps) !== undefined;
|
|
1018
1057
|
if (spec.interactiveTools === true || planReviewFace) {
|
|
1019
1058
|
tools.push(defineTool(createPresentPlanTool(requestReview)));
|
|
1020
1059
|
if (planReviewFace) {
|
|
@@ -1125,6 +1164,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1125
1164
|
gates: resume?.priorHumanReview?.gates ? [...resume.priorHumanReview.gates] : [],
|
|
1126
1165
|
};
|
|
1127
1166
|
const priorSuspendCount = resume?.priorSuspendCount ?? 0;
|
|
1167
|
+
const suspendProgressRef = { executedApproved: false };
|
|
1168
|
+
const suspendChainBase = () => (suspendProgressRef.executedApproved ? 0 : priorSuspendCount);
|
|
1128
1169
|
const maxSuspends = spec.maxSuspends ?? deps.maxSuspends ?? DEFAULT_MAX_SUSPENDS;
|
|
1129
1170
|
const maxSlices = spec.resourceSuspend?.maxSlices;
|
|
1130
1171
|
const priorLedger = resume?.priorLedger;
|
|
@@ -1308,34 +1349,40 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1308
1349
|
const callIssuedAtRef = {};
|
|
1309
1350
|
const memoryWriteGateRef = {};
|
|
1310
1351
|
const additionalRootsCanonical = [];
|
|
1352
|
+
const additionalReadRootsCanonical = [];
|
|
1311
1353
|
let attachmentRootCanonical;
|
|
1312
1354
|
if (handsEnabled) {
|
|
1313
1355
|
const rootRaw = taskRootPath;
|
|
1314
1356
|
const canon = await executionEnv.canonicalPath(rootRaw);
|
|
1315
1357
|
const rootCanonical = canon.ok ? canon.value : rootRaw;
|
|
1316
1358
|
attachmentRootCanonical = rootCanonical;
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1359
|
+
const canonicalizeExtraDirs = async (dirs, field, sink) => {
|
|
1360
|
+
for (const dir of dirs ?? []) {
|
|
1361
|
+
if (!dir || !dir.trim())
|
|
1362
|
+
continue;
|
|
1363
|
+
const c = await executionEnv.canonicalPath(dir);
|
|
1364
|
+
if (c.ok) {
|
|
1365
|
+
sink.push(c.value);
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
deps.onError?.(new Error(`${field} entry skipped (cannot canonicalize): ${dir}`), {
|
|
1369
|
+
phase: "config",
|
|
1370
|
+
sessionId,
|
|
1371
|
+
});
|
|
1372
|
+
emitTrace(deps.tracer, () => ({
|
|
1373
|
+
kind: "config.additional_directory_skipped",
|
|
1374
|
+
version: 1,
|
|
1375
|
+
taskId: hostTaskId,
|
|
1376
|
+
entry: dir,
|
|
1377
|
+
...(field !== "additionalDirectories" ? { field } : {}),
|
|
1378
|
+
reason: `${c.error.code}: ${c.error.message}`,
|
|
1379
|
+
ts: Date.now(),
|
|
1380
|
+
}));
|
|
1381
|
+
}
|
|
1337
1382
|
}
|
|
1338
|
-
}
|
|
1383
|
+
};
|
|
1384
|
+
await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
|
|
1385
|
+
await canonicalizeExtraDirs(spec.additionalReadDirectories, "additionalReadDirectories", additionalReadRootsCanonical);
|
|
1339
1386
|
if (spec.envFacts?.scratchpadDir) {
|
|
1340
1387
|
let c = await executionEnv.canonicalPath(spec.envFacts.scratchpadDir);
|
|
1341
1388
|
if (!c.ok && c.error.code === "not_found") {
|
|
@@ -1352,6 +1399,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1352
1399
|
}
|
|
1353
1400
|
const readFileState = new Map((resume?.seed.readFileState ?? []).map(([k, v]) => [rebaseRestoredPath(k), v]));
|
|
1354
1401
|
readFileStateForCheckpoint = readFileState;
|
|
1402
|
+
if (resume === undefined && spec.sessionId !== undefined) {
|
|
1403
|
+
const prior = await session.buildContext().catch(() => undefined);
|
|
1404
|
+
for (const rec of wholeFileRecordsFromTranscript(prior?.messages ?? [])) {
|
|
1405
|
+
const rk = await resolveKey(executionEnv, rootCanonical, rec.path, undefined, undefined, additionalRootsCanonical);
|
|
1406
|
+
if (rk.ok)
|
|
1407
|
+
seedReadFileStateFromTranscript(readFileState, rk.key, rec.content, rec.at);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1355
1410
|
seedContextFiles = async (files) => {
|
|
1356
1411
|
for (const f of files) {
|
|
1357
1412
|
const rk = await resolveKey(executionEnv, rootCanonical, f.path, undefined, undefined, additionalRootsCanonical);
|
|
@@ -1378,6 +1433,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1378
1433
|
}
|
|
1379
1434
|
const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
|
|
1380
1435
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
1436
|
+
...(additionalReadRootsCanonical.length > 0 ? { additionalReadRoots: additionalReadRootsCanonical } : {}),
|
|
1381
1437
|
includeShell: handsIncludeShell,
|
|
1382
1438
|
readOnly: handsReadOnly,
|
|
1383
1439
|
...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
|
|
@@ -1419,7 +1475,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1419
1475
|
irreversibilityTier.set("Bash", shellGate === "always" ? "always" : "maybe");
|
|
1420
1476
|
irreversibleTools.add("Bash");
|
|
1421
1477
|
const shellReadBoundary = () => ({
|
|
1422
|
-
roots: [rootCanonical, ...additionalRootsCanonical],
|
|
1478
|
+
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
1423
1479
|
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
1424
1480
|
});
|
|
1425
1481
|
if (shellGate === "classify")
|
|
@@ -1533,8 +1589,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1533
1589
|
}
|
|
1534
1590
|
if (offloadStore)
|
|
1535
1591
|
tools.push(createReadToolResultTool(offloadStore));
|
|
1536
|
-
const onQuestion =
|
|
1537
|
-
const durableQuestionFace = (spec
|
|
1592
|
+
const onQuestion = frozenOnQuestion;
|
|
1593
|
+
const durableQuestionFace = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
1538
1594
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
|
|
1539
1595
|
if (spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace)))
|
|
1540
1596
|
tools.push(createAskUserQuestionTool(onQuestion, { principal: spec.principal, sourceTaskId: sessionId }));
|
|
@@ -1743,6 +1799,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1743
1799
|
if (additionalRootsCanonical.length > 0) {
|
|
1744
1800
|
envFacts.additionalDirectories = [...additionalRootsCanonical];
|
|
1745
1801
|
}
|
|
1802
|
+
if (additionalReadRootsCanonical.length > 0) {
|
|
1803
|
+
envFacts.additionalReadDirectories = [...additionalReadRootsCanonical];
|
|
1804
|
+
}
|
|
1746
1805
|
try {
|
|
1747
1806
|
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 });
|
|
1748
1807
|
if (probe.ok && probe.value.exitCode === 0) {
|
|
@@ -1987,6 +2046,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1987
2046
|
.filter((s) => s.status === "failed")
|
|
1988
2047
|
.map((s) => ({ name: inlineUntrusted(s.name, 160), ...(s.error !== undefined ? { error: inlineUntrusted(s.error, 240) } : {}) }));
|
|
1989
2048
|
let toolsDeltaRef;
|
|
2049
|
+
let toolMaterializeStatic = false;
|
|
1990
2050
|
if (deferred.size > 0 || failedMcpServers.length > 0) {
|
|
1991
2051
|
toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
|
|
1992
2052
|
}
|
|
@@ -2063,8 +2123,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2063
2123
|
};
|
|
2064
2124
|
offloadReachableToolsRef.current = callableToolNames;
|
|
2065
2125
|
let toolSearch;
|
|
2126
|
+
const envStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
2127
|
+
if (envStrategy !== undefined && envStrategy !== "swap" && envStrategy !== "static") {
|
|
2128
|
+
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(envStrategy)}).`);
|
|
2129
|
+
e.code = "config.tool_materialize_invalid";
|
|
2130
|
+
throw e;
|
|
2131
|
+
}
|
|
2132
|
+
const materializeStatic = (spec.toolMaterializeStrategy ?? envStrategy ?? "static") === "static" && spec.deferSelfResolve !== false;
|
|
2133
|
+
toolMaterializeStatic = materializeStatic;
|
|
2066
2134
|
const buildToolList = (active) => {
|
|
2067
|
-
const list = tools.map((t) => (deferred.has(t.name) && !active.has(t.name) ? placeholders.get(t.name) : t));
|
|
2135
|
+
const list = tools.map((t) => (deferred.has(t.name) && (materializeStatic || !active.has(t.name)) ? placeholders.get(t.name) : t));
|
|
2068
2136
|
list.push(toolSearch);
|
|
2069
2137
|
return list;
|
|
2070
2138
|
};
|
|
@@ -2105,6 +2173,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2105
2173
|
listingRide: (newly) => listingRideRef.current?.(newly),
|
|
2106
2174
|
mountedNames: callableToolNames,
|
|
2107
2175
|
directCallEnabled: spec.deferSelfResolve !== false,
|
|
2176
|
+
...(materializeStatic
|
|
2177
|
+
? { staticSchemaFor: (name) => tools.find((t) => t.name === name)?.parameters }
|
|
2178
|
+
: {}),
|
|
2108
2179
|
serializeActivation,
|
|
2109
2180
|
});
|
|
2110
2181
|
harnessTools = buildToolList(activeTools);
|
|
@@ -2462,7 +2533,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2462
2533
|
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2463
2534
|
return first;
|
|
2464
2535
|
if (pc.durableMandate === true) {
|
|
2465
|
-
if ((spec
|
|
2536
|
+
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2466
2537
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
2467
2538
|
markInheritedUnavailable(creq.toolCallId)) {
|
|
2468
2539
|
return first;
|
|
@@ -2518,7 +2589,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2518
2589
|
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2519
2590
|
return decision;
|
|
2520
2591
|
if (pc.durableMandate === true) {
|
|
2521
|
-
if ((spec
|
|
2592
|
+
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2522
2593
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
2523
2594
|
markInheritedUnavailable(creq.toolCallId)) {
|
|
2524
2595
|
return decision;
|
|
@@ -2678,13 +2749,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2678
2749
|
const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
|
|
2679
2750
|
const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
|
|
2680
2751
|
const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
|
|
2681
|
-
const durableSuspendInfraReady = (spec
|
|
2752
|
+
const durableSuspendInfraReady = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2682
2753
|
!(offloadStore !== undefined && isVolatileOffloadStore(offloadStore)) &&
|
|
2683
2754
|
(ownedEnv === undefined || isRemoteExecutionEnv(ownedEnv)) &&
|
|
2684
2755
|
incompleteSuspendAdapter === undefined;
|
|
2685
2756
|
const resourceSuspendEligible = spec.resourceSuspend !== undefined && durableSuspendInfraReady;
|
|
2686
2757
|
if (spec.resourceSuspend !== undefined && !resourceSuspendEligible) {
|
|
2687
|
-
const why = (spec
|
|
2758
|
+
const why = resolveCheckpointStore(spec, deps) === undefined
|
|
2688
2759
|
? "no CheckpointStore is wired"
|
|
2689
2760
|
: offloadStore !== undefined && isVolatileOffloadStore(offloadStore)
|
|
2690
2761
|
? "tool-result offload uses the in-memory store (a resume needs durable results)"
|
|
@@ -2842,7 +2913,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2842
2913
|
catch {
|
|
2843
2914
|
}
|
|
2844
2915
|
};
|
|
2845
|
-
const checkpointStore = spec
|
|
2916
|
+
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
2846
2917
|
const durableApproval = spec.durableApproval ??
|
|
2847
2918
|
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
2848
2919
|
const inFlightSpendMicroUsd = () => {
|
|
@@ -3049,7 +3120,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3049
3120
|
}
|
|
3050
3121
|
return false;
|
|
3051
3122
|
}
|
|
3052
|
-
if (suspendLoopCapHit(
|
|
3123
|
+
if (suspendLoopCapHit(suspendChainBase(), maxSuspends, " for a plan_review (likely a resume/restart loop)."))
|
|
3053
3124
|
return false;
|
|
3054
3125
|
const leafId = await session.getLeafId();
|
|
3055
3126
|
if (!leafId) {
|
|
@@ -3097,7 +3168,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3097
3168
|
createdAt: mintedAt,
|
|
3098
3169
|
suspendedAt: now(),
|
|
3099
3170
|
deadline: mintedAt + (sanitizedTtlMs(spec.durableApproval?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS),
|
|
3100
|
-
suspendCount:
|
|
3171
|
+
suspendCount: suspendChainBase() + 1,
|
|
3101
3172
|
humanReview: humanReviewRef.count > 0
|
|
3102
3173
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
3103
3174
|
: undefined,
|
|
@@ -3158,7 +3229,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3158
3229
|
if (abortController.signal.aborted) {
|
|
3159
3230
|
return undefined;
|
|
3160
3231
|
}
|
|
3161
|
-
if (suspendLoopCapHit(
|
|
3232
|
+
if (suspendLoopCapHit(suspendChainBase(), maxSuspends, ` for tool "${req.toolName}" — likely a resume/restart loop.`))
|
|
3162
3233
|
return undefined;
|
|
3163
3234
|
if (remoteEnv !== undefined) {
|
|
3164
3235
|
if (hasBackgroundShell(remoteEnv))
|
|
@@ -3181,6 +3252,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3181
3252
|
args: postHookArgs,
|
|
3182
3253
|
safety,
|
|
3183
3254
|
shellGated: (req.toolName === "Bash" && shellGatedBash) || (req.toolName === "Monitor" && shellGatedMonitor),
|
|
3255
|
+
...(effectiveShellGate !== "off" ? { shellGateDoctrine: effectiveShellGate } : {}),
|
|
3184
3256
|
});
|
|
3185
3257
|
gate =
|
|
3186
3258
|
safety !== undefined
|
|
@@ -3241,7 +3313,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3241
3313
|
humanReview: humanReviewRef.count > 0
|
|
3242
3314
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
3243
3315
|
: undefined,
|
|
3244
|
-
suspendCount:
|
|
3316
|
+
suspendCount: suspendChainBase() + 1,
|
|
3245
3317
|
resourceLedger: approvalLedger,
|
|
3246
3318
|
sourceTaskId: sessionId,
|
|
3247
3319
|
...(spec.principal ? { principal: spec.principal } : {}),
|
|
@@ -3602,7 +3674,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3602
3674
|
: undefined;
|
|
3603
3675
|
overheadState.promptChars = systemPrompt.length;
|
|
3604
3676
|
const preparedHolder = {};
|
|
3605
|
-
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, 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 } : {}) });
|
|
3677
|
+
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, 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 } : {}) });
|
|
3606
3678
|
const prepared = buildPrepared();
|
|
3607
3679
|
preparedHolder.current = prepared;
|
|
3608
3680
|
return prepared;
|
|
@@ -4,6 +4,7 @@ import type { RunInternals } from "./prepare-task.js";
|
|
|
4
4
|
import { type TaskOutcome } from "../task-outcome.js";
|
|
5
5
|
import { type SideQuerySpec, type SideQueryResult } from "../side-query.js";
|
|
6
6
|
import type { SessionStore } from "../session.js";
|
|
7
|
+
import { type RecoveredOrphan } from "../session-reconcile.js";
|
|
7
8
|
import type { AgentDefinition, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream } from "../types.js";
|
|
8
9
|
export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
|
|
9
10
|
interface ResumeRun {
|
|
@@ -19,6 +20,17 @@ interface ResumeRun {
|
|
|
19
20
|
onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
|
|
20
21
|
decisionDelivered?: boolean;
|
|
21
22
|
}
|
|
23
|
+
declare function toolEndBodyFrom(result: unknown, isError: boolean): {
|
|
24
|
+
output?: unknown;
|
|
25
|
+
truncated?: boolean;
|
|
26
|
+
totalChars?: number;
|
|
27
|
+
structured?: unknown;
|
|
28
|
+
errorCode?: string;
|
|
29
|
+
};
|
|
30
|
+
export declare function reconciledToolEndBody(orphan: Pick<RecoveredOrphan, "text" | "errorKind">): ReturnType<typeof toolEndBodyFrom>;
|
|
31
|
+
export declare const GOVERNANCE_READ_STALLED: unique symbol;
|
|
32
|
+
export declare function awaitChargeWithSlowDisclosure<T>(charge: Promise<T>, onSlow: () => void, discloseAfterMs?: number): Promise<T>;
|
|
33
|
+
export declare function raceUntilDeadline<T>(p: Promise<T>, deadline: number): Promise<T | typeof GOVERNANCE_READ_STALLED>;
|
|
22
34
|
export declare class Runner {
|
|
23
35
|
private deps;
|
|
24
36
|
readonly sessions: SessionStore;
|