@sema-agent/core 7.1.0 → 7.3.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/agents/cross-session-envelope.d.ts +145 -0
  3. package/dist/agents/cross-session-envelope.js +195 -0
  4. package/dist/agents/cross-session-judge.d.ts +119 -0
  5. package/dist/agents/cross-session-judge.js +184 -0
  6. package/dist/agents/cross-session-ref.d.ts +52 -0
  7. package/dist/agents/cross-session-ref.js +64 -0
  8. package/dist/agents/list-agents-tool.d.ts +55 -0
  9. package/dist/agents/list-agents-tool.js +94 -0
  10. package/dist/agents/peer-admission.d.ts +17 -1
  11. package/dist/agents/peer-admission.js +19 -2
  12. package/dist/agents/peer-directory.d.ts +208 -0
  13. package/dist/agents/peer-directory.js +272 -0
  14. package/dist/agents/peer-session-drain.d.ts +159 -0
  15. package/dist/agents/peer-session-drain.js +245 -0
  16. package/dist/agents/send-message-tool.d.ts +44 -0
  17. package/dist/agents/send-message-tool.js +181 -16
  18. package/dist/agents/subagent-steps.d.ts +11 -0
  19. package/dist/agents/subagent-steps.js +27 -4
  20. package/dist/core/auto-mode-arming.d.ts +11 -0
  21. package/dist/core/auto-mode-arming.js +7 -1
  22. package/dist/core/auto-mode-prompt.d.ts +5 -0
  23. package/dist/core/auto-mode-prompt.js +2 -1
  24. package/dist/core/auto-mode-rebuild.d.ts +2 -1
  25. package/dist/core/auto-mode-rebuild.js +2 -0
  26. package/dist/core/checkpoint-store.d.ts +203 -3
  27. package/dist/core/checkpoint-store.js +60 -19
  28. package/dist/core/governance-codes.d.ts +1 -1
  29. package/dist/core/governance-codes.js +6 -0
  30. package/dist/core/hooks.d.ts +15 -8
  31. package/dist/core/hooks.js +6 -3
  32. package/dist/core/mailbox-store.d.ts +89 -2
  33. package/dist/core/mailbox-store.js +77 -2
  34. package/dist/core/permission-rule-consent.d.ts +72 -23
  35. package/dist/core/permission-rule-consent.js +115 -26
  36. package/dist/core/permission-rule-model.d.ts +254 -51
  37. package/dist/core/permission-rule-model.js +316 -55
  38. package/dist/core/permission-rule-org.js +13 -6
  39. package/dist/core/remote-env.d.ts +8 -1
  40. package/dist/core/runner/assemble-result.js +2 -1
  41. package/dist/core/runner/prepare-task.d.ts +59 -1
  42. package/dist/core/runner/prepare-task.js +414 -149
  43. package/dist/core/runner/prepare-workspace-restore.d.ts +6 -1
  44. package/dist/core/runner/prepare-workspace-restore.js +2 -1
  45. package/dist/core/runner/runtask.js +16 -5
  46. package/dist/core/runner/tool-output-projection.js +1 -0
  47. package/dist/core/store-contracts/mailbox-store-contract.d.ts +23 -0
  48. package/dist/core/store-contracts/mailbox-store-contract.js +157 -1
  49. package/dist/core/task-notification.d.ts +93 -5
  50. package/dist/core/task-notification.js +31 -4
  51. package/dist/core/tool-policy.d.ts +11 -0
  52. package/dist/core/types.d.ts +155 -21
  53. package/dist/core/untrusted-text.js +17 -1
  54. package/dist/core/wiring-manifest.d.ts +21 -0
  55. package/dist/core/wiring-manifest.js +1 -0
  56. package/dist/index.d.ts +14 -5
  57. package/dist/index.js +13 -4
  58. package/dist/stores/cc/mailbox-store.d.ts +1 -1
  59. package/dist/stores/cc/mailbox-store.js +13 -0
  60. package/dist/stores/file/adoption/marker.d.ts +1 -1
  61. package/dist/stores/file/mailbox-store.d.ts +57 -0
  62. package/dist/stores/file/mailbox-store.js +369 -18
  63. package/package.json +1 -1
  64. package/test/export-surface.snapshot.json +233 -1
@@ -23,7 +23,8 @@ import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send
23
23
  import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
