@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.
- package/CHANGELOG.md +65 -0
- package/dist/agents/cross-session-envelope.d.ts +145 -0
- package/dist/agents/cross-session-envelope.js +195 -0
- package/dist/agents/cross-session-judge.d.ts +119 -0
- package/dist/agents/cross-session-judge.js +184 -0
- package/dist/agents/cross-session-ref.d.ts +52 -0
- package/dist/agents/cross-session-ref.js +64 -0
- package/dist/agents/list-agents-tool.d.ts +55 -0
- package/dist/agents/list-agents-tool.js +94 -0
- package/dist/agents/peer-admission.d.ts +17 -1
- package/dist/agents/peer-admission.js +19 -2
- package/dist/agents/peer-directory.d.ts +208 -0
- package/dist/agents/peer-directory.js +272 -0
- package/dist/agents/peer-session-drain.d.ts +159 -0
- package/dist/agents/peer-session-drain.js +245 -0
- package/dist/agents/send-message-tool.d.ts +44 -0
- package/dist/agents/send-message-tool.js +181 -16
- package/dist/agents/subagent-steps.d.ts +11 -0
- package/dist/agents/subagent-steps.js +27 -4
- package/dist/core/auto-mode-arming.d.ts +11 -0
- package/dist/core/auto-mode-arming.js +7 -1
- package/dist/core/auto-mode-prompt.d.ts +5 -0
- package/dist/core/auto-mode-prompt.js +2 -1
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +2 -0
- package/dist/core/checkpoint-store.d.ts +203 -3
- package/dist/core/checkpoint-store.js +60 -19
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +6 -0
- package/dist/core/hooks.d.ts +15 -8
- package/dist/core/hooks.js +6 -3
- package/dist/core/mailbox-store.d.ts +89 -2
- package/dist/core/mailbox-store.js +77 -2
- package/dist/core/permission-rule-consent.d.ts +72 -23
- package/dist/core/permission-rule-consent.js +115 -26
- package/dist/core/permission-rule-model.d.ts +254 -51
- package/dist/core/permission-rule-model.js +316 -55
- package/dist/core/permission-rule-org.js +13 -6
- package/dist/core/remote-env.d.ts +8 -1
- package/dist/core/runner/assemble-result.js +2 -1
- package/dist/core/runner/prepare-task.d.ts +59 -1
- package/dist/core/runner/prepare-task.js +414 -149
- package/dist/core/runner/prepare-workspace-restore.d.ts +6 -1
- package/dist/core/runner/prepare-workspace-restore.js +2 -1
- package/dist/core/runner/runtask.js +16 -5
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +23 -0
- package/dist/core/store-contracts/mailbox-store-contract.js +157 -1
- package/dist/core/task-notification.d.ts +93 -5
- package/dist/core/task-notification.js +31 -4
- package/dist/core/tool-policy.d.ts +11 -0
- package/dist/core/types.d.ts +155 -21
- package/dist/core/untrusted-text.js +17 -1
- package/dist/core/wiring-manifest.d.ts +21 -0
- package/dist/core/wiring-manifest.js +1 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.js +13 -4
- package/dist/stores/cc/mailbox-store.d.ts +1 -1
- package/dist/stores/cc/mailbox-store.js +13 -0
- package/dist/stores/file/adoption/marker.d.ts +1 -1
- package/dist/stores/file/mailbox-store.d.ts +57 -0
- package/dist/stores/file/mailbox-store.js +369 -18
- package/package.json +1 -1
- 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
|
-
|
|
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 (
|
|
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
|
|
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 =
|
|
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
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
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
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
|
4440
|
-
|
|
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 {
|
|
4691
|
+
return { tag: "compensation_failed", reason };
|
|
4456
4692
|
}
|
|
4457
|
-
return {
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
},
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
:
|
|
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 (
|
|
4929
|
-
|
|
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 {
|