@sema-agent/core 5.65.0 → 6.0.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 +46 -0
- package/dist/agents/subagent.d.ts +2 -2
- package/dist/agents/subagent.js +11 -0
- package/dist/agents/verify.d.ts +1 -1
- package/dist/brain/anthropic.js +1 -1
- package/dist/brain/errors.d.ts +29 -0
- package/dist/brain/errors.js +20 -0
- package/dist/brain/open-responses.js +2 -2
- package/dist/brain/route-adjudicator.d.ts +8 -1
- package/dist/brain/route-adjudicator.js +1 -0
- package/dist/brain/status-sink.js +12 -1
- package/dist/brain/stream-engine.js +17 -6
- package/dist/core/auto-compaction.d.ts +26 -0
- package/dist/core/auto-compaction.js +7 -2
- package/dist/core/auto-mode-arming.d.ts +138 -0
- package/dist/core/auto-mode-arming.js +181 -0
- package/dist/core/auto-mode-defaults.d.ts +13 -0
- package/dist/core/auto-mode-defaults.js +5 -0
- package/dist/core/auto-mode-prompt.d.ts +14 -3
- package/dist/core/auto-mode-prompt.js +10 -7
- package/dist/core/auto-mode-rebuild.d.ts +75 -0
- package/dist/core/auto-mode-rebuild.js +41 -0
- package/dist/core/auto-mode.d.ts +15 -0
- package/dist/core/auto-mode.js +4 -2
- package/dist/core/checkpoint-store.d.ts +18 -0
- package/dist/core/context-edit.d.ts +47 -5
- package/dist/core/context-guard.d.ts +1 -1
- package/dist/core/file-history-retention.d.ts +106 -0
- package/dist/core/file-history-retention.js +36 -0
- package/dist/core/file-history-store.d.ts +768 -0
- package/dist/core/file-history-store.js +880 -0
- package/dist/core/governance-codes.d.ts +2 -1
- package/dist/core/governance-codes.js +14 -0
- package/dist/core/hooks.d.ts +39 -7
- package/dist/core/hooks.js +38 -21
- package/dist/core/lsp.d.ts +2 -2
- package/dist/core/mcp.d.ts +29 -7
- package/dist/core/memory-engine/consolidation-driver.d.ts +11 -0
- package/dist/core/memory-engine/consolidation-driver.js +71 -4
- package/dist/core/memory-engine/consolidation.d.ts +25 -2
- package/dist/core/memory-engine/consolidation.js +4 -1
- package/dist/core/memory-engine/distiller.d.ts +84 -1
- package/dist/core/memory-engine/distiller.js +68 -0
- package/dist/core/memory-engine/dual-root.js +3 -0
- package/dist/core/memory-engine/engine.d.ts +328 -15
- package/dist/core/memory-engine/engine.js +355 -29
- package/dist/core/memory-engine/file-backend.d.ts +30 -0
- package/dist/core/memory-engine/file-backend.js +14 -13
- package/dist/core/memory-engine/frontmatter.d.ts +22 -1
- package/dist/core/memory-engine/frontmatter.js +3 -0
- package/dist/core/memory-engine/header-hints.d.ts +5 -0
- package/dist/core/memory-engine/index.d.ts +5 -4
- package/dist/core/memory-engine/index.js +5 -4
- package/dist/core/memory-engine/layout.d.ts +88 -2
- package/dist/core/memory-engine/layout.js +112 -3
- package/dist/core/memory-engine/provenance-wording.d.ts +7 -0
- package/dist/core/memory-engine/provenance-wording.js +3 -0
- package/dist/core/memory-engine/tools.d.ts +89 -8
- package/dist/core/memory-engine/tools.js +263 -22
- package/dist/core/memory-engine/types.d.ts +64 -1
- package/dist/core/memory-recall.d.ts +6 -0
- package/dist/core/memory.d.ts +27 -1
- package/dist/core/memory.js +16 -2
- package/dist/core/permission-rule-consent.d.ts +20 -0
- package/dist/core/permission-rule-consent.js +12 -3
- package/dist/core/permission-rule-model.d.ts +67 -7
- package/dist/core/permission-rule-model.js +53 -7
- package/dist/core/permission-rule-store.js +15 -10
- package/dist/core/permission-rule-sync.js +15 -11
- package/dist/core/retention-policy.d.ts +9 -0
- package/dist/core/retention-policy.js +5 -2
- package/dist/core/retention.d.ts +13 -2
- package/dist/core/runner/assemble-result.d.ts +19 -1
- package/dist/core/runner/assemble-result.js +17 -2
- package/dist/core/runner/compaction-call-options.d.ts +93 -0
- package/dist/core/runner/compaction-call-options.js +3 -0
- package/dist/core/runner/memory-capture-optout.d.ts +80 -0
- package/dist/core/runner/memory-capture-optout.js +53 -0
- package/dist/core/runner/prepare-config-doors.d.ts +5 -0
- package/dist/core/runner/prepare-config-doors.js +16 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +110 -5
- package/dist/core/runner/prepare-hands-readface.js +99 -7
- package/dist/core/runner/prepare-memory.d.ts +88 -0
- package/dist/core/runner/prepare-memory.js +305 -24
- package/dist/core/runner/prepare-task.d.ts +141 -1
- package/dist/core/runner/prepare-task.js +443 -79
- package/dist/core/runner/runtask.d.ts +9 -20
- package/dist/core/runner/runtask.js +133 -96
- package/dist/core/runner/session-file-state-replay.d.ts +18 -10
- package/dist/core/runner/session-file-state-replay.js +52 -1
- package/dist/core/runner/tool-disclosure.js +2 -1
- package/dist/core/runner/turn-attachments.d.ts +22 -12
- package/dist/core/session-store.d.ts +1 -1
- package/dist/core/session-store.js +6 -1
- package/dist/core/session.d.ts +34 -1
- package/dist/core/store-contracts/file-history-store-contract.d.ts +3 -0
- package/dist/core/store-contracts/file-history-store-contract.js +720 -0
- package/dist/core/task-registry-shared.js +11 -1
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +172 -1
- package/dist/core/tool-policy.js +32 -1
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/trace.d.ts +24 -0
- package/dist/core/types.d.ts +784 -89
- package/dist/core/types.js +4 -3
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/untrusted-text.js +8 -0
- package/dist/core/workflow-run-store-contract.js +6 -2
- package/dist/core/workflow-run-store.d.ts +4 -1
- package/dist/engine/compaction/compaction.d.ts +88 -10
- package/dist/engine/compaction/compaction.js +109 -30
- package/dist/engine/execution-env/node-execution-env.d.ts +9 -1
- package/dist/engine/execution-env/node-execution-env.js +28 -0
- package/dist/engine/harness/agent-harness.d.ts +52 -1
- package/dist/engine/harness/agent-harness.js +36 -1
- package/dist/engine/harness/types.d.ts +26 -1
- package/dist/engine/llm/types.d.ts +50 -4
- package/dist/engine/loop/agent-loop.d.ts +5 -1
- package/dist/engine/loop/agent-loop.js +25 -0
- package/dist/engine/loop/types.d.ts +19 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
- package/dist/engine/session/session.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +14 -6
- package/dist/orchestration/run-workflow-tool.d.ts +20 -2
- package/dist/orchestration/run-workflow-tool.js +22 -3
- package/dist/orchestration/workflow-governance.d.ts +59 -1
- package/dist/orchestration/workflow-governance.js +61 -8
- package/dist/orchestration/workflow-meta.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +56 -13
- package/dist/orchestration/workflow-types.d.ts +78 -2
- package/dist/orchestration/workflow.d.ts +20 -0
- package/dist/orchestration/workflow.js +163 -14
- package/dist/prompt-assembly/event-registry.js +1 -1
- package/dist/prompts/default.d.ts +7 -7
- package/dist/stores/file/file-history-store.d.ts +368 -0
- package/dist/stores/file/file-history-store.js +1248 -0
- package/dist/stores/file/index.d.ts +22 -13
- package/dist/stores/file/index.js +4 -4
- package/dist/stores/file/permission-rule-store.js +1 -0
- package/dist/stores/file/strategy-store.d.ts +3 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +87 -3
- package/dist/tools/fs/bash-readonly-classifier.js +106 -4
- package/dist/tools/fs/fs-bash.js +9 -5
- package/dist/tools/fs/fs-shared.d.ts +52 -1
- package/dist/tools/fs/fs-shared.js +14 -0
- package/dist/tools/fs/fs-write.d.ts +5 -5
- package/dist/tools/fs/fs-write.js +71 -14
- package/dist/tools/fs/index.d.ts +6 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/web.js +2 -1
- package/package.json +5 -1
- package/test/export-surface.snapshot.json +155 -23
- package/dist/core/file-snapshot-store.d.ts +0 -165
- package/dist/core/file-snapshot-store.js +0 -259
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +0 -13
- package/dist/core/store-contracts/file-snapshot-store-contract.js +0 -134
- package/dist/stores/file/file-snapshot-store.d.ts +0 -58
- package/dist/stores/file/file-snapshot-store.js +0 -353
|
@@ -6,6 +6,7 @@ const PROMPT_HASH_SALT = randomBytes(16);
|
|
|
6
6
|
import { sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
7
7
|
import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
|
|
8
8
|
import { createAutoModeDecider } from "../auto-mode.js";
|
|
9
|
+
import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
|
|
9
10
|
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
10
11
|
import { resolveTaskModel } from "../roles.js";
|
|
11
12
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
@@ -20,7 +21,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial, resolveDelegationEn
|
|
|
20
21
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
21
22
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
22
23
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
23
|
-
import { askApproverIdentity, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest,
|
|
24
|
+
import { askApproverIdentity, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
24
25
|
const PERSISTED_RULE_TOOL = "Bash";
|
|
25
26
|
import { findAdmittingRule, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
26
27
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
@@ -49,7 +50,6 @@ import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDisco
|
|
|
49
50
|
import { createSharedMemoryTools } from "../shared-memory/tools.js";
|
|
50
51
|
import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
|
|
51
52
|
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
|
|
52
|
-
import { memoryRecallDisciplineSegment } from "../memory-engine/engine.js";
|
|
53
53
|
import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
|
|
54
54
|
import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
|
|
55
55
|
import { recordSyncUnattestable, sessionSettlements } from "../memory-engine/delegation-settlement.js";
|
|
@@ -97,6 +97,7 @@ import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDec
|
|
|
97
97
|
import { durableParkGapFor } from "../park-selfcheck.js";
|
|
98
98
|
import { GLOBAL_USAGE_KEY, usageRetryAfterMs, windowsGovernCost } from "../usage-window-store.js";
|
|
99
99
|
import { deliverEngineNotice } from "../types.js";
|
|
100
|
+
import { resolveTrackKey } from "../file-history-store.js";
|
|
100
101
|
let announcedMaterializeEnvBySink = new WeakMap();
|
|
101
102
|
const announcedMaterializeEnvConsole = new Set();
|
|
102
103
|
function materializeEnvLedger(onNotice) {
|
|
@@ -218,6 +219,7 @@ function announceDeclaredMcpContentClasses(args) {
|
|
|
218
219
|
toolCount,
|
|
219
220
|
...(declaredClass === "execution" ? { execIsExternalContent: args.execIsExternalContent } : {}),
|
|
220
221
|
sessionId: args.sessionId,
|
|
222
|
+
runId: args.runId,
|
|
221
223
|
},
|
|
222
224
|
});
|
|
223
225
|
}
|
|
@@ -324,7 +326,7 @@ class ParkRefusal extends Error {
|
|
|
324
326
|
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
325
327
|
export { isFableFamilyModelId, resolveAttachmentsConfig, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
|
|
326
328
|
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./prepare-workspace-restore.js";
|
|
327
|
-
function buildMicroCompactState(deps, model, sessionId, offloadStore, knob) {
|
|
329
|
+
function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, knob) {
|
|
328
330
|
const triggerWindow = resolveTriggerWindow(model);
|
|
329
331
|
if (triggerWindow.clamped) {
|
|
330
332
|
deliverEngineNotice(deps.onNotice, {
|
|
@@ -337,6 +339,7 @@ function buildMicroCompactState(deps, model, sessionId, offloadStore, knob) {
|
|
|
337
339
|
declaredAutoCompactTokens: triggerWindow.declaredAutoCompactTokens,
|
|
338
340
|
physicalWindow: triggerWindow.physicalWindow,
|
|
339
341
|
sessionId,
|
|
342
|
+
runId,
|
|
340
343
|
},
|
|
341
344
|
});
|
|
342
345
|
}
|
|
@@ -433,6 +436,187 @@ export async function runGuardChain(args) {
|
|
|
433
436
|
const t = applyGuardTrim(working, anchoredWorking);
|
|
434
437
|
return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
|
|
435
438
|
}
|
|
439
|
+
function restoredAbsPathsOf(restored, root) {
|
|
440
|
+
const out = [];
|
|
441
|
+
for (const key of [...restored.filesChanged, ...restored.failed.map((f) => f.path)]) {
|
|
442
|
+
const abs = resolveTrackKey(root, key);
|
|
443
|
+
if (abs.ok)
|
|
444
|
+
out.push(abs.abs);
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
}
|
|
448
|
+
function resolveWiredFileHistoryStore(candidate) {
|
|
449
|
+
if (candidate === undefined)
|
|
450
|
+
return undefined;
|
|
451
|
+
const verbs = ["trackEdit", "annulTrack", "snapshot", "restore", "canRestore", "reap", "adoptScope"];
|
|
452
|
+
const verbOf = (v) => {
|
|
453
|
+
try {
|
|
454
|
+
return candidate[v];
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
const probeable = (typeof candidate === "object" && candidate !== null) || typeof candidate === "function";
|
|
461
|
+
const missing = probeable ? verbs.filter((v) => typeof verbOf(v) !== "function") : [...verbs];
|
|
462
|
+
if (missing.length > 0) {
|
|
463
|
+
const shape = candidate === null ? "null" : probeable ? "a value that is not a FileHistoryStore" : `a ${typeof candidate}`;
|
|
464
|
+
const e = new Error(`RunnerDeps.fileHistoryStore was wired with ${shape} — missing contract verbs: ${missing.join(", ")}. Refusing prepare rather than half-wiring the rewind seat (a present-but-unusable store makes every Edit/Write/NotebookEdit fail its first-touch backup while restore requests report "no store wired"). Omit the key to run without file history.`);
|
|
465
|
+
e.code = "config.invalid_file_history_store";
|
|
466
|
+
throw e;
|
|
467
|
+
}
|
|
468
|
+
return candidate;
|
|
469
|
+
}
|
|
470
|
+
const LEGACY_PROBE_TIMEOUT_MS = 2_000;
|
|
471
|
+
const LEGACY_PROBE_TIMED_OUT = Symbol("legacy-rewind-probe-timeout");
|
|
472
|
+
async function legacyRewindEpochTail(deps, sessionId, entryId, form) {
|
|
473
|
+
const probe = deps.legacyRewindBoundaryProbe;
|
|
474
|
+
let verdict;
|
|
475
|
+
if (probe !== undefined) {
|
|
476
|
+
try {
|
|
477
|
+
let timer;
|
|
478
|
+
const timeout = new Promise((resolve) => {
|
|
479
|
+
timer = setTimeout(() => resolve(LEGACY_PROBE_TIMED_OUT), LEGACY_PROBE_TIMEOUT_MS);
|
|
480
|
+
});
|
|
481
|
+
try {
|
|
482
|
+
verdict = await Promise.race([Promise.resolve(probe({ sessionId, entryId })), timeout]);
|
|
483
|
+
}
|
|
484
|
+
finally {
|
|
485
|
+
clearTimeout(timer);
|
|
486
|
+
}
|
|
487
|
+
if (verdict === LEGACY_PROBE_TIMED_OUT) {
|
|
488
|
+
verdict = undefined;
|
|
489
|
+
try {
|
|
490
|
+
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe did not answer within ${LEGACY_PROBE_TIMEOUT_MS}ms while explaining a rewind refusal — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
verdict = undefined;
|
|
498
|
+
try {
|
|
499
|
+
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe threw while explaining a rewind refusal (${err.message}) — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (verdict !== undefined && typeof verdict !== "boolean") {
|
|
505
|
+
try {
|
|
506
|
+
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe answered ${JSON.stringify(String(verdict))} for entry "${entryId}" — only true/false/undefined are meaningful, so the refusal names both possibilities instead of trusting it`), { phase: "rewind", sessionId });
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
}
|
|
510
|
+
verdict = undefined;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (verdict === true) {
|
|
514
|
+
return `This deployment's legacy-epoch probe reports that entry "${entryId}" DOES carry a boundary from the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore — the two epochs are not interchangeable (a whole-tree manifest carries no tracked-set provenance, so replaying it here would converge files this session never edited). Restore it with the whole-tree-era tooling this deployment kept, or re-run the turns under the current engine.`;
|
|
515
|
+
}
|
|
516
|
+
if (verdict === false) {
|
|
517
|
+
return `This deployment's legacy-epoch probe reports no whole-tree-era boundary for entry "${entryId}" either, so the boundary simply does not exist: its turns ran without a RunnerDeps.fileHistoryStore wired, or it was reaped.`;
|
|
518
|
+
}
|
|
519
|
+
return form === "keyed"
|
|
520
|
+
? `Boundaries are keyed by a turn's INITIAL entry and exist only for turns that ran with a RunnerDeps.fileHistoryStore wired; a boundary from the retired whole-tree snapshot era is not restorable by this seat, and reaped boundaries have none.`
|
|
521
|
+
: `Either the boundary does not exist (its turns ran without a RunnerDeps.fileHistoryStore, or it was reaped), or it was produced in the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore.`;
|
|
522
|
+
}
|
|
523
|
+
const FILE_HISTORY_BOUNDARY_TIMEOUT_MS = 30_000;
|
|
524
|
+
const FILE_HISTORY_CARRY_MEMO = new WeakMap();
|
|
525
|
+
async function adoptForkedFileHistory(store, session, sessionId, onError) {
|
|
526
|
+
const memo = FILE_HISTORY_CARRY_MEMO.get(store) ?? { settled: new Set(), disclosed: new Set(), inFlight: new Map() };
|
|
527
|
+
FILE_HISTORY_CARRY_MEMO.set(store, memo);
|
|
528
|
+
const discloseOnce = (err) => {
|
|
529
|
+
if (memo.disclosed.has(sessionId))
|
|
530
|
+
return;
|
|
531
|
+
memo.disclosed.add(sessionId);
|
|
532
|
+
onError?.(err, { phase: "rewind", sessionId });
|
|
533
|
+
};
|
|
534
|
+
try {
|
|
535
|
+
if (memo.settled.has(sessionId))
|
|
536
|
+
return;
|
|
537
|
+
const forkedFrom = (await session.getMetadata()).forkedFrom;
|
|
538
|
+
if (forkedFrom === undefined || forkedFrom === sessionId)
|
|
539
|
+
return;
|
|
540
|
+
const shared = memo.inFlight.get(sessionId);
|
|
541
|
+
if (shared !== undefined)
|
|
542
|
+
return await shared;
|
|
543
|
+
if (memo.settled.has(sessionId))
|
|
544
|
+
return;
|
|
545
|
+
const attempt = store.adoptScope(forkedFrom, sessionId).then((carried) => {
|
|
546
|
+
if (carried.ok || carried.error.code === "conflict") {
|
|
547
|
+
memo.settled.add(sessionId);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
discloseOnce(new Error(`file-history fork carry from session "${forkedFrom}" failed (${carried.error.code}): ${carried.error.message} — this forked session starts with an EMPTY rewind history, so boundaries recorded before the fork are not restorable from it (the run itself proceeds; the carry is retried on this session's next turn while its scope is still empty)`));
|
|
551
|
+
}, (err) => discloseOnce(err));
|
|
552
|
+
memo.inFlight.set(sessionId, attempt);
|
|
553
|
+
try {
|
|
554
|
+
await attempt;
|
|
555
|
+
}
|
|
556
|
+
finally {
|
|
557
|
+
memo.inFlight.delete(sessionId);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
try {
|
|
562
|
+
discloseOnce(err);
|
|
563
|
+
}
|
|
564
|
+
catch {
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function createFileHistoryBoundarySeat(opts) {
|
|
569
|
+
let inFlight;
|
|
570
|
+
let begun = false;
|
|
571
|
+
return {
|
|
572
|
+
begin(entryId) {
|
|
573
|
+
if (begun)
|
|
574
|
+
return;
|
|
575
|
+
begun = true;
|
|
576
|
+
const ac = new AbortController();
|
|
577
|
+
let timer;
|
|
578
|
+
const timeout = new Promise((resolve) => {
|
|
579
|
+
timer = setTimeout(() => {
|
|
580
|
+
ac.abort();
|
|
581
|
+
resolve({ ok: false });
|
|
582
|
+
}, FILE_HISTORY_BOUNDARY_TIMEOUT_MS);
|
|
583
|
+
});
|
|
584
|
+
let work;
|
|
585
|
+
try {
|
|
586
|
+
work = opts.store.snapshot(opts.sessionId, entryId, opts.env, opts.root, ac.signal);
|
|
587
|
+
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
work = Promise.resolve({ ok: false, error: { code: "snapshot_failed", message: `history store threw synchronously: ${err.message}` } });
|
|
590
|
+
}
|
|
591
|
+
work.catch(() => { });
|
|
592
|
+
inFlight = Promise.race([
|
|
593
|
+
work.then((r) => (r.ok ? undefined : `boundary capture failed (${r.error.code}): ${r.error.message}`)),
|
|
594
|
+
timeout.then(() => `boundary capture timed out after ${FILE_HISTORY_BOUNDARY_TIMEOUT_MS / 1000}s (fenced — the attempt can never publish)`),
|
|
595
|
+
])
|
|
596
|
+
.then((failure) => {
|
|
597
|
+
if (failure !== undefined) {
|
|
598
|
+
try {
|
|
599
|
+
opts.onError?.(new Error(`file-history ${failure} — this turn has no boundary and cannot be rewound to (the turn itself proceeds)`), { phase: "rewind", sessionId: opts.sessionId });
|
|
600
|
+
}
|
|
601
|
+
catch {
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
})
|
|
605
|
+
.catch((err) => {
|
|
606
|
+
try {
|
|
607
|
+
opts.onError?.(err, { phase: "rewind", sessionId: opts.sessionId });
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
.finally(() => clearTimeout(timer));
|
|
613
|
+
},
|
|
614
|
+
async settle() {
|
|
615
|
+
if (inFlight !== undefined)
|
|
616
|
+
await inFlight;
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
}
|
|
436
620
|
export function gatedCallIdOf(p) {
|
|
437
621
|
if (p.suspendRef.token !== undefined)
|
|
438
622
|
return p.suspendRef.gatedCallId;
|
|
@@ -508,7 +692,7 @@ function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
|
|
|
508
692
|
}
|
|
509
693
|
};
|
|
510
694
|
}
|
|
511
|
-
function mcpRevocationWiring(deps) {
|
|
695
|
+
function mcpRevocationWiring(deps, runId) {
|
|
512
696
|
if (deps.mcpRevocations === undefined)
|
|
513
697
|
return undefined;
|
|
514
698
|
const ledger = deps.mcpRevocations;
|
|
@@ -517,7 +701,7 @@ function mcpRevocationWiring(deps) {
|
|
|
517
701
|
onProbeFailure: (e) => deliverEngineNotice(deps.onNotice, {
|
|
518
702
|
code: "mcp.revocation_probe_failed",
|
|
519
703
|
message: `the mcpRevocations.isRevoked probe threw — MCP dispatch fails OPEN (no server treated as revoked) until the probe recovers: ${e instanceof Error ? e.message : String(e)}`,
|
|
520
|
-
detail: { message: e instanceof Error ? e.message : String(e) },
|
|
704
|
+
detail: { message: e instanceof Error ? e.message : String(e), runId },
|
|
521
705
|
}),
|
|
522
706
|
};
|
|
523
707
|
}
|
|
@@ -674,7 +858,7 @@ async function derivedRouteFallsBack(args) {
|
|
|
674
858
|
const verdict = await adjudicateDerivedRoute({ brain: args.brain, model: args.derived, getApiKeyAndHeaders: args.getApiKeyAndHeaders });
|
|
675
859
|
if (verdict === undefined || verdict.ok)
|
|
676
860
|
return false;
|
|
677
|
-
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}) }));
|
|
861
|
+
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}), ...(args.runId !== undefined ? { runId: args.runId } : {}) }));
|
|
678
862
|
return true;
|
|
679
863
|
}
|
|
680
864
|
catch {
|
|
@@ -703,6 +887,12 @@ export function placementValueOrAbsent(value) {
|
|
|
703
887
|
function stampPlacementRootSessionId(placementRootResolved) {
|
|
704
888
|
return placementValueOrAbsent(placementRootResolved);
|
|
705
889
|
}
|
|
890
|
+
function explicitlyDeferredMemoryTrio(mounted, roster, deferNames) {
|
|
891
|
+
return mounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => roster.some((t) => t.name === n) && (deferNames ?? []).includes(n)) : [];
|
|
892
|
+
}
|
|
893
|
+
function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
|
|
894
|
+
return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
|
|
895
|
+
}
|
|
706
896
|
function refuseRequireExistingWithoutSession(spec) {
|
|
707
897
|
if (spec.requireExistingSession && !spec.sessionId) {
|
|
708
898
|
const e = new Error(`requireExistingSession requires a sessionId — cannot require an existing session without one (design/114 Phase3)`);
|
|
@@ -710,10 +900,13 @@ function refuseRequireExistingWithoutSession(spec) {
|
|
|
710
900
|
throw e;
|
|
711
901
|
}
|
|
712
902
|
}
|
|
713
|
-
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf) {
|
|
903
|
+
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
|
|
904
|
+
const runId = uuidv7();
|
|
905
|
+
if (runIdSink !== undefined)
|
|
906
|
+
runIdSink.runId = runId;
|
|
714
907
|
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
715
908
|
spec = doors.spec;
|
|
716
|
-
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
909
|
+
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
717
910
|
announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
|
|
718
911
|
const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes, ownToolNames } = prepareSafetyScan({ spec, deps });
|
|
719
912
|
let shellGatedBash = false;
|
|
@@ -727,7 +920,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
727
920
|
let effectiveCompModel = compModel;
|
|
728
921
|
if (spec.compactionModel === undefined &&
|
|
729
922
|
compModel !== undefined &&
|
|
730
|
-
(await derivedRouteFallsBack({ seat: "compaction-summary", derived: compModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId }))) {
|
|
923
|
+
(await derivedRouteFallsBack({ seat: "compaction-summary", derived: compModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId }))) {
|
|
731
924
|
effectiveCompModel = undefined;
|
|
732
925
|
}
|
|
733
926
|
warnCompactionWindowHazard(deps.tracer, spec, model, effectiveCompModel, hostTaskId);
|
|
@@ -907,79 +1100,165 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
907
1100
|
}
|
|
908
1101
|
const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
|
|
909
1102
|
const rewindNotes = [];
|
|
910
|
-
const
|
|
1103
|
+
const onTrackFailure = deps.onTrackFailure === undefined ? "refuse" : deps.onTrackFailure;
|
|
1104
|
+
if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
|
|
1105
|
+
const e = new Error(`RunnerDeps.onTrackFailure must be "refuse" or "proceed-unprotected"; got ${JSON.stringify(deps.onTrackFailure)} — refusing prepare rather than silently defaulting a first-touch-failure policy.`);
|
|
1106
|
+
e.code = "config.invalid_on_track_failure";
|
|
1107
|
+
throw e;
|
|
1108
|
+
}
|
|
1109
|
+
const fileHistoryStore = resolveWiredFileHistoryStore(deps.fileHistoryStore);
|
|
1110
|
+
if (fileHistoryStore !== undefined) {
|
|
1111
|
+
await adoptForkedFileHistory(fileHistoryStore, session, sessionId, deps.onError);
|
|
1112
|
+
}
|
|
1113
|
+
const legacyRewindFiles = spec.rewindFiles;
|
|
1114
|
+
if (legacyRewindFiles === true) {
|
|
1115
|
+
if (spec.resumeAt !== undefined) {
|
|
1116
|
+
const e = new Error(`rewind-files: TaskSpec.rewindFiles is retired (design/381 — rewind now converges the per-edited-file history, not a whole-tree snapshot). This request pairs it with resumeAt, i.e. the RESTORE sense: set restoreFiles: true instead. (The capture sense needs no request anymore — first-touch tracking is always on when a RunnerDeps.fileHistoryStore is wired.)`);
|
|
1117
|
+
e.code = "rewind.rewind_files_retired";
|
|
1118
|
+
throw e;
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
deps.onError?.(new Error(`TaskSpec.rewindFiles (capture sense) is retired and was ignored: per-edited-file first-touch tracking is always on when RunnerDeps.fileHistoryStore is wired, so a per-turn capture request has nothing left to ask for. Drop the field; the restore sense moved to TaskSpec.restoreFiles.`), { phase: "config", sessionId });
|
|
1122
|
+
}
|
|
1123
|
+
catch {
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (spec.restoreFiles === true && spec.resumeAt === undefined) {
|
|
1127
|
+
const e = new Error(`rewind-files: restoreFiles: true needs a resumeAt anchor — restoring files means converging them to a specific prior entry's boundary, and this spec names none. Use resumeAt + restoreFiles to move conversation and files together, or rewindFilesTo alone for a code-only restore.`);
|
|
1128
|
+
e.code = "rewind.invalid_spec";
|
|
1129
|
+
throw e;
|
|
1130
|
+
}
|
|
911
1131
|
if (spec.resumeAt !== undefined && spec.rewindFilesTo !== undefined) {
|
|
912
|
-
const e = new Error(`rewind-files: resumeAt ("${spec.resumeAt}") and rewindFilesTo ("${spec.rewindFilesTo}") were both set — resumeAt already anchors the file restore when
|
|
1132
|
+
const e = new Error(`rewind-files: resumeAt ("${spec.resumeAt}") and rewindFilesTo ("${spec.rewindFilesTo}") were both set — resumeAt already anchors the file restore when restoreFiles is true, so a separate rewindFilesTo target is a conflicting request. Drop one of the two.`);
|
|
913
1133
|
e.code = "rewind.conflicting_targets";
|
|
914
1134
|
throw e;
|
|
915
1135
|
}
|
|
916
|
-
let rewindTarget = spec.resumeAt !== undefined ? (
|
|
1136
|
+
let rewindTarget = spec.resumeAt !== undefined ? (spec.restoreFiles === true ? spec.resumeAt : undefined) : spec.rewindFilesTo;
|
|
917
1137
|
const rewindBefore = rewindTarget !== undefined && spec.resumeAt !== undefined && spec.resumeAtMode === "before";
|
|
918
|
-
if (spec.resumeAt !== undefined &&
|
|
1138
|
+
if (spec.resumeAt !== undefined && spec.restoreFiles !== true) {
|
|
919
1139
|
rewindNotes.push({
|
|
920
1140
|
code: "conversation_only",
|
|
921
|
-
message: `the conversation was branched at entry "${spec.resumeAt}" but the working tree was NOT rewound — this task set resumeAt without
|
|
1141
|
+
message: `the conversation was branched at entry "${spec.resumeAt}" but the working tree was NOT rewound — this task set resumeAt without restoreFiles, so files remain at their current state`,
|
|
922
1142
|
});
|
|
923
1143
|
}
|
|
924
|
-
|
|
1144
|
+
const fileHistoryEnabled = fileHistoryStore !== undefined && handsEnabled;
|
|
1145
|
+
let historyRoot = taskRootFinal;
|
|
1146
|
+
if (fileHistoryEnabled) {
|
|
1147
|
+
const canonRoot = await executionEnv.canonicalPath(taskRootFinal);
|
|
1148
|
+
if (canonRoot.ok)
|
|
1149
|
+
historyRoot = canonRoot.value;
|
|
1150
|
+
}
|
|
1151
|
+
if (fileHistoryStore === undefined) {
|
|
925
1152
|
if (rewindTarget !== undefined) {
|
|
926
|
-
const e = new Error(`rewind-files: restoring
|
|
1153
|
+
const e = new Error(`rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
|
|
927
1154
|
e.code = "rewind.store_unconfigured";
|
|
928
1155
|
throw e;
|
|
929
1156
|
}
|
|
930
|
-
if (rewindCaptureRequested) {
|
|
931
|
-
rewindNotes.push({
|
|
932
|
-
code: "snapshot_store_unconfigured",
|
|
933
|
-
message: "rewindFiles was requested but no RunnerDeps.fileSnapshotStore is wired — no working-tree snapshot was captured for this turn, so it cannot be rewound to later",
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
1157
|
}
|
|
937
1158
|
else if (!handsEnabled) {
|
|
938
|
-
if (rewindTarget !== undefined
|
|
1159
|
+
if (rewindTarget !== undefined) {
|
|
939
1160
|
rewindNotes.push({
|
|
940
1161
|
code: "files_env_unsupported",
|
|
941
|
-
message: "the file side of rewind was inert: this deployment mounts no filesystem-capable ExecutionEnv, so no
|
|
1162
|
+
message: "the file side of rewind was inert: this deployment mounts no filesystem-capable ExecutionEnv, so no file history was recorded or restored",
|
|
942
1163
|
});
|
|
943
1164
|
}
|
|
944
1165
|
}
|
|
945
|
-
|
|
1166
|
+
const legacyEpochTail = (entryId, form = "at-or-above") => legacyRewindEpochTail(deps, sessionId, entryId, form);
|
|
1167
|
+
let restoredFilePaths = [];
|
|
1168
|
+
if (rewindTarget !== undefined && fileHistoryStore !== undefined && handsEnabled) {
|
|
1169
|
+
const historyStore = fileHistoryStore;
|
|
946
1170
|
if (rewindBefore) {
|
|
947
1171
|
let anchor;
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1172
|
+
if (await historyStore.canRestore(sessionId, rewindTarget)) {
|
|
1173
|
+
anchor = rewindTarget;
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
const walked = new Set();
|
|
1177
|
+
let cursor = resumeAtBeforeParentId;
|
|
1178
|
+
while (cursor !== null && !walked.has(cursor)) {
|
|
1179
|
+
walked.add(cursor);
|
|
1180
|
+
if (await historyStore.canRestore(sessionId, cursor)) {
|
|
1181
|
+
anchor = cursor;
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
cursor = (await session.getEntry(cursor))?.parentId ?? null;
|
|
955
1185
|
}
|
|
956
|
-
cursor = (await session.getEntry(cursor))?.parentId ?? null;
|
|
957
1186
|
}
|
|
958
1187
|
if (anchor === undefined) {
|
|
959
|
-
const e = new Error(`rewind-files: no file
|
|
1188
|
+
const e = new Error(`rewind-files: no file-history boundary exists at or above the "before" branch point on session "${sessionId}" — cannot restore files consistent with the rewound context. ${await legacyEpochTail(rewindTarget)}`);
|
|
960
1189
|
e.code = "rewind_snapshot.unresolvable";
|
|
961
1190
|
throw e;
|
|
962
1191
|
}
|
|
963
1192
|
rewindTarget = anchor;
|
|
964
1193
|
}
|
|
965
|
-
const restoreRoot =
|
|
1194
|
+
const restoreRoot = historyRoot;
|
|
966
1195
|
const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal;
|
|
967
|
-
const restored = await
|
|
968
|
-
|
|
969
|
-
|
|
1196
|
+
const restored = await historyStore.restore(sessionId, rewindTarget, executionEnv, restoreRoot, restoreSignal);
|
|
1197
|
+
const ledgerLine = (r) => `applied ${r.filesChanged.length}, identical ${r.identicalSkipped.length}, refused ${r.refused.length}${r.refused.length > 0 ? ` (${r.refused.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}, failed ${r.failed.length}${r.failed.length > 0 ? ` (${r.failed.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}`;
|
|
1198
|
+
if (restored.outcome === "failed") {
|
|
1199
|
+
if (restored.error?.code === "not_found") {
|
|
970
1200
|
const e = new Error(rewindBefore
|
|
971
|
-
? `rewind-files: the resolved "before"
|
|
972
|
-
: `rewind-files: no file
|
|
1201
|
+
? `rewind-files: the resolved "before" history anchor "${rewindTarget}" disappeared before restore — files were NOT rewound`
|
|
1202
|
+
: `rewind-files: no file-history boundary exists for entry "${rewindTarget}" on session "${sessionId}" — the working tree was NOT rewound. ${await legacyEpochTail(rewindTarget, "keyed")}`);
|
|
973
1203
|
e.code = "rewind_snapshot.unresolvable";
|
|
974
1204
|
throw e;
|
|
975
1205
|
}
|
|
1206
|
+
const e = new Error(`rewind-files restore failed (${restored.error?.code ?? "restore_failed"}): ${restored.error?.message ?? "unknown"} — ledger: ${ledgerLine(restored)}`);
|
|
1207
|
+
e.code = "rewind.restore_failed";
|
|
1208
|
+
throw e;
|
|
1209
|
+
}
|
|
1210
|
+
if (restored.outcome === "partial") {
|
|
1211
|
+
if (spec.acceptPartialRestore === true) {
|
|
1212
|
+
rewindNotes.push({
|
|
1213
|
+
code: "restore_partial",
|
|
1214
|
+
message: `file restore to entry "${rewindTarget}" was PARTIAL and was tolerated by acceptPartialRestore — ledger: ${ledgerLine(restored)}. Re-running the same restore converges (per-file idempotent).`,
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
976
1217
|
else {
|
|
977
|
-
const e = new Error(`rewind-files restore
|
|
1218
|
+
const e = new Error(`rewind-files: restore to entry "${rewindTarget}" was PARTIAL — ledger: ${ledgerLine(restored)}. The task is refused rather than run on a mixed-state tree (set acceptPartialRestore: true to tolerate and disclose instead); re-running the same restore converges (per-file idempotent).`);
|
|
978
1219
|
e.code = "rewind.restore_failed";
|
|
979
1220
|
throw e;
|
|
980
1221
|
}
|
|
981
1222
|
}
|
|
1223
|
+
restoredFilePaths = restoredAbsPathsOf(restored, restoreRoot);
|
|
982
1224
|
}
|
|
1225
|
+
const trackFileEdit = fileHistoryEnabled
|
|
1226
|
+
? async (req) => {
|
|
1227
|
+
const r = await fileHistoryStore.trackEdit(sessionId, req.key, executionEnv, historyRoot, req.signal);
|
|
1228
|
+
if (r.ok) {
|
|
1229
|
+
if (!r.minted)
|
|
1230
|
+
return { ok: true };
|
|
1231
|
+
return {
|
|
1232
|
+
ok: true,
|
|
1233
|
+
annul: async (proof) => {
|
|
1234
|
+
const a = await fileHistoryStore.annulTrack(sessionId, req.key, historyRoot, proof === "verify" ? { env: executionEnv, ...(req.signal !== undefined ? { signal: req.signal } : {}) } : undefined);
|
|
1235
|
+
if (a.ok)
|
|
1236
|
+
return;
|
|
1237
|
+
try {
|
|
1238
|
+
deps.onError?.(new Error(`file-history could not retract the first-touch record for "${req.key}" after its edit failed to land (${a.error.code}: ${a.error.message}) — the path stays tracked, so a rewind to a boundary below it may converge bytes this engine never wrote`), { phase: "rewind", sessionId });
|
|
1239
|
+
}
|
|
1240
|
+
catch {
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
if (onTrackFailure === "proceed-unprotected") {
|
|
1246
|
+
try {
|
|
1247
|
+
deps.onError?.(new Error(`file-history first-touch backup for "${req.key}" failed (${r.error.code}: ${r.error.message}) — proceeding UNPROTECTED per RunnerDeps.onTrackFailure: the pristine promise for this path is void, and only in this process's memory (no durable trace exists, so after a process restart the next edit can still mint already-modified bytes as pristine v1)`), { phase: "rewind", sessionId });
|
|
1248
|
+
}
|
|
1249
|
+
catch {
|
|
1250
|
+
}
|
|
1251
|
+
return { ok: true };
|
|
1252
|
+
}
|
|
1253
|
+
return {
|
|
1254
|
+
ok: false,
|
|
1255
|
+
refusal: `Error (${req.tool}): the edit was refused because its first-touch file-history backup could not be persisted (${r.error.code}: ${r.error.message}). Proceeding would leave "${req.path}" with no durable pristine record — after the history store recovers, a retried edit would mint already-modified bytes as the pristine baseline. Retry when the file-history store is healthy.`,
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
: undefined;
|
|
1259
|
+
const fileHistoryBoundary = fileHistoryEnabled
|
|
1260
|
+
? createFileHistoryBoundarySeat({ store: fileHistoryStore, sessionId, env: executionEnv, root: historyRoot, onError: deps.onError })
|
|
1261
|
+
: undefined;
|
|
983
1262
|
const harnessRef = {};
|
|
984
1263
|
const skillScope = new ActiveSkillScope();
|
|
985
1264
|
const forwardSink = internals?.onForwardEvent;
|
|
@@ -1121,7 +1400,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1121
1400
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1122
1401
|
...(durableMandate ? { durableMandate: true } : {}),
|
|
1123
1402
|
...(contentMandate ? { contentMandate: true } : {}),
|
|
1124
|
-
...(autoModeDecider !== undefined
|
|
1403
|
+
...(autoModeDecider !== undefined
|
|
1404
|
+
? { autoMode: { decider: autoModeDecider, ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
|
|
1405
|
+
: {}),
|
|
1125
1406
|
},
|
|
1126
1407
|
]
|
|
1127
1408
|
: [];
|
|
@@ -1135,7 +1416,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1135
1416
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1136
1417
|
...(durableMandate ? { durableMandate: true } : {}),
|
|
1137
1418
|
...(contentMandate ? { contentMandate: true } : {}),
|
|
1138
|
-
...(autoModeDecider !== undefined
|
|
1419
|
+
...(autoModeDecider !== undefined
|
|
1420
|
+
? { autoMode: { decider: autoModeDecider, ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
|
|
1421
|
+
: {}),
|
|
1139
1422
|
},
|
|
1140
1423
|
]
|
|
1141
1424
|
: []),
|
|
@@ -1159,6 +1442,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1159
1442
|
spec.handsReadOnly !== true &&
|
|
1160
1443
|
!(toolFaceSnapshot.exclude?.includes("Bash") ?? false);
|
|
1161
1444
|
let autoModeDecider;
|
|
1445
|
+
let autoModeArming;
|
|
1162
1446
|
const delegationEntryCapsResolved = resolveDelegationEntryCaps(deps.delegationEntryCaps);
|
|
1163
1447
|
const enrichSpecToolCtx = (ctx) => ({
|
|
1164
1448
|
...ctx,
|
|
@@ -1184,6 +1468,19 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1184
1468
|
...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
|
|
1185
1469
|
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
1186
1470
|
...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
|
|
1471
|
+
get memoryCaptureOptedOut() {
|
|
1472
|
+
return memoryEngineSession?.captureOptOut?.optedOut() === true;
|
|
1473
|
+
},
|
|
1474
|
+
get memoryCaptureIndeterminate() {
|
|
1475
|
+
return memoryEngineSession?.captureOptOut?.indeterminate() === true;
|
|
1476
|
+
},
|
|
1477
|
+
get memoryCaptureControlDir() {
|
|
1478
|
+
return memoryEngineSession?.engine.controlPlaneDir;
|
|
1479
|
+
},
|
|
1480
|
+
get memoryCaptureAncestors() {
|
|
1481
|
+
const ctl = memoryEngineSession?.engine.controlPlaneDir;
|
|
1482
|
+
return [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }];
|
|
1483
|
+
},
|
|
1187
1484
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1188
1485
|
parentCwd: taskRootFinal,
|
|
1189
1486
|
reminderMark,
|
|
@@ -1262,6 +1559,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1262
1559
|
}
|
|
1263
1560
|
}
|
|
1264
1561
|
let runtimeCaps;
|
|
1562
|
+
let runtimeCapsFaulted = false;
|
|
1265
1563
|
if (deps.runtimeCapsResolver) {
|
|
1266
1564
|
try {
|
|
1267
1565
|
runtimeCaps = (await deps.runtimeCapsResolver(spec.principal)) ?? undefined;
|
|
@@ -1269,6 +1567,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1269
1567
|
catch (err) {
|
|
1270
1568
|
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
1271
1569
|
runtimeCaps = { allowWorkflows: false, allowFork: false };
|
|
1570
|
+
runtimeCapsFaulted = true;
|
|
1272
1571
|
}
|
|
1273
1572
|
}
|
|
1274
1573
|
let complianceDenies = new Set();
|
|
@@ -1315,7 +1614,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1315
1614
|
catch {
|
|
1316
1615
|
classifierModel = model;
|
|
1317
1616
|
}
|
|
1318
|
-
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId })) {
|
|
1617
|
+
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId })) {
|
|
1319
1618
|
classifierModel = model;
|
|
1320
1619
|
}
|
|
1321
1620
|
const classifierSystemPrompt = buildAutoModePrompt(am);
|
|
@@ -1340,6 +1639,17 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1340
1639
|
.join("");
|
|
1341
1640
|
},
|
|
1342
1641
|
});
|
|
1642
|
+
if (am.persistArming === true) {
|
|
1643
|
+
autoModeArming = autoModeArmingRecipeOf(am, {
|
|
1644
|
+
promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`,
|
|
1645
|
+
});
|
|
1646
|
+
if (autoModeArming === undefined) {
|
|
1647
|
+
deps.onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " +
|
|
1648
|
+
"(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " +
|
|
1649
|
+
"NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " +
|
|
1650
|
+
"conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId });
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1343
1653
|
}
|
|
1344
1654
|
const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps);
|
|
1345
1655
|
const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false;
|
|
@@ -1387,6 +1697,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1387
1697
|
principal: spec.principal,
|
|
1388
1698
|
oneShot: spec.oneShot,
|
|
1389
1699
|
...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
|
|
1700
|
+
parentMemoryCaptureState: () => {
|
|
1701
|
+
const ctl = memoryEngineSession?.engine.controlPlaneDir;
|
|
1702
|
+
return {
|
|
1703
|
+
optedOut: memoryEngineSession?.captureOptOut?.optedOut() === true,
|
|
1704
|
+
indeterminate: memoryEngineSession?.captureOptOut?.indeterminate() === true,
|
|
1705
|
+
...(ctl !== undefined ? { controlDir: ctl } : {}),
|
|
1706
|
+
ancestors: [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }],
|
|
1707
|
+
};
|
|
1708
|
+
},
|
|
1390
1709
|
autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
|
|
1391
1710
|
workflowDepth: internals?.workflowDepth,
|
|
1392
1711
|
parentCwd: taskRootFinal,
|
|
@@ -1465,7 +1784,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1465
1784
|
tools.push(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined));
|
|
1466
1785
|
}
|
|
1467
1786
|
mcp = lockedPreflight.mcp?.length
|
|
1468
|
-
? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps))
|
|
1787
|
+
? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
|
|
1469
1788
|
: { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
|
|
1470
1789
|
for (const w of mcp.warnings)
|
|
1471
1790
|
deps.onError?.(w, { phase: "mcp", sessionId });
|
|
@@ -1621,9 +1940,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1621
1940
|
foldProtocolAxes(a2a.toolAxes, "A2A");
|
|
1622
1941
|
const callIssuedAtRef = {};
|
|
1623
1942
|
const memoryWriteGateRef = {};
|
|
1624
|
-
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts };
|
|
1943
|
+
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
|
|
1625
1944
|
const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
|
|
1626
|
-
const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames } = handsReadFace;
|
|
1945
|
+
const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
|
|
1627
1946
|
resolvedReadFace = handsReadFace.resolvedReadFace;
|
|
1628
1947
|
readDenyAdditionsNormalized = handsReadFace.readDenyAdditionsNormalized;
|
|
1629
1948
|
handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
|
|
@@ -1868,7 +2187,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1868
2187
|
const memoryPairExcludedName = MEMORY_ENGINE_TOOL_NAMES.find((n) => (toolFaceSnapshot.exclude ?? []).includes(n));
|
|
1869
2188
|
const memoryPairOccupiedName = MEMORY_ENGINE_TOOL_NAMES.find((n) => memoryPairNameDomain.has(n));
|
|
1870
2189
|
const memorySearchToolsPlanned = memoryPairExcludedName === undefined && memoryPairOccupiedName === undefined;
|
|
1871
|
-
const
|
|
2190
|
+
const captureLineage = await (async () => {
|
|
2191
|
+
try {
|
|
2192
|
+
const ff = (await session.getMetadata()).forkedFrom;
|
|
2193
|
+
return { ...(ff !== undefined && ff !== sessionId ? { origin: ff } : {}), fault: false };
|
|
2194
|
+
}
|
|
2195
|
+
catch {
|
|
2196
|
+
return { fault: true };
|
|
2197
|
+
}
|
|
2198
|
+
})();
|
|
2199
|
+
const { memoryEngineSession, memoryBlock: memoryBlockFromEngine, memoryTools, recallDisciplineSegment: memoryRecallSegment, seedFiles: memorySeedFiles, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, effectiveMemoryScopes: memoryEffectiveScopes, } = await prepareMemory({
|
|
1872
2200
|
spec,
|
|
1873
2201
|
deps,
|
|
1874
2202
|
sessionId,
|
|
@@ -1879,6 +2207,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1879
2207
|
!(toolFaceSnapshot.exclude?.includes("Write") ?? false) &&
|
|
1880
2208
|
!(handsEnabled && isRemoteExecutionEnv(executionEnv) && spec.memoryPersistenceCapable !== true),
|
|
1881
2209
|
memoryProvenance,
|
|
2210
|
+
memoryCapturePolicy,
|
|
2211
|
+
memoryCapturePolicyDeclared: deps.memoryCapturePolicy !== undefined,
|
|
2212
|
+
captureEntitlement: {
|
|
2213
|
+
value: typeof runtimeCaps?.allowMemoryOptOut === "boolean" ? runtimeCaps.allowMemoryOptOut : undefined,
|
|
2214
|
+
faulted: runtimeCapsFaulted || (runtimeCaps?.allowMemoryOptOut !== undefined && typeof runtimeCaps.allowMemoryOptOut !== "boolean"),
|
|
2215
|
+
},
|
|
2216
|
+
captureCarrierUnsupported: isRemoteExecutionEnv(executionEnv) && deps.memoryCaptureRecordStore === undefined,
|
|
2217
|
+
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
2218
|
+
captureFloor: internals?.memoryCaptureFloor === true,
|
|
2219
|
+
...(internals?.memoryCaptureFloorIndeterminate === true ? { captureFloorIndeterminate: true } : {}),
|
|
2220
|
+
...(captureLineage.origin !== undefined ? { captureForkOrigin: captureLineage.origin } : {}),
|
|
2221
|
+
...(captureLineage.fault ? { captureLineageFault: true } : {}),
|
|
2222
|
+
...(internals?.memoryCaptureQueryDir !== undefined ? { parentCaptureQueryDir: internals.memoryCaptureQueryDir } : {}),
|
|
2223
|
+
...(internals?.memoryCaptureAncestors !== undefined ? { captureAncestors: internals.memoryCaptureAncestors } : {}),
|
|
1882
2224
|
memoryPersistenceDeclared: spec.memoryPersistenceCapable,
|
|
1883
2225
|
rosterCanPersist: spec.memoryPersistenceCapable ??
|
|
1884
2226
|
tools.some((t) => {
|
|
@@ -1937,6 +2279,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1937
2279
|
}
|
|
1938
2280
|
}
|
|
1939
2281
|
let instructionSources;
|
|
2282
|
+
let projectInstructionContent;
|
|
1940
2283
|
if (deps.loadProjectMemory) {
|
|
1941
2284
|
let projectMemoryPhase = spec.sessionId ? "resume" : "fresh";
|
|
1942
2285
|
if (spec.sessionId) {
|
|
@@ -1988,6 +2331,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1988
2331
|
}
|
|
1989
2332
|
}
|
|
1990
2333
|
if (projectMem != null && projectMem.trim()) {
|
|
2334
|
+
projectInstructionContent = projectMem;
|
|
1991
2335
|
const projectBlock = `${PROJECT_CONTEXT_FRAMING}\n\n${composeMemoryBlock(projectMem, "project")}`;
|
|
1992
2336
|
memoryBlock = memoryBlock ? `${memoryBlock}\n\n${projectBlock}` : projectBlock;
|
|
1993
2337
|
}
|
|
@@ -2052,7 +2396,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2052
2396
|
(memoryPairExcludedName !== undefined
|
|
2053
2397
|
? `excludeTools removes "${memoryPairExcludedName}"`
|
|
2054
2398
|
: `this task already declares a tool named "${memoryPairOccupiedName}" (the built-in yields to it)`) +
|
|
2055
|
-
" —
|
|
2399
|
+
" — these tools mount together or not at all."), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
|
|
2056
2400
|
}
|
|
2057
2401
|
const skillsListing = skillSpecs.length > 0
|
|
2058
2402
|
? {
|
|
@@ -2251,7 +2595,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2251
2595
|
return deferredSet;
|
|
2252
2596
|
};
|
|
2253
2597
|
const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
|
|
2254
|
-
const memoryEnginePair = memoryEnginePairMounted
|
|
2598
|
+
const memoryEnginePair = explicitlyDeferredMemoryTrio(memoryEnginePairMounted, tools, toolFaceSnapshot.defer);
|
|
2255
2599
|
const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
|
|
2256
2600
|
let deferred;
|
|
2257
2601
|
if (builtinDeferPairNames.length > 0) {
|
|
@@ -2261,8 +2605,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2261
2605
|
const soleCause = d0.size === 0 && d1.size > 0;
|
|
2262
2606
|
const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
|
|
2263
2607
|
if (soleCause && supportNameTaken) {
|
|
2608
|
+
const retractNames = memoryGroupRetractionSet(builtinDeferPairNames, memoryEnginePair.length > 0);
|
|
2264
2609
|
for (let i = tools.length - 1; i >= 0; i--) {
|
|
2265
|
-
if (
|
|
2610
|
+
if (retractNames.has(tools[i].name))
|
|
2266
2611
|
tools.splice(i, 1);
|
|
2267
2612
|
}
|
|
2268
2613
|
if (sharedMemoryPair.length > 0) {
|
|
@@ -2272,8 +2617,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2272
2617
|
}
|
|
2273
2618
|
if (memoryEnginePair.length > 0) {
|
|
2274
2619
|
memoryEnginePairMounted = false;
|
|
2275
|
-
if (memoryBlock !== undefined) {
|
|
2276
|
-
const segment =
|
|
2620
|
+
if (memoryBlock !== undefined && memoryRecallSegment !== undefined) {
|
|
2621
|
+
const segment = memoryRecallSegment;
|
|
2277
2622
|
if (memoryBlock === segment)
|
|
2278
2623
|
memoryBlock = undefined;
|
|
2279
2624
|
else if (memoryBlock.includes(`\n\n${segment}`))
|
|
@@ -2282,7 +2627,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2282
2627
|
memoryBlock = memoryBlock.slice(segment.length + 2);
|
|
2283
2628
|
}
|
|
2284
2629
|
deps.onError?.(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
|
|
2285
|
-
`the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares —
|
|
2630
|
+
`the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — these tools mount together or not at all.`), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
|
|
2286
2631
|
}
|
|
2287
2632
|
deferred = d0;
|
|
2288
2633
|
}
|
|
@@ -2307,6 +2652,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2307
2652
|
statuses: mcp?.statuses,
|
|
2308
2653
|
execIsExternalContent,
|
|
2309
2654
|
sessionId,
|
|
2655
|
+
runId,
|
|
2310
2656
|
onNotice: deps.onNotice,
|
|
2311
2657
|
});
|
|
2312
2658
|
if (memoryEngineSession?.settlement !== undefined) {
|
|
@@ -2470,6 +2816,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2470
2816
|
reason: waivedReason,
|
|
2471
2817
|
...(typeof requested === "string" ? { subagentType: inlineUntrusted(requested, 120) } : {}),
|
|
2472
2818
|
sessionId,
|
|
2819
|
+
runId,
|
|
2473
2820
|
},
|
|
2474
2821
|
});
|
|
2475
2822
|
}
|
|
@@ -2930,7 +3277,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2930
3277
|
emitTrace(deps.tracer, () => ({
|
|
2931
3278
|
kind: "loop.recovery",
|
|
2932
3279
|
version: 1,
|
|
2933
|
-
taskId:
|
|
3280
|
+
taskId: hostTaskId,
|
|
2934
3281
|
reason: step.reason,
|
|
2935
3282
|
ts: Date.now(),
|
|
2936
3283
|
}));
|
|
@@ -3161,7 +3508,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3161
3508
|
emitTrace(deps.tracer, () => ({
|
|
3162
3509
|
kind: "permission.sandbox_admitted",
|
|
3163
3510
|
version: 1,
|
|
3164
|
-
taskId:
|
|
3511
|
+
taskId: hostTaskId,
|
|
3165
3512
|
toolCallId: info.toolCallId,
|
|
3166
3513
|
toolName: info.toolName,
|
|
3167
3514
|
askClasses: info.askClasses.map((a) => a.cls),
|
|
@@ -3247,7 +3594,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3247
3594
|
emitTrace(deps.tracer, () => ({
|
|
3248
3595
|
kind: "permission.rule_store_unreadable",
|
|
3249
3596
|
version: 1,
|
|
3250
|
-
taskId:
|
|
3597
|
+
taskId: hostTaskId,
|
|
3251
3598
|
message: err instanceof Error ? err.message : String(err),
|
|
3252
3599
|
ts: Date.now(),
|
|
3253
3600
|
}));
|
|
@@ -3292,26 +3639,36 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3292
3639
|
return {};
|
|
3293
3640
|
if ((spec.principal === undefined || spec.principal === "") && deps.localOwnerRules !== true)
|
|
3294
3641
|
return {};
|
|
3295
|
-
|
|
3296
|
-
ask?.
|
|
3297
|
-
|
|
3298
|
-
ask?.
|
|
3299
|
-
|
|
3300
|
-
ask?.
|
|
3301
|
-
|
|
3302
|
-
|
|
3642
|
+
const closedDoor = (() => {
|
|
3643
|
+
if (ask?.requiresRealApproval === true)
|
|
3644
|
+
return "mandated";
|
|
3645
|
+
if (ask?.persistedRuleShadowed !== undefined)
|
|
3646
|
+
return "shadowed";
|
|
3647
|
+
if (ask?.decisionReason === "hook")
|
|
3648
|
+
return "mandated";
|
|
3649
|
+
if (ask?.matchedAskRule !== undefined)
|
|
3650
|
+
return "shadowed";
|
|
3651
|
+
if (ask?.inheritedUnresolved === true)
|
|
3652
|
+
return "mandated";
|
|
3653
|
+
if (ask?.ancestorResolved === true)
|
|
3654
|
+
return "mandated";
|
|
3655
|
+
return undefined;
|
|
3656
|
+
})();
|
|
3657
|
+
if (closedDoor !== undefined)
|
|
3658
|
+
return { ruleOffersAbsence: closedDoor };
|
|
3303
3659
|
if (persistedRuleMandateOf({
|
|
3304
3660
|
egress: egressTools.has(toolName),
|
|
3305
3661
|
irreversibility: irreversibilityTier.get(toolName),
|
|
3306
3662
|
shellGated: shellGatedBash,
|
|
3663
|
+
probeMandated: ask?.probeMandated === true,
|
|
3307
3664
|
}) !== undefined) {
|
|
3308
|
-
return {};
|
|
3665
|
+
return { ruleOffersAbsence: "mandated" };
|
|
3309
3666
|
}
|
|
3310
3667
|
const command = args?.command;
|
|
3311
3668
|
if (typeof command !== "string")
|
|
3312
|
-
return {};
|
|
3669
|
+
return { ruleOffersAbsence: "lane_cannot_speak" };
|
|
3313
3670
|
const offers = suggestRulesForCommand(command, ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : undefined);
|
|
3314
|
-
return offers.length > 0 ? { ruleOffers: offers } : {};
|
|
3671
|
+
return offers.length > 0 ? { ruleOffers: offers } : { ruleOffersAbsence: "lane_cannot_speak" };
|
|
3315
3672
|
};
|
|
3316
3673
|
const frozenClassifierExcluded = (d) => d.decisionReason === "hook" || d.matchedAskRule !== undefined;
|
|
3317
3674
|
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal, ancestorDecider) => {
|
|
@@ -3748,7 +4105,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3748
4105
|
const parkLaneArmed = wiringManifest.parkLane.effective === true;
|
|
3749
4106
|
const hookIdentity = mintHookInvocationIdentity({
|
|
3750
4107
|
sessionId,
|
|
3751
|
-
taskId:
|
|
4108
|
+
taskId: hostTaskId,
|
|
3752
4109
|
legKind: wiringManifest.leg.kind,
|
|
3753
4110
|
isDelegatedChild: delegation.isDelegatedChild,
|
|
3754
4111
|
...(internals?.insideFork === true ? { insideFork: true } : {}),
|
|
@@ -3757,6 +4114,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3757
4114
|
});
|
|
3758
4115
|
const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
|
|
3759
4116
|
const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
|
|
4117
|
+
if (fileHistoryBoundary !== undefined) {
|
|
4118
|
+
harness.on("tool_call", async () => {
|
|
4119
|
+
await fileHistoryBoundary.settle();
|
|
4120
|
+
return undefined;
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
3760
4123
|
if (effectivePolicy || hooks?.preToolUse || egressTools.size > 0 || irreversibleTools.size > 0 || resourceSuspendEligible || platformSuspendArmed || spec.enablePlanMode === true) {
|
|
3761
4124
|
const adjudicate = effectivePolicy
|
|
3762
4125
|
? (req) => raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}) }, abortController.signal)), abortController.signal, () => ({
|
|
@@ -3884,7 +4247,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3884
4247
|
const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
|
|
3885
4248
|
const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
|
|
3886
4249
|
const constraintChain = (inheritedParentConstraints?.length ?? 0) > 0
|
|
3887
|
-
? inheritedParentConstraints.map((pc) =>
|
|
4250
|
+
? inheritedParentConstraints.map((pc) => constraintChainEntryOfLayer(pc))
|
|
3888
4251
|
: seedInheritedGate?.constraintChain;
|
|
3889
4252
|
const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
|
|
3890
4253
|
? constraintChainDigest(constraintChain)
|
|
@@ -4282,7 +4645,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4282
4645
|
}
|
|
4283
4646
|
};
|
|
4284
4647
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
4285
|
-
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule) => {
|
|
4648
|
+
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated) => {
|
|
4286
4649
|
const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
4287
4650
|
if (syncFirstEligible &&
|
|
4288
4651
|
runtimeCaps?.forceDurableGate !== true &&
|
|
@@ -4453,6 +4816,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4453
4816
|
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
4454
4817
|
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
4455
4818
|
...(matchedAskRule !== undefined ? { matchedAskRule } : {}),
|
|
4819
|
+
...(probeMandated === true ? { probeMandated: true } : {}),
|
|
4456
4820
|
...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
|
|
4457
4821
|
...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
|
|
4458
4822
|
}),
|
|
@@ -4552,7 +4916,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4552
4916
|
let humanBareRejection = false;
|
|
4553
4917
|
try {
|
|
4554
4918
|
result = await runToolGate({
|
|
4555
|
-
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId:
|
|
4919
|
+
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
|
|
4556
4920
|
event: e,
|
|
4557
4921
|
identity: hookIdentity,
|
|
4558
4922
|
reminderMark,
|
|
@@ -4581,7 +4945,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4581
4945
|
onResolved: (info) => emitTrace(deps.tracer, () => ({
|
|
4582
4946
|
kind: "permission.persisted_rule_allowed",
|
|
4583
4947
|
version: 2,
|
|
4584
|
-
taskId:
|
|
4948
|
+
taskId: hostTaskId,
|
|
4585
4949
|
toolName: info.toolName,
|
|
4586
4950
|
toolCallId: info.toolCallId,
|
|
4587
4951
|
rules: [...info.rules],
|
|
@@ -4598,7 +4962,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4598
4962
|
onUnavailable: (info) => emitTrace(deps.tracer, () => ({
|
|
4599
4963
|
kind: "permission.org_snapshot_unavailable",
|
|
4600
4964
|
version: 1,
|
|
4601
|
-
taskId:
|
|
4965
|
+
taskId: hostTaskId,
|
|
4602
4966
|
toolName: info.toolName,
|
|
4603
4967
|
toolCallId: info.toolCallId,
|
|
4604
4968
|
message: info.message,
|
|
@@ -4720,7 +5084,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4720
5084
|
}
|
|
4721
5085
|
const editAt = editBudget(model);
|
|
4722
5086
|
const guardAt = guardBudget(model);
|
|
4723
|
-
const microCompact = buildMicroCompactState(deps, model, sessionId, offloadStore, microCompactKnob);
|
|
5087
|
+
const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
|
|
4724
5088
|
const charsPerToken = model.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
|
|
4725
5089
|
harness.on("context", async ({ messages, recheck, signal }) => {
|
|
4726
5090
|
batchHaltRef.current = undefined;
|
|
@@ -4735,7 +5099,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4735
5099
|
onCapped: (info) => pendingProjectionObservations.push(() => emitTrace(deps.tracer, () => ({
|
|
4736
5100
|
kind: "tool_result.capped",
|
|
4737
5101
|
version: 1,
|
|
4738
|
-
taskId:
|
|
5102
|
+
taskId: hostTaskId,
|
|
4739
5103
|
...(info.tool !== undefined ? { tool: info.tool } : {}),
|
|
4740
5104
|
sizeChars: info.sizeChars,
|
|
4741
5105
|
storeFallback: info.storeFallback,
|
|
@@ -4772,11 +5136,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4772
5136
|
: {}),
|
|
4773
5137
|
});
|
|
4774
5138
|
if (ccPass !== undefined) {
|
|
4775
|
-
recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId:
|
|
5139
|
+
recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: hostTaskId, trigger: "frontier" });
|
|
4776
5140
|
}
|
|
4777
5141
|
const chain = await runGuardChain({
|
|
4778
5142
|
edited, replayed, index: replay.index, microCompact, guardAt, charsPerToken, offloadStore,
|
|
4779
|
-
sessionId, taskId:
|
|
5143
|
+
sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal,
|
|
4780
5144
|
pendingProjectionObservations,
|
|
4781
5145
|
});
|
|
4782
5146
|
if ("earlyReturn" in chain) {
|
|
@@ -5011,7 +5375,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5011
5375
|
const effectiveReadFaceObserved = carrierReadFace();
|
|
5012
5376
|
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
5013
5377
|
const preparedHolder = {};
|
|
5014
|
-
const buildPrepared = () => ({ harness, session, sessionId, reminderMark, reminderDisclosureCounts, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), 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, microCompact, ...(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, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
5378
|
+
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), 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, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
5015
5379
|
const prepared = buildPrepared();
|
|
5016
5380
|
preparedHolder.current = prepared;
|
|
5017
5381
|
return prepared;
|