24
24
  import { askApproverIdentity, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
25
25
  const PERSISTED_RULE_TOOL = "Bash";
26
- import { findAdmittingRule, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
26
+ const DIRECTORY_RULE_TOOL = "Read";
27
+ import { directoryRuleAdmits, eligiblePersisted, findAdmittingRule, lexicalNormalAbsolutePathOf, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
27
28
  import { normalizePersistedRule } from "../permission-rule-store.js";
28
29
  import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
29
30
  import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
@@ -76,12 +77,14 @@ import { capAggregateToolResults } from "../tool-result-budget.js";
76
77
  import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
77
78
  import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
78
79
  import { StubExecutionEnv } from "../stub-env.js";
79
- import { hasDestroy, isIsolated, isRemoteExecutionEnv, isSuspendable, missingRestoreSurface } from "../remote-env.js";
80
+ import { hasDestroy, isIsolated, isRemoteExecutionEnv, isSuspendable, missingRestoreSurface, RemoteExecutionError } from "../remote-env.js";
80
81
  import { settleTeardownLeg } from "./teardown-bounded.js";
81
82
  import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
82
83
  import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
83
84
  import { createMonitorTool } from "../../tools/monitor.js";
84
85
  import { createWorktreeTools } from "../../tools/worktree.js";
86
+ import { announcePeerLaneMount, bindPeerLaneDrain, listAgentsMountable, mountListAgents, peerLaneSendMessageSeats } from "../../agents/peer-session-drain.js";
87
+ import { CROSS_SESSION_CLASSIFIER_RULE } from "../../agents/cross-session-envelope.js";
85
88
  import { applyCompactionToReadFileState, isReadDedupStubResult, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
86
89
  import { decodeTextBytes } from "../../tools/fs/encoding.js";
87
90
  import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
@@ -197,6 +200,20 @@ function announceToolModelGate(onNotice, modelId, gate) {
197
200
  }
198
201
  }
199
202
  }
203
+ export const TOOL_MODEL_GATE_RESTORE_SENTENCE = "restore via TaskSpec.restoreGatedTools, SEMA_TOOL_MODEL_GATE=off, or RunnerDeps.toolModelGate: false";
204
+ function toolModelGateManifest(gate) {
205
+ let firstClass;
206
+ const removed = [];
207
+ for (const [gateClass, names] of gate.removedByClass) {
208
+ if (names.length === 0)
209
+ continue;
210
+ firstClass ??= gateClass;
211
+ for (const n of names)
212
+ if (!removed.includes(n))
213
+ removed.push(n);
214
+ }
215
+ return firstClass === undefined ? undefined : { class: firstClass, removed, restore: TOOL_MODEL_GATE_RESTORE_SENTENCE };
216
+ }
200
217
  function announceDeclaredMcpContentClasses(args) {
201
218
  for (const entry of args.entries ?? []) {
202
219
  const declaredClass = entry.contentOrigin;
@@ -713,6 +730,93 @@ function consumeInheritedAskGrant(grants, onAsk, humanReviewRef, decision, req)
713
730
  });
714
731
  return { action: "allow", presentedInput: grant.presented };
715
732
  }
733
+ function raceSettlementAgainstSignal(p, signal) {
734
+ const settled = p.then((value) => ({ tag: "value", value }), (error) => ({ tag: "threw", error }));
735
+ if (signal.aborted)
736
+ return Promise.resolve({ tag: "aborted" });
737
+ return new Promise((resolve) => {
738
+ let done = false;
739
+ const finish = (r) => {
740
+ if (done)
741
+ return;
742
+ done = true;
743
+ signal.removeEventListener("abort", onAbortEvent);
744
+ resolve(r);
745
+ };
746
+ const onAbortEvent = () => finish({ tag: "aborted" });
747
+ signal.addEventListener("abort", onAbortEvent, { once: true });
748
+ void settled.then(finish);
749
+ });
750
+ }
751
+ const PARK_COMPENSATION_TIMEOUT_MS = 30_000;
752
+ export async function compensateUnparkedPause(remoteEnv, snapshotId, io) {
753
+ const boundCtl = new AbortController();
754
+ const boundTimer = setTimeout(() => boundCtl.abort(), io.boundMs);
755
+ const bound = boundCtl.signal;
756
+ let phase = "resumeVM";
757
+ let restoreAttempts = 1;
758
+ let vmRunning = false;
759
+ const work = (async () => {
760
+ const { outcome: back, attempts: backAttempts } = await restoreWorkspaceWithRetry(remoteEnv, snapshotId, { abortSignal: bound }, (attempt) => (restoreAttempts = attempt));
761
+ if (!back.ok) {
762
+ io.noteFailure(remoteEnvFailureNote("resumeVM", back.error, backAttempts));
763
+ io.disclose(back.error);
764
+ return { ok: false, reason: `resumeVM failed (${back.error.code}) while compensating an unparked pause` };
765
+ }
766
+ vmRunning = true;
767
+ if (bound.aborted) {
768
+ return { ok: false, reason: "resumeVM settled only after the decision bound" };
769
+ }
770
+ phase = "postResumeInit";
771
+ const init = await remoteEnv.postResumeInit({ abortSignal: bound });
772
+ if (!init.ok) {
773
+ io.noteFailure(remoteEnvFailureNote("postResumeInit", init.error, 1));
774
+ io.disclose(init.error);
775
+ return { ok: false, reason: `postResumeInit failed (${init.error.code}) while compensating an unparked pause` };
776
+ }
777
+ return { ok: true };
778
+ })();
779
+ const raced = await raceSettlementAgainstSignal(work, bound);
780
+ clearTimeout(boundTimer);
781
+ if (raced.tag === "value")
782
+ return raced.value;
783
+ if (raced.tag === "threw") {
784
+ io.noteFailure(remoteEnvFailureNote(phase, raced.error instanceof RemoteExecutionError
785
+ ? raced.error
786
+ : new RemoteExecutionError("unknown", `the pause compensation's ${phase} call threw: ${raced.error instanceof Error ? raced.error.message : String(raced.error)}`), phase === "resumeVM" ? restoreAttempts : 1));
787
+ io.disclose(raced.error);
788
+ return {
789
+ ok: false,
790
+ reason: "the pause compensation threw (adapter contract violation; the deployment's error face carries the exception)",
791
+ };
792
+ }
793
+ const timedOut = new RemoteExecutionError("timeout", `the pause compensation's ${phase} call did not settle within the ${io.boundMs}ms decision bound`);
794
+ io.noteFailure(remoteEnvFailureNote(phase, timedOut, phase === "resumeVM" ? restoreAttempts : 1));
795
+ io.disclose(timedOut);
796
+ const destroyOwnerlessLate = () => {
797
+ io.disclose(new Error(`park compensation settled after its ${io.boundMs}ms decision bound: the VM is running with no run ` +
798
+ `left to own it — destroying the env (an ownerless running VM must not linger; the run already took ` +
799
+ `the fail-closed arm when the bound fired)`));
800
+ void Promise.resolve()
801
+ .then(() => remoteEnv.destroy())
802
+ .catch((destroyErr) => io.disclose(destroyErr));
803
+ };
804
+ void work.then((late) => {
805
+ if (vmRunning) {
806
+ destroyOwnerlessLate();
807
+ return;
808
+ }
809
+ void late;
810
+ }, (lateErr) => {
811
+ io.disclose(lateErr);
812
+ if (vmRunning)
813
+ destroyOwnerlessLate();
814
+ });
815
+ return {
816
+ ok: false,
817
+ reason: `the pause compensation did not settle within ${io.boundMs}ms — treated as failed (a late-restored VM is destroyed; a late restore failure is disclosed)`,
818
+ };
819
+ }
716
820
  function raceAbort(p, signal, onAbort) {
717
821
  if (signal.aborted)
718
822
  return Promise.resolve(onAbort());
@@ -869,6 +973,87 @@ function persistedRuleHitOf(admitting) {
869
973
  ? undefined
870
974
  : { rules: admitting.map((r) => ({ rule: r.rule, dots: r.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) })) };
871
975
  }
976
+ function directoryRuleLaneAnswer(table, args, ctx) {
977
+ const filePath = args?.file_path;
978
+ if (typeof filePath !== "string" || filePath === "")
979
+ return undefined;
980
+ const spelled = filePath.startsWith("/") ? filePath : `${(ctx.liveCwd ?? ctx.root ?? "").replace(/\/+$/, "")}/${filePath}`;
981
+ const target = lexicalNormalAbsolutePathOf(spelled);
982
+ if (target === undefined)
983
+ return undefined;
984
+ const hit = table.find((r) => eligiblePersisted(r, { tool: DIRECTORY_RULE_TOOL, cwd: ctx.root, sessionId: ctx.sessionId }) && directoryRuleAdmits(r, target));
985
+ return hit !== undefined ? persistedRuleHitOf([hit]) : undefined;
986
+ }
987
+ function makeRuleOffersOf(cfg) {
988
+ return (toolName, args, ask) => {
989
+ if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL)
990
+ return {};
991
+ if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared)
992
+ return {};
993
+ const closedDoor = (() => {
994
+ if (ask?.requiresRealApproval === true)
995
+ return "mandated";
996
+ if (ask?.persistedRuleShadowed !== undefined)
997
+ return "shadowed";
998
+ if (ask?.decisionReason === "hook")
999
+ return "mandated";
1000
+ if (ask?.matchedAskRule !== undefined)
1001
+ return "shadowed";
1002
+ if (ask?.inheritedUnresolved === true)
1003
+ return "mandated";
1004
+ if (ask?.ancestorResolved === true)
1005
+ return "mandated";
1006
+ return undefined;
1007
+ })();
1008
+ if (closedDoor !== undefined)
1009
+ return { ruleOffersAbsence: closedDoor };
1010
+ if (persistedRuleMandateOf({
1011
+ egress: cfg.egressTools.has(toolName),
1012
+ irreversibility: cfg.irreversibilityTier.get(toolName),
1013
+ shellGated: cfg.shellGatedBash,
1014
+ probeMandated: ask?.probeMandated === true,
1015
+ }) !== undefined) {
1016
+ return { ruleOffersAbsence: "mandated" };
1017
+ }
1018
+ const command = args?.command;
1019
+ if (typeof command !== "string")
1020
+ return { ruleOffersAbsence: "lane_cannot_speak" };
1021
+ const offers = suggestRulesForCommand(command, {
1022
+ ...(ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : {}),
1023
+ ...(cfg.taskRoot !== undefined ? { scope: { kind: "project", root: cfg.taskRoot }, cwd: cfg.taskRoot } : {}),
1024
+ ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}),
1025
+ ...(cfg.deniesDirectoryRead !== undefined ? { deniesDirectoryRead: cfg.deniesDirectoryRead } : {}),
1026
+ });
1027
+ return offers.length > 0
1028
+ ? { ruleOffers: offers, ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}) }
1029
+ : { ruleOffersAbsence: "lane_cannot_speak" };
1030
+ };
1031
+ }
1032
+ function makeOrgAdjudicationLane(overlay, questionToolMounted, onRevisionDefect) {
1033
+ if (overlay === undefined)
1034
+ return undefined;
1035
+ return {
1036
+ adjudicate: async (req) => {
1037
+ if (req.toolName === ASK_USER_QUESTION_TOOL_NAME && questionToolMounted)
1038
+ return { status: "available" };
1039
+ let resolution;
1040
+ try {
1041
+ resolution = await overlay.resolve();
1042
+ }
1043
+ catch (err) {
1044
+ return { status: "unavailable", disclosures: [`the org rule overlay threw: ${err instanceof Error ? err.message : String(err)}`] };
1045
+ }
1046
+ if (resolution.status === "unavailable")
1047
+ return { status: "unavailable", disclosures: resolution.disclosures };
1048
+ const revisionCell = orgRevisionEvidenceOf(resolution, onRevisionDefect);
1049
+ const command = req.args?.command;
1050
+ if (req.toolName !== PERSISTED_RULE_TOOL || typeof command !== "string")
1051
+ return { status: "available", ...revisionCell };
1052
+ const verdict = orgRuleVerdictFor(resolution.rules, { tool: req.toolName, command });
1053
+ return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
1054
+ },
1055
+ };
1056
+ }
872
1057
  function cwdConflictsRestoreError(requestedCwd) {
873
1058
  const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
874
1059
  `the restored workspace's own mount path is authoritative for the task root, so a requested cwd on this leg ` +
@@ -992,14 +1177,101 @@ function refuseRequireExistingWithoutSession(spec) {
992
1177
  throw e;
993
1178
  }
994
1179
  }
