@sema-agent/core 5.32.0 → 5.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/dist/agents/cascade.d.ts +49 -1
- package/dist/agents/cascade.js +2 -2
- package/dist/agents/verify.d.ts +70 -4
- package/dist/agents/verify.js +62 -16
- package/dist/core/checkpoint-store.d.ts +95 -0
- package/dist/core/checkpoint-store.js +40 -0
- package/dist/core/hooks.d.ts +14 -6
- package/dist/core/hooks.js +14 -3
- package/dist/core/memory-engine/file-backend.d.ts +172 -22
- package/dist/core/memory-engine/file-backend.js +877 -79
- package/dist/core/memory-engine/memory-backend-contract.js +33 -0
- package/dist/core/runner/assemble-result.d.ts +7 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +72 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +126 -0
- package/dist/core/runner/prepare-config-doors.d.ts +140 -0
- package/dist/core/runner/prepare-config-doors.js +250 -0
- package/dist/core/runner/prepare-safety-scan.d.ts +53 -0
- package/dist/core/runner/prepare-safety-scan.js +80 -0
- package/dist/core/runner/prepare-task.d.ts +27 -81
- package/dist/core/runner/prepare-task.js +83 -586
- package/dist/core/runner/prepare-workspace-restore.d.ts +102 -0
- package/dist/core/runner/prepare-workspace-restore.js +144 -0
- package/dist/core/runner/runtask.js +8 -2
- package/dist/core/tool-policy.d.ts +25 -0
- package/dist/core/types.d.ts +140 -13
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/orchestration/workflow-governance.d.ts +6 -4
- package/dist/tools/fs/bash-readonly-classifier.d.ts +9 -3
- package/dist/tools/fs/bash-readonly-classifier.js +4 -1
- package/dist/tools/fs/fs-bash.d.ts +19 -3
- package/dist/tools/fs/fs-bash.js +26 -1
- package/dist/tools/fs/index.d.ts +20 -4
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/read-deny.d.ts +66 -8
- package/dist/tools/fs/read-deny.js +75 -39
- package/dist/tools/fs/read-face.d.ts +3 -2
- package/dist/tools/fs/search.js +2 -0
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
|
7
7
|
import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
|
|
8
8
|
import { createAutoModeDecider } from "../auto-mode.js";
|
|
9
9
|
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
10
|
-
import {
|
|
10
|
+
import { resolveTaskModel } from "../roles.js";
|
|
11
11
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
12
12
|
import { materializeMcpTools } from "../mcp.js";
|
|
13
13
|
import { materializeA2aTools } from "../a2a.js";
|
|
@@ -15,7 +15,7 @@ import { Type } from "typebox";
|
|
|
15
15
|
import { Value } from "typebox/value";
|
|
16
16
|
import { uuidv7 } from "../../engine/session/uuid.js";
|
|
17
17
|
import { brainToRuntime } from "../runtime.js";
|
|
18
|
-
import {
|
|
18
|
+
import { hasSessionFork } from "../session.js";
|
|
19
19
|
import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents/subagent.js";
|
|
20
20
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
21
21
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
@@ -31,13 +31,12 @@ import { emitTrace } from "../trace.js";
|
|
|
31
31
|
import { createSessionRulePolicy } from "./session-rule-policy.js";
|
|
32
32
|
import { cloneObserverInput, createHookEnvCapabilities, createPreToolUseConstraintPolicy, formatHookFeedback, persistedRuleMandateOf, runToolGate } from "../hooks.js";
|
|
33
33
|
import { orgRuleVerdictFor } from "../permission-rule-org.js";
|
|
34
|
-
import { reconcileInterruptedSession } from "../session-reconcile.js";
|
|
35
34
|
import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
|
|
36
35
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
37
|
-
import {
|
|
36
|
+
import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
|
|
38
37
|
import { defineTool, isDefineToolProduct } from "../tools.js";
|
|
39
38
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
40
|
-
import { protocolOf
|
|
39
|
+
import { protocolOf } from "../protocol-table.js";
|
|
41
40
|
import { pathToUri } from "../lsp-protocol.js";
|
|
42
41
|
import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
|
|
43
42
|
import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
@@ -50,11 +49,13 @@ import { MEMORY_RECALL_DISCIPLINE } from "../memory-engine/engine.js";
|
|
|
50
49
|
import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
|
|
51
50
|
import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
|
|
52
51
|
import { composeMemoryBlock } from "../memory.js";
|
|
53
|
-
import { preflightLockedConfig } from "../locked-config.js";
|
|
54
52
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
|
|
55
|
-
import { assertRetentionCapability } from "../retention.js";
|
|
56
53
|
import { foldAdmissionFreeze } from "../memory-admission.js";
|
|
57
54
|
import { prepareMemory } from "./prepare-memory.js";
|
|
55
|
+
import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
|
|
56
|
+
import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
|
|
57
|
+
import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
|
|
58
|
+
import { prepareWorkspaceRestore, rebaseWorkspacePath, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
|
|
58
59
|
import { defaultPromptProvider, buildEnvironmentContext, buildGitSnapshot, formatLocalDate, isValidTimeZone, PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
|
|
59
60
|
import { assemblePrompt } from "../../prompt-assembly/assemble.js";
|
|
60
61
|
import { auditToolCollisions, getToolContract, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
|
|
@@ -67,18 +68,17 @@ import { capAggregateToolResults } from "../tool-result-budget.js";
|
|
|
67
68
|
import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
|
|
68
69
|
import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
|
|
69
70
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
70
|
-
import { hasDestroy, isIsolated, isRemoteExecutionEnv,
|
|
71
|
-
import { withRetry } from "../with-retry.js";
|
|
71
|
+
import { hasDestroy, isIsolated, isRemoteExecutionEnv, isSuspendable, missingRestoreSurface } from "../remote-env.js";
|
|
72
72
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
73
73
|
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
74
74
|
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
75
75
|
import { createMonitorTool } from "../../tools/monitor.js";
|
|
76
76
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
77
|
-
import { applyCompactionToReadFileState,
|
|
77
|
+
import { applyCompactionToReadFileState, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, isReadDedupStubResult, resolveReadDenyBuiltins, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
78
78
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
79
79
|
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
|
|
80
80
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
81
|
-
import { createPresentPlanTool, createEnterPlanModeTool, PRESENT_PLAN_TOOL_NAME
|
|
81
|
+
import { createPresentPlanTool, createEnterPlanModeTool, PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
82
82
|
import { isSelfOrchestrationActive, selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
83
83
|
import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestration/run-workflow-tool.js";
|
|
84
84
|
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
@@ -87,8 +87,8 @@ import { resolveKey } from "../../tools/fs/safety.js";
|
|
|
87
87
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
88
88
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
89
89
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
90
|
-
import { countElicitOptIns,
|
|
91
|
-
import { GLOBAL_USAGE_KEY,
|
|
90
|
+
import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
91
|
+
import { GLOBAL_USAGE_KEY, usageRetryAfterMs } from "../usage-window-store.js";
|
|
92
92
|
import { deliverEngineNotice } from "../types.js";
|
|
93
93
|
const announcedMaterializeEnv = new Set();
|
|
94
94
|
export function __resetMaterializeEnvAnnouncements() {
|
|
@@ -102,74 +102,7 @@ let readFaceClampConsoleAnnounced = false;
|
|
|
102
102
|
export function __resetReadFaceClampAnnouncement() {
|
|
103
103
|
readFaceClampConsoleAnnounced = false;
|
|
104
104
|
}
|
|
105
|
-
const RECONCILE_MAX_RETRIES = 3;
|
|
106
105
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
107
|
-
const TASK_LIMIT_KEY_DICT = {
|
|
108
|
-
maxTokens: true,
|
|
109
|
-
maxCostUsd: true,
|
|
110
|
-
maxTurns: true,
|
|
111
|
-
maxWalltimeMs: true,
|
|
112
|
-
maxOutputTokens: true,
|
|
113
|
-
approachNotice: true,
|
|
114
|
-
budgetStreamCancel: true,
|
|
115
|
-
degrade: true,
|
|
116
|
-
brainCallGuardrailMs: true,
|
|
117
|
-
};
|
|
118
|
-
const TASK_LIMIT_KEYS = Object.keys(TASK_LIMIT_KEY_DICT);
|
|
119
|
-
const NUMERIC_TASK_LIMIT_KEYS = ["maxTokens", "maxCostUsd", "maxTurns", "maxWalltimeMs", "maxOutputTokens"];
|
|
120
|
-
const RETIRED_TASK_LIMIT_KEYS = {
|
|
121
|
-
timeoutSec: "maxWalltimeMs (milliseconds, not seconds)",
|
|
122
|
-
deadlineNudge: "approachNotice",
|
|
123
|
-
callCapByDeadline: "(retired — no replacement)",
|
|
124
|
-
gracefulFinalize: "(retired — the approach notice is the only end-of-budget prompt)",
|
|
125
|
-
};
|
|
126
|
-
function limitConfigError(code, message) {
|
|
127
|
-
const e = new Error(message);
|
|
128
|
-
e.code = code;
|
|
129
|
-
return e;
|
|
130
|
-
}
|
|
131
|
-
export function resolveTaskLimits(limits) {
|
|
132
|
-
if (limits === undefined)
|
|
133
|
-
return undefined;
|
|
134
|
-
if (typeof limits !== "object" || Array.isArray(limits)) {
|
|
135
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.limits must be an object (got ${Array.isArray(limits) ? "an array" : typeof limits})`);
|
|
136
|
-
}
|
|
137
|
-
const raw = limits;
|
|
138
|
-
const legal = TASK_LIMIT_KEYS;
|
|
139
|
-
for (const key of Object.keys(raw)) {
|
|
140
|
-
if (legal.includes(key))
|
|
141
|
-
continue;
|
|
142
|
-
const replacement = RETIRED_TASK_LIMIT_KEYS[key];
|
|
143
|
-
throw limitConfigError("config.limit_unknown_key", `TaskSpec.limits.${key} is not a limit this engine reads` +
|
|
144
|
-
(replacement !== undefined ? ` — it was replaced by \`${replacement}\`` : "") +
|
|
145
|
-
`. Legal keys: ${TASK_LIMIT_KEYS.join(", ")}. Refused rather than ignored: a limit that is silently dropped reads to the caller as an armed ceiling.`);
|
|
146
|
-
}
|
|
147
|
-
for (const key of NUMERIC_TASK_LIMIT_KEYS) {
|
|
148
|
-
const value = raw[key];
|
|
149
|
-
if (value === undefined)
|
|
150
|
-
continue;
|
|
151
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
152
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable ceiling is not a ceiling, and folding it to a default would run the task under a limit nobody chose.`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
const notice = raw.approachNotice;
|
|
156
|
-
if (notice !== undefined && notice !== false) {
|
|
157
|
-
if (typeof notice !== "object" || notice === null || Array.isArray(notice)) {
|
|
158
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.approachNotice must be \`false\` or { at?: [first, second] } (got ${String(notice)})`);
|
|
159
|
-
}
|
|
160
|
-
const at = notice.at;
|
|
161
|
-
if (at !== undefined) {
|
|
162
|
-
const ok = Array.isArray(at) &&
|
|
163
|
-
at.length === 2 &&
|
|
164
|
-
at.every((n) => typeof n === "number" && Number.isFinite(n) && n > 0 && n <= 1) &&
|
|
165
|
-
at[0] <= at[1];
|
|
166
|
-
if (!ok) {
|
|
167
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.approachNotice.at must be two fractions in (0, 1] with first <= second (got ${JSON.stringify(at)})`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return limits;
|
|
172
|
-
}
|
|
173
106
|
const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
|
|
174
107
|
const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
175
108
|
export const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60_000;
|
|
@@ -202,7 +135,6 @@ function sanitizedTtlMs(ttlMs) {
|
|
|
202
135
|
const ungatedWarnedShapes = new WeakMap();
|
|
203
136
|
const advisedPolicyNames = new WeakMap();
|
|
204
137
|
const ADVISED_KEYS_CAP = 64;
|
|
205
|
-
const NAMESPACED_NAME_SHAPES = PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(", ");
|
|
206
138
|
export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
207
139
|
export function checkpointScopeOf(spec) {
|
|
208
140
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
@@ -214,16 +146,8 @@ class ParkRefusal extends Error {
|
|
|
214
146
|
}
|
|
215
147
|
}
|
|
216
148
|
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
217
|
-
export
|
|
218
|
-
|
|
219
|
-
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
|
|
220
|
-
}
|
|
221
|
-
export function resolveModelPromptTraits(model, spec, internals) {
|
|
222
|
-
return {
|
|
223
|
-
promptProfile: spec.promptProfile ?? internals?.promptProfile ?? "simple",
|
|
224
|
-
fableMitigations: isFableFamilyModelId(model.id),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
149
|
+
export { isFableFamilyModelId, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
|
|
150
|
+
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./prepare-workspace-restore.js";
|
|
227
151
|
function hasConversationContent(branch) {
|
|
228
152
|
return branch.some((e) => e.type === "message" || e.type === "custom_message" || e.type === "compaction");
|
|
229
153
|
}
|
|
@@ -243,31 +167,6 @@ export function batchContextAt(messages, currentId) {
|
|
|
243
167
|
const completedCallIds = batch.filter((id) => id !== currentId && resolved.has(id));
|
|
244
168
|
return { batchToolCallIds: batch, completedCallIds };
|
|
245
169
|
}
|
|
246
|
-
function detectConflicts(storage, ref) {
|
|
247
|
-
const guard = (fn) => async (...args) => {
|
|
248
|
-
try {
|
|
249
|
-
return await fn(...args);
|
|
250
|
-
}
|
|
251
|
-
catch (err) {
|
|
252
|
-
if (isSessionConflict(err)) {
|
|
253
|
-
ref.hit = true;
|
|
254
|
-
}
|
|
255
|
-
throw err;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
return new Proxy(storage, {
|
|
259
|
-
get(target, prop, receiver) {
|
|
260
|
-
const value = Reflect.get(target, prop, receiver);
|
|
261
|
-
if (typeof value !== "function") {
|
|
262
|
-
return value;
|
|
263
|
-
}
|
|
264
|
-
const bound = value.bind(target);
|
|
265
|
-
return prop === "appendEntry" || prop === "setLeafId"
|
|
266
|
-
? guard(bound)
|
|
267
|
-
: bound;
|
|
268
|
-
},
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
170
|
function raceAbort(p, signal, onAbort) {
|
|
272
171
|
if (signal.aborted)
|
|
273
172
|
return Promise.resolve(onAbort());
|
|
@@ -293,353 +192,19 @@ async function forgetQuietly(sessions, sessionId) {
|
|
|
293
192
|
catch {
|
|
294
193
|
}
|
|
295
194
|
}
|
|
296
|
-
export function rebaseWorkspacePath(p, fromRaw, toRaw) {
|
|
297
|
-
if (p.includes("\\") || fromRaw.includes("\\") || toRaw.includes("\\"))
|
|
298
|
-
return p;
|
|
299
|
-
const stripTrail = (s) => (s.length > 1 && s.endsWith("/") ? stripTrail(s.slice(0, -1)) : s);
|
|
300
|
-
const from = stripTrail(fromRaw);
|
|
301
|
-
const to = stripTrail(toRaw);
|
|
302
|
-
if (p === from || stripTrail(p) === from)
|
|
303
|
-
return to;
|
|
304
|
-
const fromPrefix = from === "/" ? from : `${from}/`;
|
|
305
|
-
if (!p.startsWith(fromPrefix))
|
|
306
|
-
return p;
|
|
307
|
-
const suffix = p.slice(fromPrefix.length).replace(/^\/+/, "");
|
|
308
|
-
return to === "/" ? `${to}${suffix}` : `${to}/${suffix}`;
|
|
309
|
-
}
|
|
310
|
-
function remoteEnvFailureNote(op, error, attempts) {
|
|
311
|
-
return { op, code: error.code, retryable: isRetryableRemoteErrorCode(error.code), attempts, message: error.message };
|
|
312
|
-
}
|
|
313
|
-
const REMOTE_RESTORE_MAX_ATTEMPTS = 2;
|
|
314
|
-
const REMOTE_RESTORE_BACKOFF_MS = 200;
|
|
315
|
-
async function restoreWorkspaceWithRetry(env, snapshotId, options) {
|
|
316
|
-
let attempts = 0;
|
|
317
|
-
const outcome = await withRetry(async (attempt) => {
|
|
318
|
-
attempts = attempt;
|
|
319
|
-
return env.resumeVM(snapshotId, options);
|
|
320
|
-
}, { retryableCodes: RETRYABLE_REMOTE_ERROR_CODES, maxAttempts: REMOTE_RESTORE_MAX_ATTEMPTS, backoffMs: () => REMOTE_RESTORE_BACKOFF_MS }, { ...(options.abortSignal !== undefined ? { signal: options.abortSignal } : {}) });
|
|
321
|
-
return { outcome, attempts };
|
|
322
|
-
}
|
|
323
|
-
export function rebaseWorkspacePathAcross(p, froms, to) {
|
|
324
|
-
for (const from of froms) {
|
|
325
|
-
const out = rebaseWorkspacePath(p, from, to);
|
|
326
|
-
if (out !== p)
|
|
327
|
-
return out;
|
|
328
|
-
}
|
|
329
|
-
return p;
|
|
330
|
-
}
|
|
331
195
|
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf) {
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
};
|
|
337
|
-
const promptProfile = resolveModelPromptTraits({ id: "" }, spec, internals).promptProfile;
|
|
338
|
-
if (spec.resumeAt !== undefined) {
|
|
339
|
-
if (resume) {
|
|
340
|
-
const e = new Error("resumeAt cannot be combined with a durable resume");
|
|
341
|
-
e.code = "resume_at.conflicts_resume";
|
|
342
|
-
throw e;
|
|
343
|
-
}
|
|
344
|
-
if (!spec.sessionId) {
|
|
345
|
-
const e = new Error("resumeAt requires a sessionId (the session to branch)");
|
|
346
|
-
e.code = "resume_at.no_session";
|
|
347
|
-
throw e;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
assertReadFaceValue(spec.readFace, "TaskSpec.readFace");
|
|
351
|
-
assertReadFaceValue(deps.readFace, "readFace (deployment seat)");
|
|
352
|
-
if (spec.resumeAtMode !== undefined) {
|
|
353
|
-
if (spec.resumeAt === undefined) {
|
|
354
|
-
const e = new Error(`resumeAtMode "${spec.resumeAtMode}" requires resumeAt (there is no branch target to position against)`);
|
|
355
|
-
e.code = "resume_at.mode_without_target";
|
|
356
|
-
throw e;
|
|
357
|
-
}
|
|
358
|
-
if (spec.resumeAtMode !== "at" && spec.resumeAtMode !== "before") {
|
|
359
|
-
const e = new Error(`resumeAtMode "${String(spec.resumeAtMode)}" is invalid — expected "at" (inclusive branch, the default) or "before" (exclusive branch)`);
|
|
360
|
-
e.code = "resume_at.invalid_mode";
|
|
361
|
-
throw e;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (spec.toolMaterializeStrategy !== undefined && spec.toolMaterializeStrategy !== "swap" && spec.toolMaterializeStrategy !== "static") {
|
|
365
|
-
const e = new Error(`toolMaterializeStrategy must be "swap" or "static" (got ${JSON.stringify(spec.toolMaterializeStrategy)}).`);
|
|
366
|
-
e.code = "config.tool_materialize_invalid";
|
|
367
|
-
throw e;
|
|
368
|
-
}
|
|
369
|
-
if (spec.memoryPersistenceCapable !== undefined && typeof spec.memoryPersistenceCapable !== "boolean") {
|
|
370
|
-
const e = new Error(`memoryPersistenceCapable must be a boolean when present (got ${JSON.stringify(spec.memoryPersistenceCapable)}) — a non-boolean would silently read as capable.`);
|
|
371
|
-
e.code = "config.memory_persistence_invalid";
|
|
372
|
-
throw e;
|
|
373
|
-
}
|
|
374
|
-
if (spec.toolMaterializeStrategy === "static" && spec.deferSelfResolve === false) {
|
|
375
|
-
const e = new Error(`toolMaterializeStrategy "static" cannot be combined with deferSelfResolve: false — with the direct-call ` +
|
|
376
|
-
`lane disabled a placeholder is never swapped and never self-resolves, so no deferred tool could ever be ` +
|
|
377
|
-
`called. Use "swap", or leave deferSelfResolve on.`);
|
|
378
|
-
e.code = "config.tool_materialize_unreachable";
|
|
379
|
-
throw e;
|
|
380
|
-
}
|
|
381
|
-
if (resume === undefined && spec.objective.trim().length === 0) {
|
|
382
|
-
const e = new Error("TaskSpec.objective is empty — a task needs an instruction (an empty user message is rejected by strict model endpoints and would fail every later request of the session).");
|
|
383
|
-
e.code = "config.empty_objective";
|
|
384
|
-
throw e;
|
|
385
|
-
}
|
|
386
|
-
const lockedPreflight = preflightLockedConfig(spec, deps);
|
|
387
|
-
assertRetentionCapability({
|
|
388
|
-
policy: deps.retentionPolicy,
|
|
389
|
-
locked: lockedPreflight.lockedKeys.has("retentionPolicy"),
|
|
390
|
-
stores: [
|
|
391
|
-
{ name: "sessionStore", store: sessions },
|
|
392
|
-
{ name: "checkpointStore", store: resolveCheckpointStore(spec, deps) },
|
|
393
|
-
{ name: "toolResultStore", store: deps.toolResultStore },
|
|
394
|
-
],
|
|
395
|
-
});
|
|
396
|
-
const resolvedInteractionPosture = spec.interactionPosture ?? internals?.parentInteractionPosture ?? deps.interactionPosture;
|
|
397
|
-
{
|
|
398
|
-
const interactionPosture = resolvedInteractionPosture;
|
|
399
|
-
const discloseInteractionPostureRefusal = (err) => {
|
|
400
|
-
try {
|
|
401
|
-
deps.onError?.(err, { phase: "config", sessionId: spec.sessionId ?? "(pre-session)", classification: "interaction-posture-refused" });
|
|
402
|
-
}
|
|
403
|
-
catch {
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
if (interactionPosture !== undefined && interactionPosture !== "interactive" && interactionPosture !== "headless") {
|
|
407
|
-
const e = new Error(`interactionPosture ${JSON.stringify(interactionPosture)} is not a recognized posture ("interactive" | "headless") — ` +
|
|
408
|
-
`an unevaluable declaration is refused loudly, never folded to either posture.`);
|
|
409
|
-
e.code = "config.interaction_posture";
|
|
410
|
-
discloseInteractionPostureRefusal(e);
|
|
411
|
-
throw e;
|
|
412
|
-
}
|
|
413
|
-
if (interactionPosture === "interactive") {
|
|
414
|
-
const askDoor = resolveAskSeamForm(spec, deps);
|
|
415
|
-
const humanReachable = deriveAskEffective(askDoor.form, "unresolved") === "human_reachable";
|
|
416
|
-
const questionDoor = resolveQuestionSeam(spec, deps);
|
|
417
|
-
const strippedByEngine = internals?.questionFaceStripped === true;
|
|
418
|
-
if (spec.interactiveTools === false) {
|
|
419
|
-
const e = new Error(`interaction posture "interactive" declared together with interactiveTools: false — the hard-headless ` +
|
|
420
|
-
`clamp removes the AskUserQuestion mount, so no content question can ever reach the human this posture ` +
|
|
421
|
-
`promises. Drop one of the two declarations.`);
|
|
422
|
-
e.code = "config.interaction_posture";
|
|
423
|
-
discloseInteractionPostureRefusal(e);
|
|
424
|
-
throw e;
|
|
425
|
-
}
|
|
426
|
-
if (!humanReachable || !(questionDoor.wired || strippedByEngine)) {
|
|
427
|
-
const missing = [];
|
|
428
|
-
if (!humanReachable) {
|
|
429
|
-
missing.push(askDoor.form === "absent"
|
|
430
|
-
? "no onAsk approver is wired (spec.onAsk ?? deps.onAsk is absent — asks would auto-deny or park)"
|
|
431
|
-
: `the resolved onAsk seat is the blanket policy "${askDoor.form}" (${askDoor.provenance ?? "?"}) — a policy setting is not a reachable human`);
|
|
432
|
-
}
|
|
433
|
-
if (!questionDoor.wired && !strippedByEngine) {
|
|
434
|
-
missing.push("no content-question channel (spec.onQuestion ?? deps.onQuestion is absent, and this leg is not an engine-stripped background lane)");
|
|
435
|
-
}
|
|
436
|
-
const e = new Error(`interaction posture "interactive" declared, but this assembly cannot reach a human: ${missing.join("; ")}. ` +
|
|
437
|
-
`Wire the missing seam(s), or drop the posture declaration (absent = no check).`);
|
|
438
|
-
e.code = "config.interaction_posture";
|
|
439
|
-
discloseInteractionPostureRefusal(e);
|
|
440
|
-
throw e;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
resolveTaskLimits(spec.limits);
|
|
445
|
-
if (spec.resourceSuspend !== undefined) {
|
|
446
|
-
const rsus = spec.resourceSuspend;
|
|
447
|
-
if (typeof rsus.scope !== "string" || rsus.scope === "") {
|
|
448
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.scope must be a non-empty string (got ${String(rsus.scope)}) — it is the multi-tenant isolation key every resource checkpoint is filed under.`);
|
|
449
|
-
}
|
|
450
|
-
for (const key of ["totalBudgetUsd", "totalTokens", "maxSlices", "ttlMs"]) {
|
|
451
|
-
const value = rsus[key];
|
|
452
|
-
if (value === undefined)
|
|
453
|
-
continue;
|
|
454
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
455
|
-
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable allocation is not an allocation, and a NaN here blinds even the validated per-slice window.`);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
const usageWindows = resolveUsageWindows(deps.usageWindows);
|
|
460
|
-
const brainCallGuardrailRef = {};
|
|
461
|
-
const brainCallGuardrailMs = resolveBrainCallGuardrailMs(spec.limits?.brainCallGuardrailMs ?? deps.brainCallGuardrailMs);
|
|
462
|
-
if (spec.agents !== undefined && spec.agents.length > 0) {
|
|
463
|
-
const pool = spec.tools ?? [];
|
|
464
|
-
if (!pool.some((t) => typeof t.withAgents === "function")) {
|
|
465
|
-
const e = new Error(`TaskSpec.agents was provided but no delegation tool (createSubagentTool) is mounted in spec.tools — the per-task agents could never be offered. Mount the delegation tool, or drop spec.agents.`);
|
|
466
|
-
e.code = "config.agents.no_delegation_tool";
|
|
467
|
-
throw e;
|
|
468
|
-
}
|
|
469
|
-
const perTask = spec.agents;
|
|
470
|
-
spec = { ...spec, tools: pool.map((t) => (typeof t.withAgents === "function" ? t.withAgents(perTask) : t)) };
|
|
471
|
-
}
|
|
472
|
-
const resolvedRole = resolveTaskModel(spec, deps);
|
|
473
|
-
const model = resolvedRole.model;
|
|
474
|
-
const fableMitigations = resolveModelPromptTraits(model, spec, internals).fableMitigations;
|
|
475
|
-
const thinking = spec.thinking ?? resolvedRole.thinking ?? model.defaultThinking;
|
|
476
|
-
const compModel = spec.compactionModel
|
|
477
|
-
? resolveModel(spec.compactionModel, deps.models)
|
|
478
|
-
: roleModelIfSet("summarize", spec, deps);
|
|
479
|
-
const toolEffects = new Map();
|
|
480
|
-
const ownToolNames = new Set((spec.tools ?? []).map((t) => t.name));
|
|
481
|
-
const claimedAliases = new Set();
|
|
482
|
-
const egressTools = new Set();
|
|
483
|
-
const irreversibleTools = new Set();
|
|
484
|
-
const irreversibilityTier = new Map();
|
|
485
|
-
const axisExplicitNegatives = new Map();
|
|
486
|
-
const reversibilityProbes = new Map();
|
|
196
|
+
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
197
|
+
spec = doors.spec;
|
|
198
|
+
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, resolvedRole, model, thinking, compModel, fableMitigations, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
199
|
+
const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes, ownToolNames } = prepareSafetyScan({ spec, deps });
|
|
487
200
|
let shellGatedBash = false;
|
|
488
201
|
let shellGatedMonitor = false;
|
|
489
|
-
for (const t of spec.tools ?? []) {
|
|
490
|
-
if (t.name.includes("__")) {
|
|
491
|
-
const e = new Error(`Tool name "${t.name}" is invalid: "__" is reserved for the engine's protocol tool namespaces (${NAMESPACED_NAME_SHAPES}) and must not appear in a caller tool name.`);
|
|
492
|
-
e.code = "config.tool_name_invalid";
|
|
493
|
-
throw e;
|
|
494
|
-
}
|
|
495
|
-
if (t.effect) {
|
|
496
|
-
toolEffects.set(t.name, t.effect);
|
|
497
|
-
}
|
|
498
|
-
const tier = t.irreversibility ?? (t.reversibilityProbe ? "maybe" : undefined);
|
|
499
|
-
if (tier !== undefined) {
|
|
500
|
-
irreversibilityTier.set(t.name, tier);
|
|
501
|
-
if (tier === "always" || tier === "maybe")
|
|
502
|
-
irreversibleTools.add(t.name);
|
|
503
|
-
}
|
|
504
|
-
if (t.reversibilityProbe)
|
|
505
|
-
reversibilityProbes.set(t.name, t.reversibilityProbe);
|
|
506
|
-
if (t.egress) {
|
|
507
|
-
if (t.effect !== undefined && t.effect !== "write") {
|
|
508
|
-
const e = new Error(`Tool "${t.name}" declares egress:true with effect:"${t.effect}" — an egress tool (external write) must have effect:"write" (or omit effect; write is the default).`);
|
|
509
|
-
e.code = "config.egress_requires_write_effect";
|
|
510
|
-
throw e;
|
|
511
|
-
}
|
|
512
|
-
egressTools.add(t.name);
|
|
513
|
-
}
|
|
514
|
-
for (const alias of t.aliases ?? []) {
|
|
515
|
-
if (alias === t.name || ownToolNames.has(alias))
|
|
516
|
-
continue;
|
|
517
|
-
if (claimedAliases.has(alias))
|
|
518
|
-
continue;
|
|
519
|
-
claimedAliases.add(alias);
|
|
520
|
-
if (t.effect)
|
|
521
|
-
toolEffects.set(alias, t.effect);
|
|
522
|
-
if (tier !== undefined) {
|
|
523
|
-
irreversibilityTier.set(alias, tier);
|
|
524
|
-
if (tier === "always" || tier === "maybe")
|
|
525
|
-
irreversibleTools.add(alias);
|
|
526
|
-
}
|
|
527
|
-
if (t.reversibilityProbe)
|
|
528
|
-
reversibilityProbes.set(alias, t.reversibilityProbe);
|
|
529
|
-
if (t.egress)
|
|
530
|
-
egressTools.add(alias);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
toolEffects.set(OFFLOAD_TOOL_NAME, "read");
|
|
534
|
-
if (deps.executionEnvFactory || deps.executionEnv) {
|
|
535
|
-
for (const [name, effect] of Object.entries(HAND_TOOL_EFFECTS))
|
|
536
|
-
toolEffects.set(name, effect);
|
|
537
|
-
if (spec.handsReadOnly === true)
|
|
538
|
-
toolEffects.set("Bash", "read");
|
|
539
|
-
}
|
|
540
|
-
if (spec.enablePlanMode === true) {
|
|
541
|
-
toolEffects.set(PRESENT_PLAN_TOOL_NAME, "read");
|
|
542
|
-
toolEffects.set(ENTER_PLAN_MODE_TOOL_NAME, "read");
|
|
543
|
-
}
|
|
544
202
|
if (spec.requireExistingSession && !spec.sessionId) {
|
|
545
203
|
const e = new Error(`requireExistingSession requires a sessionId — cannot require an existing session without one (design/114 Phase3)`);
|
|
546
204
|
e.code = "resume.session_not_found";
|
|
547
205
|
throw e;
|
|
548
206
|
}
|
|
549
|
-
|
|
550
|
-
let session;
|
|
551
|
-
let conflictRef;
|
|
552
|
-
let wakeRecovered = [];
|
|
553
|
-
let resumeAtBeforeParentId = null;
|
|
554
|
-
for (let attempt = 0;; attempt++) {
|
|
555
|
-
try {
|
|
556
|
-
acquired = await sessions.acquire(spec.sessionId, spec.requireExistingSession ? { requireExisting: true } : undefined);
|
|
557
|
-
}
|
|
558
|
-
catch (err) {
|
|
559
|
-
if (spec.requireExistingSession && err?.code === "not_found") {
|
|
560
|
-
const e = new Error(`requireExistingSession: session "${spec.sessionId}" does not exist — refusing a silent fresh run (design/114 Phase3)`);
|
|
561
|
-
e.code = "resume.session_not_found";
|
|
562
|
-
throw e;
|
|
563
|
-
}
|
|
564
|
-
throw err;
|
|
565
|
-
}
|
|
566
|
-
conflictRef = { hit: false };
|
|
567
|
-
session = new StoredSession(detectConflicts(acquired.session.getStorage(), conflictRef));
|
|
568
|
-
if (!spec.sessionId) {
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
try {
|
|
572
|
-
if (resume) {
|
|
573
|
-
await session.getStorage().setLeafId(resume.leafId);
|
|
574
|
-
}
|
|
575
|
-
else if (spec.resumeAt !== undefined) {
|
|
576
|
-
const entry = await session.getEntry(spec.resumeAt);
|
|
577
|
-
if (!entry) {
|
|
578
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" not found in session "${spec.sessionId}"`);
|
|
579
|
-
e.code = "resume_at.not_found";
|
|
580
|
-
throw e;
|
|
581
|
-
}
|
|
582
|
-
if (entry.type !== "message" && entry.type !== "custom_message") {
|
|
583
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" is a "${entry.type}" entry; resume-at requires a message boundary`);
|
|
584
|
-
e.code = "resume_at.not_a_message";
|
|
585
|
-
throw e;
|
|
586
|
-
}
|
|
587
|
-
if (entry.type === "message") {
|
|
588
|
-
const msg = entry.message;
|
|
589
|
-
if (msg?.role === "toolResult") {
|
|
590
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" is a tool-result (mid-turn); resume-at requires a settled turn boundary (a user message or a finished assistant turn)`);
|
|
591
|
-
e.code = "resume_at.not_a_message";
|
|
592
|
-
throw e;
|
|
593
|
-
}
|
|
594
|
-
if (msg?.role === "assistant" && Array.isArray(msg.content) && msg.content.some((p) => p?.type === "toolCall")) {
|
|
595
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" ends a turn mid-tool-call; resume-at requires a settled turn boundary`);
|
|
596
|
-
e.code = "resume_at.not_a_message";
|
|
597
|
-
throw e;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
if (spec.resumeAtMode === "before") {
|
|
601
|
-
const role = entry.type === "message" ? entry.message.role : undefined;
|
|
602
|
-
if (role !== "user") {
|
|
603
|
-
const kind = entry.type === "message" ? `role "${String(role)}" message` : `"${entry.type}" entry`;
|
|
604
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" is a ${kind}; resumeAtMode "before" supports only USER-message targets (the exclusive-rewind shape: the removed message is a user message)`);
|
|
605
|
-
e.code = "resume_at.before_target_not_user";
|
|
606
|
-
throw e;
|
|
607
|
-
}
|
|
608
|
-
let ancestorId = entry.parentId;
|
|
609
|
-
let sawConversationAbove = false;
|
|
610
|
-
while (ancestorId !== null) {
|
|
611
|
-
const ancestor = await session.getEntry(ancestorId);
|
|
612
|
-
if (!ancestor)
|
|
613
|
-
break;
|
|
614
|
-
if (ancestor.type === "message" || ancestor.type === "custom_message") {
|
|
615
|
-
sawConversationAbove = true;
|
|
616
|
-
break;
|
|
617
|
-
}
|
|
618
|
-
ancestorId = ancestor.parentId;
|
|
619
|
-
}
|
|
620
|
-
if (!sawConversationAbove) {
|
|
621
|
-
const e = new Error(`resumeAt entry "${spec.resumeAt}" is the session's first message; resumeAtMode "before" cannot rewind past the session root — start a NEW session instead`);
|
|
622
|
-
e.code = "resume_at.before_root_unsupported";
|
|
623
|
-
throw e;
|
|
624
|
-
}
|
|
625
|
-
resumeAtBeforeParentId = entry.parentId;
|
|
626
|
-
}
|
|
627
|
-
await session.getStorage().setLeafId(spec.resumeAtMode === "before" ? entry.parentId : spec.resumeAt);
|
|
628
|
-
}
|
|
629
|
-
wakeRecovered = (await reconcileInterruptedSession(session, toolEffects, resume?.suspendedBatch)).recovered;
|
|
630
|
-
break;
|
|
631
|
-
}
|
|
632
|
-
catch (err) {
|
|
633
|
-
if (isSessionConflict(err) && attempt < RECONCILE_MAX_RETRIES) {
|
|
634
|
-
if (sessions.forget) {
|
|
635
|
-
await Promise.resolve(sessions.forget(spec.sessionId)).catch(() => undefined);
|
|
636
|
-
}
|
|
637
|
-
continue;
|
|
638
|
-
}
|
|
639
|
-
await Promise.resolve(sessions.forget ? sessions.forget(acquired.sessionId) : undefined).catch(() => undefined);
|
|
640
|
-
throw err;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
207
|
+
const { acquired, session, conflictRef, wakeRecovered, resumeAtBeforeParentId } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects });
|
|
643
208
|
const sessionId = acquired.sessionId;
|
|
644
209
|
const hostTaskId = spec.taskId ?? sessionId;
|
|
645
210
|
if (compModel !== undefined) {
|
|
@@ -781,7 +346,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
781
346
|
}
|
|
782
347
|
}
|
|
783
348
|
const handsEnabled = ownedEnv !== undefined || deps.executionEnv !== undefined;
|
|
784
|
-
|
|
349
|
+
const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
|
|
785
350
|
const abortController = new AbortController();
|
|
786
351
|
if (spec.signal?.aborted)
|
|
787
352
|
abortController.abort();
|
|
@@ -829,108 +394,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
829
394
|
}
|
|
830
395
|
}
|
|
831
396
|
}
|
|
832
|
-
|
|
833
|
-
const rebaseRestoredPath = (p) => restoredRootRebase === undefined ? p : rebaseWorkspacePathAcross(p, restoredRootRebase.from, restoredRootRebase.to);
|
|
834
|
-
if (resume?.workspaceHandle !== undefined) {
|
|
835
|
-
const failResume = (message, cause, note) => {
|
|
836
|
-
const e = new Error(message, cause ? { cause } : undefined);
|
|
837
|
-
e.code = "resume.env_failed";
|
|
838
|
-
if (note !== undefined)
|
|
839
|
-
e.remoteEnvFailure = note;
|
|
840
|
-
throw e;
|
|
841
|
-
};
|
|
842
|
-
const handle = resume.workspaceHandle;
|
|
843
|
-
if (ownedEnv === undefined || !isRemoteExecutionEnv(ownedEnv)) {
|
|
844
|
-
failResume("resume needs a RemoteExecutionEnv from executionEnvFactory to restore the workspace snapshot");
|
|
845
|
-
}
|
|
846
|
-
else if (handle.snapshotId === undefined) {
|
|
847
|
-
if (handle.restoreMode !== "park_only" && ownedEnv.capabilities.suspendable) {
|
|
848
|
-
failResume("checkpoint workspaceHandle has no snapshotId and is not a park_only handle, but the resumed env is suspendable — refusing to resume on a possibly-unrestored workspace (corrupt checkpoint?)");
|
|
849
|
-
}
|
|
850
|
-
if (handle.mountPath && handle.mountPath !== taskRootPath) {
|
|
851
|
-
taskRootPath = handle.mountPath;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
const restoreSignal = spec.signal
|
|
856
|
-
? AbortSignal.any([abortController.signal, spec.signal])
|
|
857
|
-
: abortController.signal;
|
|
858
|
-
const missingHere = missingRestoreSurface(ownedEnv);
|
|
859
|
-
if (missingHere.length > 0) {
|
|
860
|
-
failResume(`the resumed execution env cannot restore a workspace snapshot: its adapter does not implement ${missingHere.join(" or ")}. The checkpoint holds snapshot "${handle.snapshotId}" — wire an adapter that implements the full RemoteExecutionEnv restore surface and re-resume.`);
|
|
861
|
-
}
|
|
862
|
-
const { outcome: restored, attempts: restoreAttempts } = await restoreWorkspaceWithRetry(ownedEnv, handle.snapshotId, {
|
|
863
|
-
abortSignal: restoreSignal,
|
|
864
|
-
priorHandle: handle,
|
|
865
|
-
});
|
|
866
|
-
if (!restored.ok) {
|
|
867
|
-
failResume(`resumeVM failed after ${restoreAttempts} attempt(s) (${restored.error.code}): ${restored.error.message}`, restored.error, remoteEnvFailureNote("resumeVM", restored.error, restoreAttempts));
|
|
868
|
-
}
|
|
869
|
-
else {
|
|
870
|
-
const restoredEnv = ownedEnv;
|
|
871
|
-
const canonicalInEnv = async (p) => {
|
|
872
|
-
try {
|
|
873
|
-
const r = await restoredEnv.canonicalPath(p, restoreSignal);
|
|
874
|
-
return r.ok ? r.value : undefined;
|
|
875
|
-
}
|
|
876
|
-
catch {
|
|
877
|
-
return undefined;
|
|
878
|
-
}
|
|
879
|
-
};
|
|
880
|
-
let checkpointedCanonical;
|
|
881
|
-
let sameRootUnderAlias = false;
|
|
882
|
-
if (restored.value.mountPath !== handle.mountPath) {
|
|
883
|
-
checkpointedCanonical = await canonicalInEnv(handle.mountPath);
|
|
884
|
-
const restoredCanonical = await canonicalInEnv(restored.value.mountPath);
|
|
885
|
-
sameRootUnderAlias =
|
|
886
|
-
checkpointedCanonical !== undefined && restoredCanonical !== undefined && checkpointedCanonical === restoredCanonical;
|
|
887
|
-
}
|
|
888
|
-
if (restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) {
|
|
889
|
-
if (resume.executesApprovedAction === true) {
|
|
890
|
-
failResume(`resumeVM workspace-root divergence with a pending approved action: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the approved args are bound to the checkpointed root; refusing to execute them against a moved workspace (adapter should honor priorHandle)`);
|
|
891
|
-
}
|
|
892
|
-
try {
|
|
893
|
-
deps.onError?.(new Error(`resumeVM workspace-root divergence: checkpointed mountPath "${handle.mountPath}" but the restored handle reports "${restored.value.mountPath}" — the resumed task follows the restored root`), { phase: "config", sessionId });
|
|
894
|
-
}
|
|
895
|
-
catch {
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
if (restored.value.mountPath && restored.value.mountPath !== taskRootPath) {
|
|
899
|
-
taskRootPath = restored.value.mountPath;
|
|
900
|
-
}
|
|
901
|
-
if (restored.value.mountPath && restored.value.mountPath !== handle.mountPath && !sameRootUnderAlias) {
|
|
902
|
-
const from = checkpointedCanonical !== undefined && checkpointedCanonical !== handle.mountPath
|
|
903
|
-
? [handle.mountPath, checkpointedCanonical]
|
|
904
|
-
: [handle.mountPath];
|
|
905
|
-
restoredRootRebase = { from, to: restored.value.mountPath };
|
|
906
|
-
try {
|
|
907
|
-
deps.onError?.(new Error(from.length > 1
|
|
908
|
-
? `resumeVM workspace-root rebase accepts both spellings of the checkpointed root (${from.map((f) => `"${f}"`).join(" and ")}) when migrating persisted paths to "${restored.value.mountPath}"`
|
|
909
|
-
: `resumeVM workspace-root rebase is spelling-exact: it matches the checkpointed root "${handle.mountPath}" only, so persisted paths recorded under an equivalent alias of it are NOT migrated to "${restored.value.mountPath}" and stay as written`), { phase: "config", sessionId });
|
|
910
|
-
}
|
|
911
|
-
catch {
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
const init = await ownedEnv.postResumeInit();
|
|
916
|
-
if (!init.ok) {
|
|
917
|
-
failResume(`postResumeInit failed (${init.error.code}): ${init.error.message}`, init.error, remoteEnvFailureNote("postResumeInit", init.error, 1));
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
if (internals?.onWorkspaceResolved !== undefined) {
|
|
922
|
-
try {
|
|
923
|
-
const workspaceEnv = ownedEnv ?? deps.executionEnv;
|
|
924
|
-
internals.onWorkspaceResolved({
|
|
925
|
-
cwd: taskRootPath,
|
|
926
|
-
isolated: internals.isolation === "worktree",
|
|
927
|
-
remote: workspaceEnv !== undefined &&
|
|
928
|
-
(isRemoteExecutionEnv(workspaceEnv) || workspaceEnv.hostLocalPaths === false),
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
catch {
|
|
932
|
-
}
|
|
933
|
-
}
|
|
397
|
+
const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
|
|
934
398
|
const rewindNotes = [];
|
|
935
399
|
const rewindCaptureRequested = spec.rewindFiles === true;
|
|
936
400
|
if (spec.resumeAt !== undefined && spec.rewindFilesTo !== undefined) {
|
|
@@ -987,7 +451,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
987
451
|
}
|
|
988
452
|
rewindTarget = anchor;
|
|
989
453
|
}
|
|
990
|
-
const restoreRoot =
|
|
454
|
+
const restoreRoot = taskRootFinal;
|
|
991
455
|
const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal;
|
|
992
456
|
const restored = await deps.fileSnapshotStore.restore(sessionId, rewindTarget, executionEnv, restoreRoot, restoreSignal);
|
|
993
457
|
if (!restored.ok) {
|
|
@@ -1032,7 +496,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1032
496
|
planModeRef.active = true;
|
|
1033
497
|
};
|
|
1034
498
|
const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
|
|
1035
|
-
const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv,
|
|
499
|
+
const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
|
|
1036
500
|
const liveInheritedGate = internals?.inheritedGate;
|
|
1037
501
|
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
1038
502
|
const inheritedAncestorRules = (() => {
|
|
@@ -1090,12 +554,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1090
554
|
const ownOrgVerdictRef = { current: undefined };
|
|
1091
555
|
const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
|
|
1092
556
|
const faceCheckpointSection = () => {
|
|
1093
|
-
if (resolvedReadFace === undefined) {
|
|
1094
|
-
const seed = resume
|
|
557
|
+
if (resolvedReadFace === undefined && resume !== undefined) {
|
|
558
|
+
const seed = resume.seed.readFace;
|
|
1095
559
|
return seed !== undefined ? { face: seed.face, ...(seed.denyEntries !== undefined ? { denyEntries: seed.denyEntries.map((e) => ({ ...e })) } : {}) } : undefined;
|
|
1096
560
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
561
|
+
const face = resolvedReadFace ?? handsLessResolvedFace;
|
|
562
|
+
if (face === undefined)
|
|
563
|
+
return undefined;
|
|
564
|
+
if (face === "open" || readDenyAdditionsNormalized.length > 0) {
|
|
565
|
+
return { face, ...(readDenyAdditionsNormalized.length > 0 ? { denyEntries: readDenyAdditionsNormalized.map((e) => ({ ...e })) } : {}) };
|
|
1099
566
|
}
|
|
1100
567
|
return undefined;
|
|
1101
568
|
};
|
|
@@ -1172,6 +639,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1172
639
|
let observersActive = false;
|
|
1173
640
|
let resolvedReadFace;
|
|
1174
641
|
let readDenyAdditionsNormalized = [];
|
|
642
|
+
let handsLessResolvedFace;
|
|
643
|
+
const carrierReadFace = () => resolvedReadFace ?? handsLessResolvedFace;
|
|
1175
644
|
const fullShellReachable = handsEnabled &&
|
|
1176
645
|
!(executionEnv instanceof StubExecutionEnv) &&
|
|
1177
646
|
spec.handsReadOnly !== true &&
|
|
@@ -1186,7 +655,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1186
655
|
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1187
656
|
...(frozenOnQuestion !== undefined ? { onQuestion: frozenOnQuestion } : {}),
|
|
1188
657
|
...(spec.handsReadOnly === true ? { handsReadOnly: true } : {}),
|
|
1189
|
-
...(
|
|
658
|
+
...(carrierReadFace() === "roots" ? { readFace: "roots" } : {}),
|
|
1190
659
|
...(readDenyAdditionsNormalized.length > 0 ? { readDenyPatterns: Object.freeze(readDenyAdditionsNormalized.map((e) => ({ ...e }))) } : {}),
|
|
1191
660
|
...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
1192
661
|
oneShot: spec.oneShot,
|
|
@@ -1200,7 +669,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1200
669
|
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
1201
670
|
...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
|
|
1202
671
|
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1203
|
-
parentCwd:
|
|
672
|
+
parentCwd: taskRootFinal,
|
|
1204
673
|
...(centerAdoption !== undefined ? { centerArtifactDigest: centerAdoption.artifact.artifactDigest } : {}),
|
|
1205
674
|
...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
|
|
1206
675
|
activeSkillScope: () => skillScope.active(),
|
|
@@ -1391,9 +860,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1391
860
|
...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
|
|
1392
861
|
autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
|
|
1393
862
|
workflowDepth: internals?.workflowDepth,
|
|
1394
|
-
parentCwd:
|
|
863
|
+
parentCwd: taskRootFinal,
|
|
1395
864
|
parentThinking: () => harnessRef.current?.getThinkingLevel() ?? thinking,
|
|
1396
|
-
parentReadFace: () =>
|
|
865
|
+
parentReadFace: () => carrierReadFace(),
|
|
1397
866
|
parentReadDenyPatterns: () => (readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized : undefined),
|
|
1398
867
|
onNotice: deps.onNotice,
|
|
1399
868
|
parentCheckpointStoreDisabled: spec.checkpointStore === null,
|
|
@@ -1613,7 +1082,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1613
1082
|
let attachmentRootCanonical;
|
|
1614
1083
|
let readDenyMatcher;
|
|
1615
1084
|
if (handsEnabled) {
|
|
1616
|
-
const rootRaw =
|
|
1085
|
+
const rootRaw = taskRootFinal;
|
|
1617
1086
|
const canon = await executionEnv.canonicalPath(rootRaw);
|
|
1618
1087
|
const rootCanonical = canon.ok ? canon.value : rootRaw;
|
|
1619
1088
|
attachmentRootCanonical = rootCanonical;
|
|
@@ -1686,11 +1155,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1686
1155
|
}
|
|
1687
1156
|
const seedReadFaceSection = resume?.seed.readFace;
|
|
1688
1157
|
const readDenyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seedReadFaceSection?.denyEntries ?? [])];
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1158
|
+
const readDenyBuiltinCfg = {
|
|
1159
|
+
...(deps.readDenyBuiltinTiers !== undefined ? { tiers: [...deps.readDenyBuiltinTiers] } : {}),
|
|
1160
|
+
...(deps.readDenyBuiltinExclude !== undefined ? { exclude: [...deps.readDenyBuiltinExclude] } : {}),
|
|
1161
|
+
};
|
|
1162
|
+
readDenyMatcher = compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg);
|
|
1163
|
+
readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
1694
1164
|
let liveReadFace = resolveReadFace({
|
|
1695
1165
|
specReadFace: spec.readFace,
|
|
1696
1166
|
depsReadFace: deps.readFace,
|
|
@@ -1727,6 +1197,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1727
1197
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
1728
1198
|
...(additionalReadRootsCanonical.length > 0 ? { additionalReadRoots: additionalReadRootsCanonical } : {}),
|
|
1729
1199
|
...(readDenyAdditions.length > 0 ? { readDenyPatterns: readDenyAdditions } : {}),
|
|
1200
|
+
...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
|
|
1201
|
+
...(readDenyBuiltinCfg.exclude !== undefined ? { readDenyBuiltinExclude: readDenyBuiltinCfg.exclude } : {}),
|
|
1730
1202
|
readFace: liveReadFace,
|
|
1731
1203
|
includeShell: handsIncludeShell,
|
|
1732
1204
|
readOnly: handsReadOnly,
|
|
@@ -1797,6 +1269,25 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1797
1269
|
}
|
|
1798
1270
|
}
|
|
1799
1271
|
}
|
|
1272
|
+
else {
|
|
1273
|
+
const liveFace = resolveReadFace({
|
|
1274
|
+
specReadFace: spec.readFace,
|
|
1275
|
+
depsReadFace: deps.readFace,
|
|
1276
|
+
readOnlyMount: spec.handsReadOnly === true,
|
|
1277
|
+
orgGoverned: deps.permissionRuleOrg !== undefined,
|
|
1278
|
+
fullShellReachable,
|
|
1279
|
+
});
|
|
1280
|
+
const seed = resume?.seed.readFace;
|
|
1281
|
+
handsLessResolvedFace = resume !== undefined && (seed === undefined || seed.face === "roots") ? "roots" : liveFace;
|
|
1282
|
+
const denyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seed?.denyEntries ?? [])];
|
|
1283
|
+
resolveReadDenyBuiltins({
|
|
1284
|
+
...(deps.readDenyBuiltinTiers !== undefined ? { tiers: deps.readDenyBuiltinTiers } : {}),
|
|
1285
|
+
...(deps.readDenyBuiltinExclude !== undefined ? { exclude: deps.readDenyBuiltinExclude } : {}),
|
|
1286
|
+
});
|
|
1287
|
+
if (denyAdditions.length > 0) {
|
|
1288
|
+
readDenyAdditionsNormalized = compileReadDeny(denyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1800
1291
|
if (backgroundTaskToolsActive || workflowToolsActive) {
|
|
1801
1292
|
toolEffects.set("TaskOutput", "read");
|
|
1802
1293
|
toolEffects.set("TaskStop", "write");
|
|
@@ -1898,7 +1389,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1898
1389
|
};
|
|
1899
1390
|
if (workspaceStateSettle !== undefined)
|
|
1900
1391
|
workspaceStateSettle.restoredWorktreeDir = worktreeSessionRef.current?.worktreeDir;
|
|
1901
|
-
tools.push(...createWorktreeTools(executionEnv, { repoRoot:
|
|
1392
|
+
tools.push(...createWorktreeTools(executionEnv, { repoRoot: taskRootFinal, cwdRef: handsCwdRef, session: worktreeSessionRef }).map((t) => firstPartyOffload(t)));
|
|
1902
1393
|
}
|
|
1903
1394
|
if (offloadStore)
|
|
1904
1395
|
tools.push(createReadToolResultTool(offloadStore));
|
|
@@ -1995,7 +1486,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1995
1486
|
}
|
|
1996
1487
|
const lspManager = spec.lspManager ?? deps.lspManager;
|
|
1997
1488
|
if (lspManager) {
|
|
1998
|
-
const lspRoot =
|
|
1489
|
+
const lspRoot = taskRootFinal;
|
|
1999
1490
|
tools.push(createLspTool(lspManager, { isPathIgnored: gitCheckIgnoreFilter(executionEnv, lspRoot), env: executionEnv }));
|
|
2000
1491
|
}
|
|
2001
1492
|
const lspDiagnostics = spec.lspDiagnostics !== false && lspManager?.diagnostics !== undefined && handsEnabled && spec.handsReadOnly !== true
|
|
@@ -2004,7 +1495,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2004
1495
|
const lspRunIdent = sessionId;
|
|
2005
1496
|
const nudgeLspOnEdit = lspDiagnostics
|
|
2006
1497
|
? (rawPath) => {
|
|
2007
|
-
const baseDir = handsCwdRef?.current ??
|
|
1498
|
+
const baseDir = handsCwdRef?.current ?? taskRootFinal;
|
|
2008
1499
|
const filePath = resolveLspPath(rawPath, baseDir);
|
|
2009
1500
|
lspDiagnostics.fileEdited(lspRunIdent, pathToUri(filePath));
|
|
2010
1501
|
void lspManager
|
|
@@ -2026,7 +1517,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2026
1517
|
spec,
|
|
2027
1518
|
deps,
|
|
2028
1519
|
sessionId,
|
|
2029
|
-
taskRootPath,
|
|
1520
|
+
taskRootPath: taskRootFinal,
|
|
2030
1521
|
memoryWriteGateRef,
|
|
2031
1522
|
writeToolsMounted: tools.some((t) => t.name === "Write") &&
|
|
2032
1523
|
!(toolFaceSnapshot.exclude?.includes("Write") ?? false) &&
|
|
@@ -2115,7 +1606,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2115
1606
|
let loaded = null;
|
|
2116
1607
|
try {
|
|
2117
1608
|
loaded = await Promise.resolve(deps.loadProjectMemory({
|
|
2118
|
-
cwd:
|
|
1609
|
+
cwd: taskRootFinal,
|
|
2119
1610
|
handsEnabled,
|
|
2120
1611
|
isSubagent: isDelegatedNonForkChild(internals),
|
|
2121
1612
|
...(internals?.agentName ? { agentName: internals.agentName } : {}),
|
|
@@ -2282,7 +1773,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2282
1773
|
envFacts.resumeFacts = copy;
|
|
2283
1774
|
}
|
|
2284
1775
|
if (handsEnabled) {
|
|
2285
|
-
envFacts.cwd =
|
|
1776
|
+
envFacts.cwd = taskRootFinal;
|
|
2286
1777
|
if (additionalRootsCanonical.length > 0) {
|
|
2287
1778
|
envFacts.additionalDirectories = [...additionalRootsCanonical];
|
|
2288
1779
|
}
|
|
@@ -3107,7 +2598,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3107
2598
|
if (deps.sessionPolicyStore && spec.sessionId) {
|
|
3108
2599
|
const rules = await deps.sessionPolicyStore.getRules(spec.sessionId, spec.principal);
|
|
3109
2600
|
if (rules) {
|
|
3110
|
-
sessionRulePolicy = createSessionRulePolicy(rules, { env: executionEnv, rootPath:
|
|
2601
|
+
sessionRulePolicy = createSessionRulePolicy(rules, { env: executionEnv, rootPath: taskRootFinal, toolEffects });
|
|
3111
2602
|
ownSessionRulesRef.current = {
|
|
3112
2603
|
sessionId: spec.sessionId,
|
|
3113
2604
|
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
@@ -3128,13 +2619,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3128
2619
|
catch {
|
|
3129
2620
|
}
|
|
3130
2621
|
}
|
|
3131
|
-
ancestorRulePolicies.push(createSessionRulePolicy(ancestorRules, { env: executionEnv, rootPath:
|
|
2622
|
+
ancestorRulePolicies.push(createSessionRulePolicy(ancestorRules, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
|
|
3132
2623
|
}
|
|
3133
2624
|
const skillScopePolicy = hasSkillManifest
|
|
3134
2625
|
? createActiveSkillScopePolicy({
|
|
3135
2626
|
scope: skillScope,
|
|
3136
2627
|
env: executionEnv,
|
|
3137
|
-
rootPath:
|
|
2628
|
+
rootPath: taskRootFinal,
|
|
3138
2629
|
toolEffects,
|
|
3139
2630
|
})
|
|
3140
2631
|
: undefined;
|
|
@@ -3300,7 +2791,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3300
2791
|
}
|
|
3301
2792
|
if (provider === undefined)
|
|
3302
2793
|
return undefined;
|
|
3303
|
-
const root =
|
|
2794
|
+
const root = taskRootFinal;
|
|
3304
2795
|
return {
|
|
3305
2796
|
admits: async (req) => {
|
|
3306
2797
|
const anonymous = spec.principal === undefined || spec.principal === "";
|
|
@@ -3920,6 +3411,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3920
3411
|
...riskAxesOf(req.toolName),
|
|
3921
3412
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
3922
3413
|
...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
|
|
3414
|
+
...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
|
|
3415
|
+
...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
|
|
3923
3416
|
}, onAsk, abortController.signal);
|
|
3924
3417
|
const waitMs = Math.max(0, now() - t0);
|
|
3925
3418
|
if (resolved.approverUnavailable !== true) {
|
|
@@ -4402,7 +3895,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4402
3895
|
}
|
|
4403
3896
|
};
|
|
4404
3897
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
4405
|
-
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason) => {
|
|
3898
|
+
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause) => {
|
|
4406
3899
|
const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
4407
3900
|
if (syncFirstEligible &&
|
|
4408
3901
|
runtimeCaps?.forceDurableGate !== true &&
|
|
@@ -4508,6 +4001,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4508
4001
|
args: parkedArgs,
|
|
4509
4002
|
safety,
|
|
4510
4003
|
...(shadowedRule !== undefined ? { shadowedRule } : {}),
|
|
4004
|
+
...(probeReason !== undefined ? { probeReason } : {}),
|
|
4005
|
+
...(probeCause !== undefined ? { probeCause } : {}),
|
|
4511
4006
|
shellGated: (req.toolName === "Bash" && shellGatedBash) || (req.toolName === "Monitor" && shellGatedMonitor),
|
|
4512
4007
|
...(effectiveShellGate !== "off" ? { shellGateDoctrine: effectiveShellGate } : {}),
|
|
4513
4008
|
});
|
|
@@ -5054,8 +4549,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5054
4549
|
}
|
|
5055
4550
|
: undefined;
|
|
5056
4551
|
overheadState.promptChars = systemPrompt.length;
|
|
4552
|
+
const effectiveReadFaceObserved = carrierReadFace();
|
|
4553
|
+
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
5057
4554
|
const preparedHolder = {};
|
|
5058
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettledBy, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4555
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath: taskRootFinal, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettledBy, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
5059
4556
|
const prepared = buildPrepared();
|
|
5060
4557
|
preparedHolder.current = prepared;
|
|
5061
4558
|
return prepared;
|