1180
+ function screenAutoModeCap(caps, onError, sessionId) {
1181
+ if (caps === undefined)
1182
+ return { caps, faulted: false };
1183
+ if (typeof caps !== "object" || caps === null || Array.isArray(caps)) {
1184
+ onError?.(new Error(`runtimeCapsResolver returned a non-record value (${JSON.stringify(caps)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain). Return a RuntimeCaps object or undefined.`), { phase: "config", sessionId });
1185
+ return { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, faulted: true };
1186
+ }
1187
+ const record = caps;
1188
+ if (record.autoMode === undefined || typeof record.autoMode === "boolean")
1189
+ return { caps: record, faulted: false };
1190
+ onError?.(new Error(`runtimeCapsResolver returned a non-boolean autoMode (${JSON.stringify(record.autoMode)}) — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the original chain). Return true, false, or omit the key.`), { phase: "config", sessionId });
1191
+ return { caps: { ...record, autoMode: false }, faulted: true };
1192
+ }
1193
+ function mintPersistedArming(am, laneRule, classifierSystemPrompt, onError, sessionId) {
1194
+ const arming = autoModeArmingRecipeOf(laneRule ? { ...am, crossSessionMessagesRule: true } : am, {
1195
+ promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`,
1196
+ });
1197
+ if (arming === undefined) {
1198
+ onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " +
1199
+ "(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " +
1200
+ "NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " +
1201
+ "conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId });
1202
+ }
1203
+ return arming;
1204
+ }
1205
+ function autoModeLatchHealthy(d) {
1206
+ try {
1207
+ return d.breakerOpen() !== true && typeof d.consecutiveFailures === "function" && d.consecutiveFailures() === 0;
1208
+ }
1209
+ catch {
1210
+ return false;
1211
+ }
1212
+ }
1213
+ function refuseInvalidAutoModeSeat(spec) {
1214
+ if (spec.autoModeRequested !== undefined && spec.autoModeRequested !== true) {
1215
+ const e = new Error(`autoModeRequested must be the literal true when present (got ${JSON.stringify(spec.autoModeRequested)}) — omit it for a non-auto task; a non-boolean or false value is refused rather than read as either polarity.`);
1216
+ e.code = "config.auto_mode_requested_invalid";
1217
+ throw e;
1218
+ }
1219
+ return spec.autoModeRequested === true;
1220
+ }
1221
+ function announceDurableGateUnavailable(args) {
1222
+ if (!args.forceDurableGate || args.storeWired)
1223
+ return;
1224
+ const cause = args.taskStoreNull ? "task_store_null" : "no_deployment_store";
1225
+ const inheritedContentMandate = (args.parentConstraints ?? []).some((pc) => pc.contentMandate === true);
1226
+ const liveQuestionFace = args.liveQuestionFace && !inheritedContentMandate;
1227
+ deliverEngineNotice(args.onNotice, {
1228
+ code: "config.durable_gate_unavailable",
1229
+ message: `Durable approval gate mandated but unavailable: the runtime entitlement forceDurableGate is in force for this ` +
1230
+ `principal, but ${cause === "task_store_null" ? "this task disabled the checkpoint store (checkpointStore: null)" : "this deployment wired no checkpoint store"}, ` +
1231
+ `so no interactive ask of this task can be parked for a durable answer. Asks resolve on the live chain instead ` +
1232
+ `(${args.liveApprover ? "a live approver answers permission asks in-stream" : "permission asks are denied fail-closed — no live approver is wired"}; ` +
1233
+ `${liveQuestionFace ? "a live question face answers AskUserQuestion in-stream" : args.liveQuestionFace ? "AskUserQuestion is withheld from the live face by an inherited durable mandate and refused" : "AskUserQuestion has no live face and is refused"}) ` +
1234
+ `and NO durable approval record is written. ${cause === "task_store_null" ? "Run this task with its store, or withdraw the entitlement for principals whose runs are machine-started." : "Wire a checkpoint store on this deployment, or withdraw the entitlement for principals served here."}`,
1235
+ detail: {
1236
+ sessionId: args.sessionId,
1237
+ runId: args.runId,
1238
+ ...(args.principal ? { principal: args.principal } : {}),
1239
+ cause,
1240
+ liveApprover: args.liveApprover,
1241
+ liveQuestionFace,
1242
+ },
1243
+ });
1244
+ }
1245
+ function buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) {
1246
+ if (delegationForRevive === undefined)
1247
+ return undefined;
1248
+ return async (req) => {
1249
+ const out = await delegationForRevive.execute({
1250
+ description: req.row.description ?? "revived teammate",
1251
+ prompt: req.prompt,
1252
+ run_in_background: true,
1253
+ ...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
1254
+ ...(req.row.name !== undefined ? { name: req.row.name } : {}),
1255
+ ...(req.row.model !== undefined ? { model: req.row.model } : {}),
1256
+ }, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
1257
+ const o = (typeof out === "string" ? { content: out } : out);
1258
+ return {
1259
+ content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
1260
+ ...(o.isError === true ? { isError: true } : {}),
1261
+ ...(o.details !== undefined ? { details: o.details } : {}),
1262
+ };
1263
+ };
1264
+ }
995
1265
  export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
996
1266
  const runId = uuidv7();
997
1267
  if (runIdSink !== undefined)
998
1268
  runIdSink.runId = runId;
999
1269
  const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
1000
1270
  spec = doors.spec;
1271
+ const autoModeSeat = refuseInvalidAutoModeSeat(spec);
1001
1272
  const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
1002
1273
  announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
1274
+ const modelGateManifest = toolModelGateManifest(doors.modelGate);
1003
1275
  const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes, ownToolNames } = prepareSafetyScan({ spec, deps });
1004
1276
  let shellGatedBash = false;
1005
1277
  let shellGatedMonitor = false;
@@ -1019,6 +1291,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1019
1291
  const taskScope = internals?.registryScope ?? spec.principal ?? "default";
1020
1292
  internals?.peerSelfRef?.addAxis("s", sessionId);
1021
1293
  internals?.peerSelfRef?.addAxis("t", hostTaskId);
1294
+ const peerLaneActive = announcePeerLaneMount(deps, { sessionId, runId });
1295
+ const peerLaneRefs = {};
1296
+ const peerSendMessageBuiltIn = runnerSelf !== undefined && !(toolFaceSnapshot.exclude ?? []).includes(SEND_MESSAGE_TOOL_NAME) && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME);
1022
1297
  const { reminderMark, reminderDisclosureCounts } = await adoptReminderMark(session, sessionId, resume?.seed.reminderMark, internals?.reminderMark, deps.onError);
1023
1298
  const offloadScope = spec.principal ?? "default";
1024
1299
  if (internals?.registryScope !== undefined && internals.registryScope !== offloadScope) {
@@ -1406,6 +1681,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1406
1681
  ? liveShellGate
1407
1682
  : seedShellGate;
1408
1683
  const inheritedParentConstraints = liveInheritedGate?.parentConstraints;
1684
+ const autoModeIntent = autoModeSeat || liveInheritedGate?.autoModeRequested === true || seedInheritedGate?.autoModeRequested === true;
1409
1685
  const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
1410
1686
  const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
1411
1687
  const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
@@ -1518,6 +1794,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1518
1794
  return {
1519
1795
  ...(ancestorRules.length > 0 ? { ancestorRules } : {}),
1520
1796
  ...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
1797
+ ...(autoModeIntent ? { autoModeRequested: true } : {}),
1521
1798
  ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
1522
1799
  admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
1523
1800
  ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
@@ -1658,9 +1935,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1658
1935
  }
1659
1936
  catch (err) {
1660
1937
  deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
1661
- runtimeCaps = { allowWorkflows: false, allowFork: false };
1938
+ runtimeCaps = { allowWorkflows: false, allowFork: false, autoMode: false };
1662
1939
  runtimeCapsFaulted = true;
1663
1940
  }
1941
+ const screened = screenAutoModeCap(runtimeCaps, deps.onError, sessionId);
1942
+ runtimeCaps = screened.caps;
1943
+ runtimeCapsFaulted ||= screened.faulted;
1664
1944
  }
1665
1945
  let complianceDenies = new Set();
1666
1946
  let complianceDegraded = false;
@@ -1697,7 +1977,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1697
1977
  }
1698
1978
  agentForkDenial = forkGovernanceDenial(spec.enableFork, runtimeCaps?.allowFork);
1699
1979
  observersActive = runtimeCaps?.allowObservers === true;
1700
- if (runtimeCaps?.autoMode === true && deps.autoMode !== undefined) {
1980
+ if (autoModeIntent && deps.autoMode !== undefined && runtimeCaps?.autoMode !== false) {
1701
1981
  const am = deps.autoMode;
1702
1982
  let classifierModel;
1703
1983
  try {
@@ -1709,7 +1989,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1709
1989
  if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId })) {
1710
1990
  classifierModel = model;
1711
1991
  }
1712
- const classifierSystemPrompt = buildAutoModePrompt(am);
1992
+ const classifierLaneRule = peerLaneActive && peerSendMessageBuiltIn;
1993
+ const classifierSystemPrompt = buildAutoModePrompt(classifierLaneRule ? { ...am, crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : am);
1713
1994
  const classifierRuntime = brainToRuntime(deps.brain);
1714
1995
  autoModeDecider = createAutoModeDecider({
1715
1996
  ...(am.timeoutMs !== undefined ? { timeoutMs: am.timeoutMs } : {}),
@@ -1732,17 +2013,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1732
2013
  .join("");
1733
2014
  },
1734
2015
  });
1735
- if (am.persistArming === true) {
1736
- autoModeArming = autoModeArmingRecipeOf(am, {
1737
- promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`,
1738
- });
1739
- if (autoModeArming === undefined) {
1740
- deps.onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " +
1741
- "(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " +
1742
- "NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " +
1743
- "conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId });
1744
- }
1745
- }
2016
+ if (am.persistArming === true)
2017
+ autoModeArming = mintPersistedArming(am, classifierLaneRule, classifierSystemPrompt, deps.onError, sessionId);
1746
2018
  }
1747
2019
  const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps);
1748
2020
  const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false;
@@ -2037,32 +2309,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2037
2309
  handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
2038
2310
  shellGatedBash = handsReadFace.shellGatedBash;
2039
2311
  shellGatedMonitor = handsReadFace.shellGatedMonitor;
2040
- if (backgroundTaskToolsActive || workflowToolsActive) {
2041
- toolEffects.set("TaskOutput", "read");
2042
- toolEffects.set("TaskStop", "write");
2043
- tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, toolResultStore: offloadStore })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
2312
+ const delegationSurfaceActive = backgroundTaskToolsActive || workflowToolsActive;
2313
+ if (delegationSurfaceActive || internals?.parentNotify !== undefined || peerLaneActive) {
2314
+ if (delegationSurfaceActive) {
2315
+ toolEffects.set("TaskOutput", "read");
2316
+ toolEffects.set("TaskStop", "write");
2317
+ tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, toolResultStore: offloadStore })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
2318
+ }
2044
2319
  if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
2045
2320
  toolEffects.set(SEND_MESSAGE_TOOL_NAME, "write");
2046
2321
  axisExplicitNegatives.set(SEND_MESSAGE_TOOL_NAME, { ...axisExplicitNegatives.get(SEND_MESSAGE_TOOL_NAME), egress: false });
2047
2322
  const delegationForRevive = (spec.tools ?? []).find((t) => t.agentListing !== undefined);
2048
- const reviveSpawn = delegationForRevive !== undefined && deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined
2049
- ? async (req) => {
2050
- const out = await delegationForRevive.execute({
2051
- description: req.row.description ?? "revived teammate",
2052
- prompt: req.prompt,
2053
- run_in_background: true,
2054
- ...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
2055
- ...(req.row.name !== undefined ? { name: req.row.name } : {}),
2056
- ...(req.row.model !== undefined ? { model: req.row.model } : {}),
2057
- }, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
2058
- const o = (typeof out === "string" ? { content: out } : out);
2059
- return {
2060
- content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
2061
- ...(o.isError === true ? { isError: true } : {}),
2062
- ...(o.details !== undefined ? { details: o.details } : {}),
2063
- };
2064
- }
2065
- : undefined;
2323
+ const reviveSpawn = deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined ? buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) : undefined;
2066
2324
  tools.push(firstPartyOffload(createSendMessageTool({
2067
2325
  runner: runnerSelf,
2068
2326
  registry: defaultTaskRegistry,
@@ -2070,7 +2328,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2070
2328
  owner: hostTaskId,
2071
2329
  scope: taskScope,
2072
2330
  ...(sessionId !== undefined ? { sessionId } : {}),
2073
- ...(internals?.onTaskNotification !== undefined ? { notify: internals.onTaskNotification } : {}), ...(spec.oneShot !== undefined ? { oneShot: spec.oneShot } : {}),
2331
+ ...(internals?.onTaskNotification !== undefined ? { notify: internals.onTaskNotification } : {}), ...(spec.oneShot !== undefined ? { oneShot: spec.oneShot } : {}), retrievalToolMounted: delegationSurfaceActive,
2074
2332
  ...(internals?.onSubagentSpawn !== undefined ? { sink: internals.onSubagentSpawn } : {}),
2075
2333
  ...(internals?.parentNotify !== undefined
2076
2334
  ? { uplink: internals.parentNotify, ...(internals.parentPeerRef !== undefined ? { uplinkRecipient: internals.parentPeerRef } : {}) }
@@ -2088,6 +2346,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2088
2346
  ...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
2089
2347
  ...(deps.mailboxStore !== undefined ? { mailbox: deps.mailboxStore } : {}),
2090
2348
  ...(reviveSpawn !== undefined ? { reviveSpawn } : {}),
2349
+ ...(peerLaneActive ? peerLaneSendMessageSeats({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, ...(internals?.explicitAgentName !== undefined ? { name: internals.explicitAgentName } : {}), refs: peerLaneRefs, listingMounted: listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) }) : {}),
2091
2350
  onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
2092
2351
  onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
2093
2352
  code: "delegation.transcript_integrity",
@@ -2095,7 +2354,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2095
2354
  detail: { handle, ...(scope !== undefined ? { scope } : {}) },
2096
2355
  }),
2097
2356
  })));
2098
- if (!(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
2357
+ if (delegationSurfaceActive && !(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
2099
2358
  tools.push(firstPartyOffload(createAgentTranscriptTool({
2100
2359
  runner: runnerSelf,
2101
2360
  registry: defaultTaskRegistry,
@@ -2113,6 +2372,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2113
2372
  }
2114
2373
  }
2115
2374
  }
2375
+ const listAgents = peerLaneActive && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
2376
+ if (listAgents !== undefined)
2377
+ tools.push(firstPartyOffload(listAgents));
2116
2378
  if (backgroundTaskToolsActive) {
2117
2379
  toolEffects.set("Monitor", "write");
2118
2380
  envHandToolNames.add("Monitor");
@@ -2159,10 +2421,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2159
2421
  const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
2160
2422
  const contentAskBindings = new Map();
2161
2423
  const CONTENT_ASK_BINDING_CAP = 32;
2424
+ const checkpointStore = resolveCheckpointStore(spec, deps);
2425
+ const questionParkStoreWired = checkpointStore !== undefined;
2426
+ const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
2162
2427
  const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
2163
2428
  mountedQuestionTool !== undefined &&
2164
2429
  tools.includes(mountedQuestionTool) &&
2165
- runtimeCaps?.forceDurableGate !== true &&
2430
+ !questionDurableMandateBinding &&
2166
2431
  !inheritedUnavailableAsks.has(toolCallId);
2167
2432
  const lateStrandedAnswers = [];
2168
2433
  const discloseStrandedAnswers = (records, why) => {
@@ -2189,8 +2454,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2189
2454
  byDelivery.set(r.deliveryId, r);
2190
2455
  return [...byDelivery.values()];
2191
2456
  };
2192
- const durableQuestionFace = resolveCheckpointStore(spec, deps) !== undefined &&
2193
- (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
2457
+ const durableQuestionFace = questionParkStoreWired && (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
2194
2458
  const mountedQuestionFace = liveQuestionFace !== undefined
2195
2459
  ? async (req, signal) => {
2196
2460
  const bound = contentAskBindings.get(req.toolCallId);
@@ -3659,10 +3923,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3659
3923
  const anonymous = spec.principal === undefined || spec.principal === "";
3660
3924
  if (anonymous && !localOwnerDeclared)
3661
3925
  return undefined;
3662
- if (req.toolName !== PERSISTED_RULE_TOOL)
3663
- return undefined;
3664
- const command = req.args?.command;
3665
- if (typeof command !== "string")
3926
+ if (req.toolName !== PERSISTED_RULE_TOOL && req.toolName !== DIRECTORY_RULE_TOOL)
3666
3927
  return undefined;
3667
3928
  let listed;
3668
3929
  try {
@@ -3682,76 +3943,32 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3682
3943
  return { unreadable: true };
3683
3944
  }
3684
3945
  const table = await spliceSessionOverlayRows(deps.sessionPermissionRules, sessionId, listed.rules, deps.tracer, hostTaskId);
3685
- const admitting = findAdmittingRule(table, { tool: req.toolName, command, cwd: root, sessionId });
3946
+ if (req.toolName === DIRECTORY_RULE_TOOL) {
3947
+ return directoryRuleLaneAnswer(table, req.args, { root, sessionId, liveCwd: handsCwdRef?.current });
3948
+ }
3949
+ const command = req.args?.command;
3950
+ if (typeof command !== "string")
3951
+ return undefined;
3952
+ const admitting = findAdmittingRule(table, { tool: req.toolName, command, cwd: root, sessionId, ...(handsCwdRef?.current !== undefined ? { execCwd: handsCwdRef.current } : {}) });
3686
3953
  if (admitting !== undefined)
3687
3954
  return persistedRuleHitOf(admitting);
3688
- const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: root, sessionId });
3955
+ const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: root, sessionId, ...(handsCwdRef?.current !== undefined ? { execCwd: handsCwdRef.current } : {}) });
3689
3956
  return coverage !== undefined ? { segmentCoverage: coverage } : undefined;
3690
3957
  },
3691
3958
  };
3692
3959
  })();
3693
- const permissionRuleOrgLane = (() => {
3694
- const overlay = deps.permissionRuleOrg;
3695
- if (overlay === undefined)
3696
- return undefined;
3697
- return {
3698
- adjudicate: async (req) => {
3699
- if (req.toolName === ASK_USER_QUESTION_TOOL_NAME && questionToolMounted)
3700
- return { status: "available" };
3701
- let resolution;
3702
- try {
3703
- resolution = await overlay.resolve();
3704
- }
3705
- catch (err) {
3706
- return { status: "unavailable", disclosures: [`the org rule overlay threw: ${err instanceof Error ? err.message : String(err)}`] };
3707
- }
3708
- if (resolution.status === "unavailable")
3709
- return { status: "unavailable", disclosures: resolution.disclosures };
3710
- const revisionCell = orgRevisionEvidenceOf(resolution, (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }));
3711
- const command = req.args?.command;
3712
- if (req.toolName !== PERSISTED_RULE_TOOL || typeof command !== "string")
3713
- return { status: "available", ...revisionCell };
3714
- const verdict = orgRuleVerdictFor(resolution.rules, { tool: req.toolName, command });
3715
- return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
3716
- },
3717
- };
3718
- })();
3719
- const ruleOffersOf = (toolName, args, ask) => {
3720
- if (permissionRuleLane === undefined || toolName !== PERSISTED_RULE_TOOL)
3721
- return {};
3722
- if ((spec.principal === undefined || spec.principal === "") && deps.localOwnerRules !== true)
3723
- return {};
3724
- const closedDoor = (() => {
3725
- if (ask?.requiresRealApproval === true)
3726
- return "mandated";
3727
- if (ask?.persistedRuleShadowed !== undefined)
3728
- return "shadowed";
3729
- if (ask?.decisionReason === "hook")
3730
- return "mandated";
3731
- if (ask?.matchedAskRule !== undefined)
3732
- return "shadowed";
3733
- if (ask?.inheritedUnresolved === true)
3734
- return "mandated";
3735
- if (ask?.ancestorResolved === true)
3736
- return "mandated";
3737
- return undefined;
3738
- })();
3739
- if (closedDoor !== undefined)
3740
- return { ruleOffersAbsence: closedDoor };
3741
- if (persistedRuleMandateOf({
3742
- egress: egressTools.has(toolName),
3743
- irreversibility: irreversibilityTier.get(toolName),
3744
- shellGated: shellGatedBash,
3745
- probeMandated: ask?.probeMandated === true,
3746
- }) !== undefined) {
3747
- return { ruleOffersAbsence: "mandated" };
3748
- }
3749
- const command = args?.command;
3750
- if (typeof command !== "string")
3751
- return { ruleOffersAbsence: "lane_cannot_speak" };
3752
- const offers = suggestRulesForCommand(command, ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : undefined);
3753
- return offers.length > 0 ? { ruleOffers: offers } : { ruleOffersAbsence: "lane_cannot_speak" };
3754
- };
3960
+ const permissionRuleOrgLane = makeOrgAdjudicationLane(deps.permissionRuleOrg, questionToolMounted, (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }));
3961
+ const ruleOffersOf = makeRuleOffersOf({
3962
+ laneArmed: permissionRuleLane !== undefined,
3963
+ principal: spec.principal,
3964
+ localOwnerDeclared: deps.localOwnerRules === true,
3965
+ egressTools,
3966
+ irreversibilityTier,
3967
+ shellGatedBash,
3968
+ taskRoot: taskRootFinal,
3969
+ cwdRef: handsCwdRef,
3970
+ deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
3971
+ });
3755
3972
  const frozenClassifierExcluded = (d) => d.decisionReason === "hook" || d.matchedAskRule !== undefined;
3756
3973
  const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal, ancestorDecider) => {
3757
3974
  let editArgs = edit;
@@ -4147,7 +4364,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4147
4364
  }
4148
4365
  const usageGovernance = buildUsageGovernance(usageWindows, deps, spec.principal, sessionId);
4149
4366
  const platformSuspendArmed = durableSuspendInfraReady && (envLifetimeSuspendAt !== undefined || usageGovernance !== undefined);
4150
- const checkpointStore = resolveCheckpointStore(spec, deps);
4151
4367
  const durableApproval = spec.durableApproval ??
4152
4368
  (runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
4153
4369
  const wiringQuestionSeam = resolveQuestionSeam(spec, deps);
@@ -4183,8 +4399,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4183
4399
  complianceWired: deps.compliancePostureResolver !== undefined,
4184
4400
  memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
4185
4401
  retentionPolicyWired: deps.retentionPolicy !== undefined,
4402
+ ...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
4186
4403
  });
4404
+ peerLaneRefs.askEffective = wiringManifest.ask.effective;
4405
+ if (peerLaneActive && internals?.onTaskNotification !== undefined) {
4406
+ bindPeerLaneDrain(harness, { deps, sessionId, runId, scope: taskScope, inject: internals.onTaskNotification, ownTokens: () => internals?.peerSelfRef?.current.ownTokens ?? [], refs: peerLaneRefs });
4407
+ }
4187
4408
  const parkLaneArmed = wiringManifest.parkLane.effective === true;
4409
+ announceDurableGateUnavailable({ onNotice: deps.onNotice, forceDurableGate: runtimeCaps?.forceDurableGate === true, storeWired: checkpointStore !== undefined, taskStoreNull: spec.checkpointStore === null, liveApprover: isLiveApproverSeat(frozenOnAsk), liveQuestionFace: liveQuestionFace !== undefined, parentConstraints: liveInheritedGate?.parentConstraints, sessionId, runId, principal: spec.principal });
4188
4410
  const hookIdentity = mintHookInvocationIdentity({
4189
4411
  sessionId,
4190
4412
  taskId: hostTaskId,
@@ -4324,8 +4546,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4324
4546
  const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
4325
4547
  ? constraintChainDigest(constraintChain)
4326
4548
  : seedInheritedGate?.constraintDigest;
4549
+ const autoModeIntentCarried = autoModeIntent && (autoModeDecider === undefined || autoModeLatchHealthy(autoModeDecider));
4327
4550
  return inheritedAncestorRules !== undefined ||
4328
4551
  inheritedShellGate !== undefined ||
4552
+ autoModeIntentCarried ||
4329
4553
  inheritedAdmittedOrgScopes !== undefined ||
4330
4554
  ownOrgVerdictRef.current !== undefined ||
4331
4555
  orgGovernedProvenance ||
@@ -4333,6 +4557,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4333
4557
  ? {
4334
4558
  ...(inheritedAncestorRules !== undefined ? { ancestorRules: structuredClone(inheritedAncestorRules) } : {}),
4335
4559
  ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
4560
+ ...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
4336
4561
  ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
4337
4562
  ...(ownOrgVerdictRef.current !== undefined
4338
4563
  ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
@@ -4359,11 +4584,33 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4359
4584
  placementRootSessionId: stampPlacementRootSessionId(placementRootResolved),
4360
4585
  externalContentTarget: externalContentTargetActive ? true : undefined,
4361
4586
  });
4362
- const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle) => {
4587
+ const compensatePausedVM = (remoteEnv, snapshotId) => compensateUnparkedPause(remoteEnv, snapshotId, {
4588
+ boundMs: PARK_COMPENSATION_TIMEOUT_MS,
4589
+ noteFailure: (note) => remoteEnvFailures.push(note),
4590
+ disclose: (err) => {
4591
+ try {
4592
+ deps.onError?.(err, { phase: "config", sessionId });
4593
+ }
4594
+ catch {
4595
+ }
4596
+ },
4597
+ });
4598
+ const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle, cutSignal) => {
4363
4599
  if (!checkpointStore)
4364
- return { ok: false };
4600
+ return { tag: "absent" };
4365
4601
  if (memoryEngineSession)
4366
4602
  await memoryEngineSession.harvest("checkpoint");
4603
+ if (cutSignal?.aborted) {
4604
+ if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
4605
+ const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
4606
+ if (!back.ok) {
4607
+ abortController.abort();
4608
+ void harness.abort();
4609
+ return { tag: "compensation_failed", reason: back.reason };
4610
+ }
4611
+ }
4612
+ return { tag: "cut" };
4613
+ }
4367
4614
  const announceCommittedScreeningPark = () => {
4368
4615
  const committedCount = cp.state.inheritedGate?.parentConstraintCount;
4369
4616
  if (cp.state.inheritedGate?.requiresParentConstraint === true &&
@@ -4401,7 +4648,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4401
4648
  try {
4402
4649
  await checkpointStore.put(token, cp);
4403
4650
  announceCommittedScreeningPark();
4404
- return { ok: true };
4651
+ return { tag: "committed" };
4405
4652
  }
4406
4653
  catch (putErr) {
4407
4654
  deps.onError?.(putErr, { phase: "config", sessionId });
@@ -4417,7 +4664,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4417
4664
  catch {
4418
4665
  }
4419
4666
  announceCommittedScreeningPark();
4420
- return { ok: true };
4667
+ return { tag: "committed" };
4421
4668
  }
4422
4669
  if (confirmed === "unknown") {
4423
4670
  const unknownReason = "the approval checkpoint's state cannot be established (the store rejected the write and then could not be read back; a committed-but-unacknowledged row may exist) — the run is stopped rather than continued past an approval whose durable record is unknown";
@@ -4432,29 +4679,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4432
4679
  }
4433
4680
  abortController.abort();
4434
4681
  void harness.abort();
4435
- return { ok: false, reason: unknownReason };
4682
+ return { tag: "unknown", reason: unknownReason };
4436
4683
  }
4437
4684
  const reason = "the approval checkpoint could not be persisted (the store rejected the write; the deployment's error face carries the store's own message)";
4438
4685
  if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
4439
- const { outcome: back, attempts: backAttempts } = await restoreWorkspaceWithRetry(remoteEnv, remoteHandle.snapshotId, {
4440
- abortSignal: abortController.signal,
4441
- });
4442
- if (back.ok) {
4443
- const init = await remoteEnv.postResumeInit();
4444
- if (init.ok)
4445
- return { ok: false, reason };
4446
- remoteEnvFailures.push(remoteEnvFailureNote("postResumeInit", init.error, 1));
4447
- deps.onError?.(init.error, { phase: "config", sessionId });
4448
- }
4449
- else {
4450
- remoteEnvFailures.push(remoteEnvFailureNote("resumeVM", back.error, backAttempts));
4451
- deps.onError?.(back.error, { phase: "config", sessionId });
4452
- }
4686
+ const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
4687
+ if (back.ok)
4688
+ return { tag: "absent", reason };
4453
4689
  abortController.abort();
4454
4690
  void harness.abort();
4455
- return { ok: false, reason };
4691
+ return { tag: "compensation_failed", reason };
4456
4692
  }
4457
- return { ok: false, reason };
4693
+ return { tag: "absent", reason };
4458
4694
  }
4459
4695
  };
4460
4696
  const suspendLoopCapHit = (count, cap, detail) => {
@@ -4536,7 +4772,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4536
4772
  ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}),
4537
4773
  ...(spec.principal ? { principal: spec.principal } : {}),
4538
4774
  };
4539
- if (!(await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).ok)
4775
+ if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed")
4540
4776
  return false;
4541
4777
  publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction);
4542
4778
  try {
@@ -4622,7 +4858,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4622
4858
  ...(spec.principal ? { principal: spec.principal } : {}),
4623
4859
  ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}),
4624
4860
  };
4625
- if (!(await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).ok)
4861
+ if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed")
4626
4862
  return false;
4627
4863
  publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction);
4628
4864
  try {
@@ -4718,7 +4954,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4718
4954
  }
4719
4955
  };
4720
4956
  const suspendAsk = parkLaneArmed && checkpointStore !== undefined
4721
- ? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated) => {
4957
+ ? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, callSignal) => {
4722
4958
  const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
4723
4959
  if (syncFirstEligible &&
4724
4960
  runtimeCaps?.forceDurableGate !== true &&
@@ -4726,6 +4962,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4726
4962
  !inheritedUnavailableAsks.has(req.toolCallId)) {
4727
4963
  return undefined;
4728
4964
  }
4965
+ const cutSignal = composedCallSignal(callSignal);
4729
4966
  let token;
4730
4967
  let gate;
4731
4968
  let cp;
@@ -4761,13 +4998,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4761
4998
  if (!presented.ok) {
4762
4999
  throw new ParkRefusal(`the stored form of "${req.toolName}"'s arguments could not be presented for re-adjudication (${describeThrown(presented.cause)})`, { cause: presented.cause });
4763
5000
  }
4764
- const reprojected = refuseOutOfContractDecision(await basePolicyForResumeEdit.check({
5001
+ const reprojectedRaced = await raceSettlementAgainstSignal(Promise.resolve(basePolicyForResumeEdit.check({
4765
5002
  toolName: req.toolName,
4766
5003
  args: presented.value,
4767
5004
  toolCallId: req.toolCallId,
4768
5005
  budget: budgetSnapshot,
4769
5006
  ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}),
4770
- }, abortController.signal));
5007
+ }, cutSignal)), cutSignal);
5008
+ if (reprojectedRaced.tag === "aborted")
5009
+ return undefined;
5010
+ if (reprojectedRaced.tag === "threw")
5011
+ throw reprojectedRaced.error;
5012
+ const reprojected = refuseOutOfContractDecision(reprojectedRaced.value);
4771
5013
  const demanded = reprojected.updatedInput !== undefined ? tryCloneArgs(reprojected.updatedInput) : undefined;
4772
5014
  const rewroteTheFiledValue = reprojected.updatedInput !== undefined &&
4773
5015
  !(demanded?.ok === true &&
@@ -4798,7 +5040,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4798
5040
  }
4799
5041
  const { messages } = await session.buildContext();
4800
5042
  const { batchToolCallIds, completedCallIds } = batchContextAt(messages, req.toolCallId);
4801
- if (abortController.signal.aborted) {
5043
+ if (cutSignal.aborted) {
4802
5044
  return undefined;
4803
5045
  }
4804
5046
  if (suspendLoopCapHit(suspendChainBase(), maxSuspends, ` for tool "${req.toolName}" — likely a resume/restart loop.`))
@@ -4806,12 +5048,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4806
5048
  if (remoteEnv !== undefined) {
4807
5049
  if (hasBackgroundShell(remoteEnv))
4808
5050
  await sweepBackgroundShells(remoteEnv, defaultTaskRegistry);
4809
- const snap = await remoteEnv.suspendVM({ abortSignal: abortController.signal });
5051
+ const snap = await remoteEnv.suspendVM({ abortSignal: cutSignal });
4810
5052
  if (!snap.ok) {
4811
5053
  remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1));
4812
5054
  throw new Error(`suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error });
4813
5055
  }
4814
5056
  remoteHandle = { ...remoteEnv.workspaceHandle(), snapshotId: snap.value };
5057
+ if (cutSignal.aborted) {
5058
+ const back = await compensatePausedVM(remoteEnv, snap.value);
5059
+ if (!back.ok) {
5060
+ abortController.abort();
5061
+ void harness.abort();
5062
+ }
5063
+ return undefined;
5064
+ }
4815
5065
  }
4816
5066
  else if (parkOnlyRemoteEnv !== undefined) {
4817
5067
  if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
@@ -4917,16 +5167,31 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4917
5167
  };
4918
5168
  }
4919
5169
  catch (err) {
4920
- deps.onError?.(err, { phase: "config", sessionId });
4921
- return {
4922
- parkFailed: err instanceof ParkRefusal
4923
- ? err.message
4924
- : "the durable approval park could not be prepared (the deployment's error face carries the exception)",
4925
- };
5170
+ const cutWon = cutSignal.aborted;
5171
+ const preselected = cutWon
5172
+ ? undefined
5173
+ : {
5174
+ parkFailed: err instanceof ParkRefusal
5175
+ ? err.message
5176
+ : "the durable approval park could not be prepared (the deployment's error face carries the exception)",
5177
+ };
5178
+ try {
5179
+ deps.onError?.(err, { phase: "config", sessionId });
5180
+ }
5181
+ catch {
5182
+ }
5183
+ return preselected;
4926
5184
  }
4927
- const committed = await commitSuspendSaga(token, cp, remoteEnv, remoteHandle);
4928
- if (!committed.ok) {
4929
- return committed.reason !== undefined ? { parkFailed: committed.reason } : undefined;
5185
+ const committed = await commitSuspendSaga(token, cp, remoteEnv, remoteHandle, cutSignal);
5186
+ if (committed.tag !== "committed") {
5187
+ if (committed.tag === "cut")
5188
+ return undefined;
5189
+ if (committed.tag === "absent") {
5190
+ if (cutSignal.aborted)
5191
+ return undefined;
5192
+ return committed.reason !== undefined ? { parkFailed: committed.reason } : undefined;
5193
+ }
5194
+ return { parkFailed: committed.reason };
4930
5195
  }
4931
5196
  publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, cp.scope, remoteHandle, cp.checkpointId, cp.pendingAction);
4932
5197
  try {