@sema-agent/core 7.5.1 → 7.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/core/protocol-table.d.ts +5 -0
  3. package/dist/core/protocol-table.js +1 -0
  4. package/dist/core/runner/abort-race.d.ts +41 -0
  5. package/dist/core/runner/abort-race.js +38 -0
  6. package/dist/core/runner/checkpoint-scope.d.ts +15 -3
  7. package/dist/core/runner/checkpoint-scope.js +3 -0
  8. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  9. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  10. package/dist/core/runner/content-ask-bindings.js +1 -0
  11. package/dist/core/runner/contracts.d.ts +118 -6
  12. package/dist/core/runner/denial-limit-arms.d.ts +23 -0
  13. package/dist/core/runner/denial-limit-arms.js +21 -0
  14. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  15. package/dist/core/runner/inherited-ask-grants.js +29 -0
  16. package/dist/core/runner/park-commit.d.ts +108 -0
  17. package/dist/core/runner/park-commit.js +32 -0
  18. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  19. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  20. package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
  21. package/dist/core/runner/prepare-ask-lane.js +133 -0
  22. package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
  23. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  24. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  25. package/dist/core/runner/prepare-context-lane.js +230 -0
  26. package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
  27. package/dist/core/runner/prepare-gate-stations.js +290 -0
  28. package/dist/core/runner/prepare-hands-readface.d.ts +4 -4
  29. package/dist/core/runner/prepare-inherited-gate.d.ts +4 -4
  30. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  31. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  32. package/dist/core/runner/prepare-park-ask.d.ts +164 -0
  33. package/dist/core/runner/prepare-park-ask.js +377 -0
  34. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  35. package/dist/core/runner/prepare-policy-chain.js +584 -0
  36. package/dist/core/runner/prepare-project-context.d.ts +1 -13
  37. package/dist/core/runner/prepare-project-context.js +1 -3
  38. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  39. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  40. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -20
  41. package/dist/core/runner/prepare-protocol-tools.d.ts +3 -3
  42. package/dist/core/runner/prepare-protocol-tools.js +0 -3
  43. package/dist/core/runner/prepare-question-face.d.ts +3 -21
  44. package/dist/core/runner/prepare-question-face.js +2 -1
  45. package/dist/core/runner/prepare-safety-scan.d.ts +0 -5
  46. package/dist/core/runner/prepare-safety-scan.js +1 -2
  47. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  48. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  49. package/dist/core/runner/prepare-task.d.ts +9 -136
  50. package/dist/core/runner/prepare-task.js +44 -2741
  51. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  52. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  53. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -3
  54. package/dist/core/runner/prepare-wiring-manifest.js +9 -2
  55. package/dist/core/runner/prepare-workspace-restore.d.ts +2 -29
  56. package/dist/core/runner/prepare-workspace-restore.js +3 -16
  57. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  58. package/dist/core/runner/prompt-hash-salt.js +2 -0
  59. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  60. package/dist/core/runner/remote-env-retry.js +16 -0
  61. package/dist/core/runner/runtask.js +1 -1
  62. package/dist/core/session.d.ts +12 -0
  63. package/dist/core/session.js +3 -0
  64. package/package.json +1 -1
  65. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  66. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -1,55 +1,40 @@
1
- import { createHash, randomBytes } from "node:crypto";
2
1
  import { realpathSync } from "node:fs";
3
2
  import { resolve as resolveFsPath } from "node:path";
4
- import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, summaryOutputBudgetTokens } from "../../internal/harness.js";
5
- const PROMPT_HASH_SALT = randomBytes(16);
3
+ import { AgentHarness, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, summaryOutputBudgetTokens } from "../../internal/harness.js";
6
4
  import { sanitizeCompactionSettings } from "../auto-compaction.js";
7
5
  import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
8
- import { gateAskCarry, inheritedAskCarry, headlessDenyAtFold, headlessDenyAtRecheck, judgeInheritedClassifier, settleDenialLimitFallback } from "./denial-limit-arms.js";
9
- import { primaryActivityArg } from "../arg-summary.js";
10
- import { Value } from "typebox/value";
11
6
  import { uuidv7 } from "../../engine/session/uuid.js";
12
7
  import { brainToRuntime } from "../runtime.js";
13
8
  import { hasSessionFork } from "../session.js";
14
9
  import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
15
- import { askApproverIdentity, isLiveApproverSeat, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
16
- import { suggestRulesForCommand } from "../permission-rule-model.js";
17
- import { PERSISTED_RULE_TOOL, inheritedAskRuleEvidence, createPermissionRuleLanes } from "./prepare-permission-rules.js";
18
- import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
19
- import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
20
- import { inlineUntrusted } from "../untrusted-text.js";
21
- import { loadAnnounceOnceLedger, serializeAnnouncedListings } from "./prepare-announce-once.js";
22
- import { isValidReminderMark, mintReminderMark, reminderMarkDeclaration } from "../reminder-mint.js";
23
- import { policyAskClassOf } from "../ask-class.js";
10
+ import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
11
+ import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
24
12
  import { emitTrace } from "../trace.js";
25
- import { createSessionRulePolicy, PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
26
- import { askCarryRowMembers, cloneObserverInput, formatHookFeedback, persistedRuleMandateOf, resolveHookTimeoutMs, runHookSeat, hookSeatExpiredError, runToolGate } from "../hooks.js";
27
- import { createWriteProtectionCheck } from "../write-protect.js";
28
- import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
29
13
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
30
14
  import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
31
- import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
32
- import { protocolOf } from "../protocol-table.js";
33
- import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
34
- import { createOffloadPersist, InMemoryToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, } from "../tool-result-store.js";
15
+ import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
35
16
  import { OUTPUT_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
36
17
  import { compileOutputSchema } from "./strict-output-schema.js";
37
18
  import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
38
- import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
39
- import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
40
- import { recordSyncUnattestable, sessionSettlements } from "../memory-engine/delegation-settlement.js";
41
- import { complianceCallDenial } from "../compliance.js";
42
19
  import { foldAdmissionFreeze } from "../memory-admission.js";
43
20
  import { prepareMemory } from "./prepare-memory.js";
21
+ import { prepareMemoryEngineSession } from "./prepare-memory-engine-session.js";
44
22
  import { prepareDeferClassify } from "./prepare-defer-classify.js";
45
23
  import { prepareListings } from "./prepare-listings.js";
46
24
  import { preparePromptInputs } from "./prepare-prompt-inputs.js";
47
- import { hasConversationContent, prepareProjectContext } from "./prepare-project-context.js";
25
+ import { prepareProjectContext } from "./prepare-project-context.js";
26
+ import { preparePolicyChain } from "./prepare-policy-chain.js";
27
+ import { prepareContextLane } from "./prepare-context-lane.js";
28
+ import { prepareTurnWiring } from "./prepare-turn-wiring.js";
29
+ export { runGuardChain } from "./prepare-context-lane.js";
30
+ import { preparePromptAssembly } from "./prepare-prompt-assembly.js";
48
31
  import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
49
- import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
32
+ import { prepareSafetyScan } from "./prepare-safety-scan.js";
50
33
  import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
51
34
  import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
52
- import { prepareWorkspaceRestore, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
35
+ import { prepareWorkspaceRestore } from "./prepare-workspace-restore.js";
36
+ import { parkContaminationMarker } from "./park-commit.js";
37
+ export { gatedCallIdOf, USAGE_WINDOW_REAP_MARGIN_MS } from "./park-commit.js";
53
38
  import { prepareOffloadWrappers } from "./prepare-offload-wrappers.js";
54
39
  import { prepareFileHistory } from "./prepare-file-history.js";
55
40
  import { prepareExecutionEnv } from "./prepare-execution-env.js";
@@ -59,42 +44,31 @@ import { prepareCapsAndWorkflow } from "./prepare-caps-and-workflow.js";
59
44
  import { prepareDelegationSurface } from "./prepare-delegation-surface.js";
60
45
  import { prepareToolDisclosureMount } from "./prepare-tool-disclosure-mount.js";
61
46
  import { prepareWiringManifest } from "./prepare-wiring-manifest.js";
62
- import { checkpointScopeOf } from "./checkpoint-scope.js";
63
- export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf } from "./checkpoint-scope.js";
47
+ import { prepareAskLane } from "./prepare-ask-lane.js";
48
+ import { prepareSuspendSaga } from "./prepare-suspend-saga.js";
49
+ import { prepareBoundaryParks } from "./prepare-boundary-parks.js";
50
+ import { prepareParkAsk } from "./prepare-park-ask.js";
51
+ import { prepareGateStations } from "./prepare-gate-stations.js";
52
+ export { batchContextAt } from "./prepare-park-ask.js";
53
+ export { compensateUnparkedPause } from "./prepare-suspend-saga.js";
54
+ import { placementValueOrAbsent } from "./checkpoint-scope.js";
55
+ export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf, placementValueOrAbsent } from "./checkpoint-scope.js";
64
56
  export { mcpManifestEntries } from "./prepare-wiring-manifest.js";
65
57
  import { derivedRouteFallsBack } from "./derived-route-fallback.js";
66
58
  import { prepareProtocolTools } from "./prepare-protocol-tools.js";
67
- import { CONTENT_ASK_BINDING_CAP, prepareQuestionFace } from "./prepare-question-face.js";
59
+ import { prepareQuestionFace } from "./prepare-question-face.js";
68
60
  import { prepareLsp } from "./prepare-lsp.js";
69
61
  import { createRollbackStack } from "./rollback-stack.js";
70
62
  export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
71
63
  export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
72
- import { defaultPromptProvider, buildEnvironmentContext } from "../../prompts/default.js";
73
- import { applyGitFrameGuard } from "./git-status-frame.js";
74
- import { assemblePrompt } from "../../prompt-assembly/assemble.js";
75
- import { auditToolCollisions, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
76
- import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
77
- import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
78
- import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
79
- import { SEMA_DEFAULT_PACK } from "../../prompt-assembly/packs/sema-default.js";
80
- import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureAssistants, editBudget, replayClearedProjection, resolveTriggerWindow, } from "../context-edit.js";
81
- import { capAggregateToolResults } from "../tool-result-budget.js";
82
- import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
83
- import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
84
- import { isIsolated, isRemoteExecutionEnv, isSuspendable, RemoteExecutionError } from "../remote-env.js";
64
+ import { resolveTriggerWindow, } from "../context-edit.js";
65
+ import { isRemoteExecutionEnv } from "../remote-env.js";
85
66
  import { settleTeardownLeg } from "./teardown-bounded.js";
86
- import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
87
67
  import { defaultTaskRegistry } from "../task-registry.js";
88
68
  import { announcePeerLaneMount } from "../../agents/peer-session-drain.js";
89
- import { applyCompactionToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
90
- import { decodeTextBytes } from "../../tools/fs/encoding.js";
91
- import { ASK_USER_QUESTION_TOOL_NAME, classifyQuestionOutcome, validateAskQuestions, } from "../ask-question.js";
92
69
  import { createSchedulerTools } from "../../tools/scheduler-tools.js";
93
- import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
94
70
  import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
95
- import { fileArgPath, resolveKey } from "../../tools/fs/safety.js";
96
- import { BINDING_CHECKPOINT_VERSION, mintCheckpointId, 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";
97
- import { boundInputHashOf } from "../canonical-json.js";
71
+ import { remainingBudgetMicroUsd } from "../checkpoint-store.js";
98
72
  import { durableParkGapFor } from "../park-selfcheck.js";
99
73
  import { deliverEngineNotice } from "../types.js";
100
74
  function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId) {
@@ -186,39 +160,9 @@ function toolModelGateManifest(gate) {
186
160
  }
187
161
  return firstClass === undefined ? undefined : { class: firstClass, removed, restore: TOOL_MODEL_GATE_RESTORE_SENTENCE };
188
162
  }
189
- function announceDeclaredMcpContentClasses(args) {
190
- for (const entry of args.entries ?? []) {
191
- const declaredClass = entry.contentOrigin;
192
- if (declaredClass === undefined)
193
- continue;
194
- const server = inlineUntrusted(String(entry.name), 160);
195
- const toolCount = args.statuses?.find((s) => s.name === entry.name)?.toolNames?.length ?? 0;
196
- const resourceFaceCaveat = " (Not covered: the cross-server resource faces — ListMcpResourcesTool/ReadMcpResourceTool/ReadMcpResourceDirTool — stay externally classified and still mark.)";
197
- const posture = declaredClass === "local"
198
- ? `this server's mounted tools' invocations will not mark this session's memory as externally exposed.${resourceFaceCaveat}`
199
- : declaredClass === "execution"
200
- ? `this server's mounted tools' invocations will not mark this session's memory unless the execIsExternalContent strict upgrade is armed (currently: ${args.execIsExternalContent ? "armed" : "off"}).${resourceFaceCaveat}`
201
- : "its tools stay externally classified and this declaration PINS that — the trustedTools allowlist no longer exempts them, and invocations mark this session's memory.";
202
- deliverEngineNotice(args.onNotice, {
203
- code: "memory.content_class_declared",
204
- message: `MCP server "${server}" (${toolCount} tool(s) mounted) is declared ` +
205
- `contentOrigin "${declaredClass}" by this deployment's configuration — ${posture}`,
206
- detail: {
207
- server,
208
- contentOrigin: declaredClass,
209
- toolCount,
210
- ...(declaredClass === "execution" ? { execIsExternalContent: args.execIsExternalContent } : {}),
211
- sessionId: args.sessionId,
212
- runId: args.runId,
213
- },
214
- });
215
- }
216
- }
217
163
  export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
218
164
  const DEFAULT_MAX_SUSPENDS = 5;
219
- const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
220
165
  export const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60_000;
221
- export const USAGE_WINDOW_REAP_MARGIN_MS = 60 * 60 * 1000;
222
166
  export function resolveEnvLifetimeExpiry(env, observedAt) {
223
167
  const lifetimeMs = env.lifetimeMs;
224
168
  if (lifetimeMs === undefined)
@@ -235,343 +179,13 @@ export function resolveEnvLifetimeExpiry(env, observedAt) {
235
179
  return { unanchored: true };
236
180
  return { expiresAt: anchor + lifetimeMs };
237
181
  }
238
- const DEFAULT_UNATTENDED_APPROVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000;
239
- function sanitizedTtlMs(ttlMs) {
240
- if (ttlMs === undefined)
241
- return undefined;
242
- return Number.isFinite(ttlMs) && ttlMs > 0 ? ttlMs : DEFAULT_RESOURCE_TTL_MS;
243
- }
244
- const advisedPolicyNames = new WeakMap();
245
- const ADVISED_KEYS_CAP = 64;
246
- class ParkRefusal extends Error {
247
- constructor(message, options) {
248
- super(message, options);
249
- this.name = "ParkRefusal";
250
- }
251
- }
252
182
  export { resolveCheckpointStore } from "../checkpoint-store.js";
253
183
  export { isFableFamilyModelId, resolveAttachmentsConfig, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
254
184
  export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./workspace-path.js";
255
- function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, knob) {
256
- const triggerWindow = resolveTriggerWindow(model);
257
- if (triggerWindow.clamped) {
258
- deliverEngineNotice(deps.onNotice, {
259
- code: "config.autocompact_window_clamped",
260
- message: `model "${model.id}" declares autoCompactTokens=${triggerWindow.declaredAutoCompactTokens} ABOVE its physical window ` +
261
- `${triggerWindow.physicalWindow}; the autocompact window only ever LOWERS the trigger-side geometry, so the value was ` +
262
- `clamped to the physical window — fix the model config (declare a value at or below the physical window, or omit it)`,
263
- detail: {
264
- modelId: model.id,
265
- declaredAutoCompactTokens: triggerWindow.declaredAutoCompactTokens,
266
- physicalWindow: triggerWindow.physicalWindow,
267
- sessionId,
268
- runId,
269
- },
270
- });
271
- }
272
- return {
273
- machine: knob.machine,
274
- clearOnRejection: knob.clearOnRejection,
275
- ledger: createClearedProjectionLedger(),
276
- projectionRef: {},
277
- inTurnCompactionRef: {},
278
- ...(offloadStore ? { offloadPersist: createOffloadPersist(offloadStore, sessionId, deps.onNotice) } : {}),
279
- };
280
- }
281
- function recordFrontierClears(args) {
282
- for (const { index } of args.pass.clears) {
283
- const at = args.index.keyAt(index);
284
- const clearedMsg = args.edited[index];
285
- const markerText = clearedMsg.content?.[0]?.text;
286
- if (at !== undefined && typeof markerText === "string") {
287
- args.ledger.entries.set(at.key, { marker: markerText, groupCount: at.groupCount, fp: at.fp });
288
- }
289
- }
290
- emitTrace(args.tracer, () => ({
291
- kind: "context.mc_clear",
292
- version: 1,
293
- taskId: args.taskId,
294
- clearedCount: args.pass.clears.length,
295
- tokensSavedEstimate: args.pass.tokensSavedEstimate,
296
- trigger: args.trigger,
297
- ts: Date.now(),
298
- }));
299
- }
300
- function flushProjectionObservations(pending) {
301
- for (const fire of pending.splice(0)) {
302
- try {
303
- fire();
304
- }
305
- catch {
306
- }
307
- }
308
- }
309
- export async function runGuardChain(args) {
310
- const { edited, microCompact, guardAt, charsPerToken, deps, taskId } = args;
311
- const applyGuardTrim = (view, anchoredTotal) => {
312
- let trimmed = trimToBudget(view, guardAt, anchoredTotal, charsPerToken);
313
- const dropped = trimmed.length < view.length;
314
- if (dropped) {
315
- args.trimPressureRef.droppedMessages = true;
316
- const droppedCount = view.length - trimmed.length;
317
- trimmed = insertTrimNotice(trimmed);
318
- emitTrace(deps.tracer, () => ({
319
- kind: "context.trim",
320
- version: 1,
321
- taskId,
322
- dropped: droppedCount,
323
- ts: Date.now(),
324
- }));
325
- }
326
- return { trimmed, dropped };
327
- };
328
- let working = edited;
329
- let anchoredWorking = estimateContextTokens(working, charsPerToken).tokens;
330
- if (microCompact.machine === "legacy") {
331
- const t = applyGuardTrim(working, anchoredWorking);
332
- return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
333
- }
334
- if (microCompact.machine === "off" && anchoredWorking > guardAt) {
335
- let blockingPass;
336
- const blockingEdited = clearStaleToolResults(working, {
337
- budgetTokens: guardAt,
338
- machine: "cc",
339
- clearSource: args.replayed,
340
- onCleared: (pass) => { blockingPass = pass; },
341
- anchoredTotalTokens: anchoredWorking,
342
- charsPerToken,
343
- ...(args.offloadStore ? { offload: { persist: createOffloadPersist(args.offloadStore, args.sessionId, deps.onNotice) } } : {}),
344
- });
345
- if (blockingPass !== undefined) {
346
- recordFrontierClears({ pass: blockingPass, index: args.index, edited: blockingEdited, ledger: microCompact.ledger, tracer: deps.tracer, taskId, trigger: "blocking" });
347
- working = blockingEdited;
348
- anchoredWorking = estimateContextTokens(working, charsPerToken).tokens;
349
- }
350
- }
351
- if (anchoredWorking > guardAt && args.recheck !== true) {
352
- const compacted = (await microCompact.inTurnCompactionRef.current?.(args.signal, anchoredWorking)) === true;
353
- if (compacted) {
354
- args.pendingProjectionObservations.length = 0;
355
- return { earlyReturn: { messages: working, adoptSessionRebuild: true } };
356
- }
357
- if (args.signal?.aborted === true) {
358
- args.pendingProjectionObservations.length = 0;
359
- return { working, trimmed: working, trimDroppedMessages: false };
360
- }
361
- }
362
- const t = applyGuardTrim(working, anchoredWorking);
363
- return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
364
- }
365
- export function gatedCallIdOf(p) {
366
- if (p.suspendRef.token !== undefined)
367
- return p.suspendRef.gatedCallId;
368
- if (p.reviewRef.token !== undefined)
369
- return p.reviewRef.gatedCallId;
370
- return undefined;
371
- }
372
- function parkContaminationMarker(refs) {
373
- if (refs.suspendRef.token === undefined && refs.reviewRef.token === undefined)
374
- return undefined;
375
- const gatedCallId = gatedCallIdOf(refs);
376
- return { code: "gate.parked", ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
377
- }
378
- function publishCommittedSuspend(refs, token, gate, scope, remoteHandle, checkpointId, pendingAction) {
379
- const ref = gate.kind === "needs_review" || gate.kind === "plan_review" ? refs.reviewRef : refs.suspendRef;
380
- ref.token = token;
381
- if (checkpointId !== undefined)
382
- ref.checkpointId = checkpointId;
383
- ref.gate = gate;
384
- ref.gatedCallId = pendingAction?.kind === "tool_approval" ? pendingAction.toolCallId : undefined;
385
- if (remoteHandle !== undefined)
386
- ref.restoreMode = remoteHandle.restoreMode === "park_only" ? "park_only" : "snapshot";
387
- ref.scope = scope;
388
- }
389
185
  export function effectiveDelegationFacts(internals, seedIsDelegatedChild) {
390
186
  const isDelegatedChild = internals?.isDelegatedChild !== undefined ? internals.isDelegatedChild === true : seedIsDelegatedChild === true;
391
187
  return { isDelegatedChild, isNonForkChild: isDelegatedChild && internals?.insideFork !== true };
392
188
  }
393
- export function batchContextAt(messages, currentId) {
394
- let batch = [];
395
- for (const m of messages) {
396
- if (m.role === "assistant") {
397
- const ids = [...new Set(m.content.filter((c) => c.type === "toolCall").map((c) => c.id))];
398
- if (ids.includes(currentId))
399
- batch = ids;
400
- }
401
- }
402
- const resolved = new Set(messages.filter((m) => m.role === "toolResult").map((m) => m.toolCallId));
403
- const completedCallIds = batch.filter((id) => id !== currentId && resolved.has(id));
404
- return { batchToolCallIds: batch, completedCallIds };
405
- }
406
- function composeCallSignal(runSignal, callSignal) {
407
- return callSignal !== undefined ? AbortSignal.any([runSignal, callSignal]) : runSignal;
408
- }
409
- function lateAskSettlementObserver(args) {
410
- return (late) => {
411
- if (late.kind === "approve") {
412
- deliverEngineNotice(args.onNotice, {
413
- code: "task.late_approval",
414
- message: `an approval for "${args.toolName}" was not consumed: a run or turn interrupt released the ask wait, ` +
415
- `so the tool did NOT run and the approval was not honored (an unconsumed answer is the approver ` +
416
- `releasing its wait, never a verdict).`,
417
- detail: {
418
- toolName: args.toolName,
419
- toolCallId: args.toolCallId,
420
- sessionId: args.sessionId,
421
- runId: args.runId,
422
- ...(args.taskId !== undefined ? { taskId: args.taskId } : {}),
423
- },
424
- });
425
- return;
426
- }
427
- args.onError?.(late.error, { phase: "hook", sessionId: args.sessionId });
428
- };
429
- }
430
- function askGrantShapeOf(args) {
431
- try {
432
- return JSON.stringify(args ?? null);
433
- }
434
- catch {
435
- return undefined;
436
- }
437
- }
438
- function consumeInheritedAskGrant(grants, onAsk, humanReviewRef, decision, req) {
439
- if (decision.decisionReason !== undefined && decision.decisionReason !== "rule")
440
- return undefined;
441
- const grant = grants.get(req.toolCallId);
442
- if (grant === undefined || askApproverIdentity(grant.approver) !== askApproverIdentity(onAsk) || grant.argsJson !== askGrantShapeOf(req.args))
443
- return undefined;
444
- grants.delete(req.toolCallId);
445
- humanReviewRef.count += 1;
446
- humanReviewRef.totalWaitMs += grant.waitMs;
447
- const toolArg = primaryActivityArg(req.args);
448
- humanReviewRef.gates.push({
449
- kind: "human",
450
- waitMs: grant.waitMs,
451
- decision: "allow",
452
- toolName: req.toolName,
453
- ...(toolArg !== undefined ? { toolArg } : {}),
454
- });
455
- return { action: "allow", presentedInput: grant.presented };
456
- }
457
- function raceSettlementAgainstSignal(p, signal) {
458
- const settled = p.then((value) => ({ tag: "value", value }), (error) => ({ tag: "threw", error }));
459
- if (signal.aborted)
460
- return Promise.resolve({ tag: "aborted" });
461
- return new Promise((resolve) => {
462
- let done = false;
463
- const finish = (r) => {
464
- if (done)
465
- return;
466
- done = true;
467
- signal.removeEventListener("abort", onAbortEvent);
468
- resolve(r);
469
- };
470
- const onAbortEvent = () => finish({ tag: "aborted" });
471
- signal.addEventListener("abort", onAbortEvent, { once: true });
472
- void settled.then(finish);
473
- });
474
- }
475
- const PARK_COMPENSATION_TIMEOUT_MS = 30_000;
476
- export async function compensateUnparkedPause(remoteEnv, snapshotId, io) {
477
- const boundCtl = new AbortController();
478
- const boundTimer = setTimeout(() => boundCtl.abort(), io.boundMs);
479
- const bound = boundCtl.signal;
480
- let phase = "resumeVM";
481
- let restoreAttempts = 1;
482
- let vmRunning = false;
483
- const work = (async () => {
484
- const { outcome: back, attempts: backAttempts } = await restoreWorkspaceWithRetry(remoteEnv, snapshotId, { abortSignal: bound }, (attempt) => (restoreAttempts = attempt));
485
- if (!back.ok) {
486
- io.noteFailure(remoteEnvFailureNote("resumeVM", back.error, backAttempts));
487
- io.disclose(back.error);
488
- return { ok: false, reason: `resumeVM failed (${back.error.code}) while compensating an unparked pause` };
489
- }
490
- vmRunning = true;
491
- if (bound.aborted) {
492
- return { ok: false, reason: "resumeVM settled only after the decision bound" };
493
- }
494
- phase = "postResumeInit";
495
- const init = await remoteEnv.postResumeInit({ abortSignal: bound });
496
- if (!init.ok) {
497
- io.noteFailure(remoteEnvFailureNote("postResumeInit", init.error, 1));
498
- io.disclose(init.error);
499
- return { ok: false, reason: `postResumeInit failed (${init.error.code}) while compensating an unparked pause` };
500
- }
501
- return { ok: true };
502
- })();
503
- const raced = await raceSettlementAgainstSignal(work, bound);
504
- clearTimeout(boundTimer);
505
- if (raced.tag === "value")
506
- return raced.value;
507
- if (raced.tag === "threw") {
508
- io.noteFailure(remoteEnvFailureNote(phase, raced.error instanceof RemoteExecutionError
509
- ? raced.error
510
- : new RemoteExecutionError("unknown", `the pause compensation's ${phase} call threw: ${raced.error instanceof Error ? raced.error.message : String(raced.error)}`), phase === "resumeVM" ? restoreAttempts : 1));
511
- io.disclose(raced.error);
512
- return {
513
- ok: false,
514
- reason: "the pause compensation threw (adapter contract violation; the deployment's error face carries the exception)",
515
- };
516
- }
517
- const timedOut = new RemoteExecutionError("timeout", `the pause compensation's ${phase} call did not settle within the ${io.boundMs}ms decision bound`);
518
- io.noteFailure(remoteEnvFailureNote(phase, timedOut, phase === "resumeVM" ? restoreAttempts : 1));
519
- io.disclose(timedOut);
520
- const destroyOwnerlessLate = () => {
521
- io.disclose(new Error(`park compensation settled after its ${io.boundMs}ms decision bound: the VM is running with no run ` +
522
- `left to own it — destroying the env (an ownerless running VM must not linger; the run already took ` +
523
- `the fail-closed arm when the bound fired)`));
524
- void Promise.resolve()
525
- .then(() => remoteEnv.destroy())
526
- .catch((destroyErr) => io.disclose(destroyErr));
527
- };
528
- void work.then((late) => {
529
- if (vmRunning) {
530
- destroyOwnerlessLate();
531
- return;
532
- }
533
- void late;
534
- }, (lateErr) => {
535
- io.disclose(lateErr);
536
- if (vmRunning)
537
- destroyOwnerlessLate();
538
- });
539
- return {
540
- ok: false,
541
- 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)`,
542
- };
543
- }
544
- function raceAbort(p, signal, onAbort) {
545
- if (signal.aborted)
546
- return Promise.resolve(onAbort());
547
- return new Promise((resolve) => {
548
- let settled = false;
549
- const finish = (v) => {
550
- if (settled)
551
- return;
552
- settled = true;
553
- signal.removeEventListener("abort", onAbortEvent);
554
- resolve(v);
555
- };
556
- const onAbortEvent = () => finish(onAbort());
557
- signal.addEventListener("abort", onAbortEvent, { once: true });
558
- p.then(finish, () => finish(onAbort()));
559
- });
560
- }
561
- function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
562
- if (hooks?.permissionDenied === undefined)
563
- return undefined;
564
- return async (payload) => {
565
- try {
566
- const seat = await runHookSeat("permissionDenied", { timeoutMs: hookTimeoutMs, signal }, (sig) => hooks.permissionDenied({ ...payload, signal: sig }));
567
- if (seat.expired)
568
- report(hookSeatExpiredError("permissionDenied", hookTimeoutMs, seat.cause, "the deny observation was abandoned; the deny itself is unchanged"));
569
- }
570
- catch (err) {
571
- report(err);
572
- }
573
- };
574
- }
575
189
  async function forgetQuietly(sessions, sessionId) {
576
190
  try {
577
191
  if (sessions.forget)
@@ -580,144 +194,6 @@ async function forgetQuietly(sessions, sessionId) {
580
194
  catch {
581
195
  }
582
196
  }
583
- function screenGateSettlement(result, settling) {
584
- const defects = [];
585
- const reportedBy = result.settledBy;
586
- let settledBy;
587
- if (reportedBy !== undefined) {
588
- if (!isApprovalSettledBy(reportedBy) || (!settling && reportedBy !== "human")) {
589
- defects.push(`a tool-gate settlement reported settledBy "${String(reportedBy)}" on ${settling ? "a blocked" : "an executing"} call — ` +
590
- `it is one of "human" / "timeout" / "aborted", and only "human" can be the source of a call that runs; the frame carries no source`);
591
- }
592
- else
593
- settledBy = reportedBy;
594
- }
595
- const attribution = screenApproverAttribution(result.approver);
596
- if (attribution.defect !== undefined) {
597
- defects.push(`a tool-gate settlement reported an attribution this engine refuses: ${attribution.defect}; the frame carries no approver`);
598
- }
599
- const reportedResolution = result.resolution;
600
- let resolution;
601
- if (reportedResolution !== undefined) {
602
- if (!isAskDenyResolution(reportedResolution) || !settling) {
603
- defects.push(`a tool-gate settlement reported a deny resolution "${String(reportedResolution)}" on ${settling ? "a blocked" : "an executing"} call — ` +
604
- `it is one of the closed ask-deny vocabulary and only a BLOCKED call can carry one; the frame carries no resolution`);
605
- }
606
- else
607
- resolution = reportedResolution;
608
- }
609
- const reportedAutoDenied = result.autoDenied;
610
- let autoDenied;
611
- if (reportedAutoDenied === true) {
612
- if (!settling)
613
- defects.push("a tool-gate settlement reported autoDenied on an executing call — only a BLOCKED call can carry the marker; the frame carries none");
614
- else
615
- autoDenied = true;
616
- }
617
- const record = {
618
- ...(settledBy !== undefined ? { settledBy } : {}),
619
- ...(attribution.approver !== undefined ? { approver: attribution.approver } : {}),
620
- ...(resolution !== undefined ? { resolution } : {}),
621
- ...(autoDenied !== undefined ? { autoDenied } : {}),
622
- };
623
- return { ...(settledBy !== undefined || attribution.approver !== undefined || resolution !== undefined || autoDenied !== undefined ? { record } : {}), defects };
624
- }
625
- function maybeHumanRejectionHalt(input) {
626
- if (!input.bare || input.settledBy !== "human" || input.isDelegatedChild)
627
- return undefined;
628
- return {
629
- reason: `This tool call was NOT executed: the user rejected the "${input.toolName}" tool call in the same ` +
630
- `assistant message, which stops the rest of the batch. Nothing was run for this call — ` +
631
- `re-issue it after the user's direction only if it is still needed.`,
632
- details: {
633
- error: "gate.batch_halted",
634
- code: "gate.batch_halted",
635
- rejectedToolCallId: input.toolCallId,
636
- rejectedToolName: input.toolName,
637
- },
638
- };
639
- }
640
- const sanitizePreview = (node, depth = 0) => {
641
- if (depth > 6)
642
- return undefined;
643
- if (typeof node === "string") {
644
- return node.replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "\u2400");
645
- }
646
- if (node === null || typeof node !== "object")
647
- return node;
648
- if (Array.isArray(node))
649
- return node.map((v) => sanitizePreview(v, depth + 1));
650
- const out = {};
651
- for (const [k, v] of Object.entries(node)) {
652
- out[sanitizePreview(k, depth + 1)] = sanitizePreview(v, depth + 1);
653
- }
654
- return out;
655
- };
656
- function resolveApprovalPreview(tools, toolName, args) {
657
- const t = tools.find((x) => x.name === toolName || (x.aliases?.includes(toolName) ?? false));
658
- if (t?.approvalPreview === undefined)
659
- return {};
660
- try {
661
- const raw = t.approvalPreview(args);
662
- if (raw === undefined)
663
- return {};
664
- const bytes = JSON.stringify(raw);
665
- if (bytes === undefined)
666
- return { withheld: "unavailable" };
667
- if (bytes.length > 16_384) {
668
- return { preview: { truncated: true, note: `approval preview exceeded 16KiB (${bytes.length} chars serialized)` }, withheld: "oversize" };
669
- }
670
- return { preview: sanitizePreview(raw) };
671
- }
672
- catch {
673
- return { withheld: "unavailable" };
674
- }
675
- }
676
- function makeRuleOffersOf(cfg) {
677
- return (toolName, args, ask) => {
678
- if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL)
679
- return {};
680
- if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared)
681
- return {};
682
- const closedDoor = (() => {
683
- if (ask?.requiresRealApproval === true)
684
- return "mandated";
685
- if (ask?.persistedRuleShadowed !== undefined)
686
- return "shadowed";
687
- if (ask?.decisionReason === "hook")
688
- return "mandated";
689
- if (ask?.matchedAskRule !== undefined)
690
- return "shadowed";
691
- if (ask?.inheritedUnresolved === true)
692
- return "mandated";
693
- if (ask?.ancestorResolved === true)
694
- return "mandated";
695
- return undefined;
696
- })();
697
- if (closedDoor !== undefined)
698
- return { ruleOffersAbsence: closedDoor };
699
- if (persistedRuleMandateOf({
700
- egress: cfg.egressTools.has(toolName),
701
- irreversibility: cfg.irreversibilityTier.get(toolName),
702
- shellGated: cfg.shellGatedBash,
703
- probeMandated: ask?.probeMandated === true,
704
- }) !== undefined) {
705
- return { ruleOffersAbsence: "mandated" };
706
- }
707
- const command = args?.command;
708
- if (typeof command !== "string")
709
- return { ruleOffersAbsence: "lane_cannot_speak" };
710
- const offers = suggestRulesForCommand(command, {
711
- ...(ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : {}),
712
- ...(cfg.taskRoot !== undefined ? { scope: { kind: "project", root: cfg.taskRoot }, cwd: cfg.taskRoot } : {}),
713
- ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}),
714
- ...(cfg.deniesDirectoryRead !== undefined ? { deniesDirectoryRead: cfg.deniesDirectoryRead } : {}),
715
- });
716
- return offers.length > 0
717
- ? { ruleOffers: offers, ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}) }
718
- : { ruleOffersAbsence: "lane_cannot_speak" };
719
- };
720
- }
721
197
  async function adoptReminderMark(session, sessionId, seedMark, spawnMark, onError) {
722
198
  const sessionReminderMark = await (async () => {
723
199
  try {
@@ -753,12 +229,6 @@ function restorePlacementRoot(internals, seedPlacementRoot) {
753
229
  return internals;
754
230
  return { ...(internals ?? {}), placementRoot: seed };
755
231
  }
756
- export function placementValueOrAbsent(value) {
757
- return value || undefined;
758
- }
759
- function stampPlacementRootSessionId(placementRootResolved) {
760
- return placementValueOrAbsent(placementRootResolved);
761
- }
762
232
  function refuseRequireExistingWithoutSession(spec) {
763
233
  if (spec.requireExistingSession && !spec.sessionId) {
764
234
  const e = new Error(`requireExistingSession requires a sessionId — cannot require an existing session without one (design/114 Phase3)`);
@@ -766,14 +236,6 @@ function refuseRequireExistingWithoutSession(spec) {
766
236
  throw e;
767
237
  }
768
238
  }
769
- function autoModeLatchHealthy(d) {
770
- try {
771
- return d.breakerOpen() !== true && typeof d.consecutiveFailures === "function" && d.consecutiveFailures() === 0;
772
- }
773
- catch {
774
- return false;
775
- }
776
- }
777
239
  function validateInputFaultNotice(onError, sessionId) {
778
240
  return ({ toolName, toolCallId, error }) => onError?.(new Error(`tool input pre-validation (validateInput) threw for ${toolName} (call ${toolCallId}) — read as no verdict; the call proceeded to the gate: ${error instanceof Error ? error.message : String(error)}`), { phase: "hook", sessionId });
779
241
  }
@@ -900,8 +362,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
900
362
  let resolvedReadFace;
901
363
  let readDenyAdditionsNormalized = [];
902
364
  let handsLessResolvedFace;
903
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, centerAdoption: () => centerAdoption, memoryEngineSession: () => memoryEngineSession });
904
- const { tools, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoption, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
365
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
366
+ const { tools, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
905
367
  const suspendRef = {};
906
368
  const reviewRef = {};
907
369
  const remoteEnvFailures = [];
@@ -934,9 +396,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
934
396
  resourceSpentMicroUsd: priorLedger?.spentMicroUsd ?? 0,
935
397
  suspendCount: priorSuspendCount,
936
398
  };
937
- let suspendForResource;
938
- let suspendForPlatformLimit;
939
- let suspendForReview;
940
399
  const outputRef = {};
941
400
  if (resume?.seed.outputRef) {
942
401
  outputRef.value = resume.seed.outputRef.value;
@@ -1097,323 +556,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1097
556
  const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
1098
557
  const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, gitStatusRef } = await preparePromptInputs({ spec, deps, failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId });
1099
558
  const { deferred, memoryBlock } = prepareDeferClassify({ spec, deps, tools, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
1100
- const delegationProvenanceChannel = internals?.delegationProvenance;
1101
- if (memoryEngineSession !== undefined || delegationProvenanceChannel !== undefined) {
1102
- const pollution = memoryEngineSession?.pollution;
1103
- const delegationEvidenceAttestedOnly = memoryDelegationEvidence === "attested-only";
1104
- let staticMarkWaiverAnnounced = false;
1105
- const effectiveSafety = narrowContentSafety(delegationProvenanceChannel?.contentSafety, memoryEngineSession?.contentSafety);
1106
- const trustedTools = effectiveSafety.trustedTools;
1107
- const execIsExternalContent = effectiveSafety.execIsExternalContent || externalContentTargetActive;
1108
- provenanceForChildrenRef.current = { trustedTools: [...trustedTools], execIsExternalContent };
1109
- announceDeclaredMcpContentClasses({
1110
- entries: lockedPreflight.mcp,
1111
- statuses: mcp?.statuses,
1112
- execIsExternalContent,
1113
- sessionId,
1114
- runId,
1115
- onNotice: deps.onNotice,
1116
- });
1117
- if (memoryEngineSession?.settlement !== undefined) {
1118
- delegationSettlementRef.current = { controlDir: memoryEngineSession.settlement.controlDir, sessionId: memoryEngineSession.settlement.sessionId };
1119
- }
1120
- const recordExternal = () => {
1121
- if (delegationProvenanceChannel !== undefined)
1122
- delegationProvenanceChannel.ref.current.sawExternal = true;
1123
- };
1124
- const recordIncomplete = () => {
1125
- if (delegationProvenanceChannel !== undefined)
1126
- delegationProvenanceChannel.ref.current.incomplete = true;
1127
- };
1128
- const callerToolNames = new Set((spec.tools ?? []).map((t) => t.name));
1129
- const contentOriginWrapped = new WeakSet();
1130
- const classOf = (t) => externalContentTargetActive && envHandToolNames.has(t.name)
1131
- ? "external"
1132
- : classifyToolContentOrigin({
1133
- declared: t.contentOrigin,
1134
- isProtocolTool: protocolOf(t.name) !== undefined,
1135
- isCallerTool: callerToolNames.has(t.name),
1136
- trusted: trustedTools.has(t.name),
1137
- });
1138
- const delegationByName = new Map();
1139
- for (const t of spec.tools ?? []) {
1140
- const withFaces = t;
1141
- if (withFaces.agentToolFaces === undefined && t.agentListing === undefined)
1142
- continue;
1143
- delegationByName.set(t.name, { faces: withFaces.agentToolFaces, pool: withFaces.agentToolPool });
1144
- }
1145
- const poolToolPollutes = (t) => contentOriginPollutes(classifyToolContentOrigin({
1146
- declared: t.contentOrigin,
1147
- isProtocolTool: protocolOf(t.name) !== undefined,
1148
- isCallerTool: true,
1149
- trusted: trustedTools.has(t.name),
1150
- }), execIsExternalContent);
1151
- const recallTaintJudge = memoryEngineSession?.recallTaint?.judgeDeliveredPath;
1152
- contentOriginWrapRef.current = () => {
1153
- for (let i = 0; i < tools.length; i++) {
1154
- const t = tools[i];
1155
- if (contentOriginWrapped.has(t))
1156
- continue;
1157
- const origin = classOf(t);
1158
- const delegation = delegationByName.get(t.name);
1159
- const isDelegation = delegation !== undefined;
1160
- const pollutes = contentOriginPollutes(origin, execIsExternalContent);
1161
- const readsMemoryPlane = recallTaintJudge !== undefined && t.name === "Read";
1162
- if (!pollutes && !isDelegation && !readsMemoryPlane) {
1163
- contentOriginWrapped.add(t);
1164
- continue;
1165
- }
1166
- const inner = t.execute.bind(t);
1167
- const mark = (reason, cause) => {
1168
- try {
1169
- pollution?.markPolluted(reason, cause);
1170
- }
1171
- catch {
1172
- }
1173
- };
1174
- const wrapped = {
1175
- ...t,
1176
- execute: async (toolCallId, params, signal, onUpdate) => {
1177
- if (pollutes) {
1178
- mark(`tool "${t.name}" (${origin} content class) was invoked in this session`, "observed");
1179
- recordExternal();
1180
- }
1181
- if (!isDelegation) {
1182
- if (!readsMemoryPlane)
1183
- return inner(toolCallId, params, signal, onUpdate);
1184
- const result = await inner(toolCallId, params, signal, onUpdate);
1185
- const raw = result;
1186
- const contentField = typeof raw === "object" && raw !== null ? raw.content : undefined;
1187
- const headText = typeof raw === "string"
1188
- ? raw
1189
- : typeof contentField === "string"
1190
- ? contentField
1191
- : Array.isArray(contentField) && typeof contentField[0]?.text === "string"
1192
- ? contentField[0].text
1193
- : "";
1194
- const delivered = result?.isError !== true &&
1195
- result?.details?.type !== "file_unchanged" &&
1196
- !headText.startsWith("<system-reminder");
1197
- const fp = fileArgPath(params);
1198
- if (delivered && fp !== undefined && recallTaintJudge(fp)) {
1199
- mark(`the "${t.name}" tool delivered the content of an external-origin memory entry file`, "derived");
1200
- }
1201
- return result;
1202
- }
1203
- let preCallLaunchRows;
1204
- if (delegationEvidenceAttestedOnly && memoryEngineSession?.settlement !== undefined) {
1205
- try {
1206
- preCallLaunchRows = new Set(sessionSettlements(memoryEngineSession.settlement.controlDir, memoryEngineSession.settlement.sessionId)
1207
- .filter((r) => r.row.lane === "background" && r.row.toolUseId === toolCallId)
1208
- .map((r) => r.row.settleId));
1209
- }
1210
- catch {
1211
- preCallLaunchRows = undefined;
1212
- }
1213
- }
1214
- const requested = params?.subagent_type;
1215
- const external = delegationCallIsExternal({
1216
- requestedType: typeof requested === "string" ? requested : undefined,
1217
- faces: delegation.faces,
1218
- pool: delegation.pool,
1219
- isPolluting: poolToolPollutes,
1220
- });
1221
- const result = await inner(toolCallId, params, signal, onUpdate);
1222
- const att = readCardAttestation(result?.details);
1223
- if (att === "external") {
1224
- mark(`tool "${t.name}" returned the output of a delegated agent that invoked an external-content-class tool at runtime` +
1225
- (typeof requested === "string" ? ` (subagent_type "${requested}")` : ""), "observed");
1226
- recordExternal();
1227
- }
1228
- else if (att !== "clean") {
1229
- if (external) {
1230
- const staticFaceReason = `tool "${t.name}" returned the output of a delegated agent whose tool face can reach external content` +
1231
- (typeof requested === "string" ? ` (subagent_type "${requested}")` : " (no subagent_type named — classified fail-closed)");
1232
- if (!delegationEvidenceAttestedOnly) {
1233
- mark(staticFaceReason, "static");
1234
- }
1235
- else {
1236
- const receiptDetails = result?.details;
1237
- const claimsLaunchReceipt = receiptDetails !== undefined && receiptDetails !== null && receiptDetails.status === "async_launched";
1238
- const claimedSettleId = claimsLaunchReceipt && typeof receiptDetails.settle_id === "string" ? receiptDetails.settle_id : undefined;
1239
- const settlementSeat = memoryEngineSession?.settlement;
1240
- let isLaunchReceipt = false;
1241
- if (claimsLaunchReceipt && settlementSeat !== undefined) {
1242
- try {
1243
- isLaunchReceipt =
1244
- preCallLaunchRows !== undefined &&
1245
- claimedSettleId !== undefined &&
1246
- !preCallLaunchRows.has(claimedSettleId) &&
1247
- sessionSettlements(settlementSeat.controlDir, settlementSeat.sessionId).some((r) => r.row.lane === "background" && r.row.settleId === claimedSettleId && r.row.toolUseId === toolCallId);
1248
- }
1249
- catch {
1250
- isLaunchReceipt = false;
1251
- }
1252
- }
1253
- if (!isLaunchReceipt && settlementSeat !== undefined) {
1254
- try {
1255
- recordSyncUnattestable(settlementSeat.controlDir, { sessionId: settlementSeat.sessionId, toolCallId, now: Date.now });
1256
- }
1257
- catch (err) {
1258
- const e = new Error(`delegated output withheld (fail-closed): the delegation delivered without a provable attestation and the settlement account could not record the fact (${err instanceof Error ? err.message : String(err)}) — repair the memory control plane or retry`);
1259
- e.code = "memory.settlement_record_failed";
1260
- throw e;
1261
- }
1262
- }
1263
- }
1264
- if (delegationEvidenceAttestedOnly && pollution !== undefined && !staticMarkWaiverAnnounced) {
1265
- staticMarkWaiverAnnounced = true;
1266
- const waivedReason = inlineUntrusted(staticFaceReason, 200);
1267
- deliverEngineNotice(deps.onNotice, {
1268
- code: "memory.delegation_static_mark_waived",
1269
- message: `Delegation static-face pollution mark waived: ${waivedReason}. The deployment configured ` +
1270
- `memoryDelegationEvidence="attested-only", so a static tool-face verdict alone does not mark this ` +
1271
- `session's memory polluted; a delivered "external" attestation and a direct polluting-class ` +
1272
- `invocation still mark, and on a provenance-armed chain this delegation still records as ` +
1273
- `incomplete. At most one notice per prepared task leg.`,
1274
- detail: {
1275
- reason: waivedReason,
1276
- ...(typeof requested === "string" ? { subagentType: inlineUntrusted(requested, 120) } : {}),
1277
- sessionId,
1278
- runId,
1279
- },
1280
- });
1281
- }
1282
- }
1283
- recordIncomplete();
1284
- }
1285
- return result;
1286
- },
1287
- };
1288
- contentOriginWrapped.add(wrapped);
1289
- tools[i] = wrapped;
1290
- }
1291
- };
1292
- contentOriginWrapRef.current();
1293
- }
1294
- let centerAdoption;
1295
- let centerAdoptionFresh = false;
1296
- {
1297
- const failUnavailable = (digest, via) => {
1298
- const e = new Error(`prompt snapshot unavailable: ${via} center prompt artifact ${digest.slice(0, 23)}… has no verified resolvable copy (artifact store miss/corrupt${deps.promptSource === undefined ? "; RunnerDeps.promptSource is not configured" : ""}) — refusing to recompose a hybrid prompt (protocol §9.2)`);
1299
- e.code = "prompt.snapshot_unavailable";
1300
- throw e;
1301
- };
1302
- const pinnedEarly = await session.getPromptEpoch();
1303
- if (pinnedEarly?.centerArtifactDigest !== undefined) {
1304
- const art = await deps.promptSource?.get(pinnedEarly.centerArtifactDigest);
1305
- if (art === undefined)
1306
- failUnavailable(pinnedEarly.centerArtifactDigest, "this session is pinned to");
1307
- centerAdoption = { artifact: art, ...(pinnedEarly.sourceRevision !== undefined ? { sourceRevision: pinnedEarly.sourceRevision } : {}) };
1308
- }
1309
- else if (pinnedEarly === undefined) {
1310
- if (internals?.parentCenterArtifactDigest !== undefined) {
1311
- const art = await deps.promptSource?.get(internals.parentCenterArtifactDigest);
1312
- if (art === undefined)
1313
- failUnavailable(internals.parentCenterArtifactDigest, "the spawning parent adopted");
1314
- centerAdoption = {
1315
- artifact: art,
1316
- ...(internals.parentCenterSourceRevision !== undefined ? { sourceRevision: internals.parentCenterSourceRevision } : {}),
1317
- };
1318
- }
1319
- else if (resume === undefined && internals?.parentTaskId === undefined && internals?.parentSessionId === undefined) {
1320
- const candidate = deps.promptSource?.current();
1321
- if (candidate !== undefined && !hasConversationContent(await session.getBranch())) {
1322
- centerAdoption = { artifact: candidate.artifact, sourceRevision: candidate.sourceRevision };
1323
- centerAdoptionFresh = true;
1324
- }
1325
- }
1326
- }
1327
- }
1328
- const buildAssembleInputs = (centerDecls) => ({
1329
- provider,
1330
- isDefaultProvider: provider === defaultPromptProvider,
1331
- userSystemPrompt,
1332
- userAppendSystemPrompt,
1333
- tools: tools.map((t) => (t.aliases === undefined ? { ...t } : { ...t, aliases: [...t.aliases] })),
1334
- facts: {
1335
- supervisorEnabled: false,
1336
- teammateEnabled: internals?.explicitAgentName !== undefined && tools.some((t) => t.name === SEND_MESSAGE_TOOL_NAME),
1337
- ...featureFlags,
1338
- orchestrationEnabled: featureFlags.orchestrationEnabled && tools.some((t) => t.name === "Workflow"),
1339
- orchestrationDeferred: featureFlags.orchestrationDeferred && tools.some((t) => t.name === "Workflow"),
1340
- promptProfile,
1341
- fableMitigations,
1342
- },
1343
- mcpInstructionsBlock,
1344
- memoryBlock,
1345
- buildEnvBlock: (date) => buildEnvironmentContext({ ...envFacts, date }) || undefined,
1346
- date: envFacts.date,
1347
- ...(model.promptGuidance && model.promptGuidance.length > 0 ? { modelGuidance: model.promptGuidance.join("\n\n") } : {}),
1348
- onWarn: (message, phase) => deps.onError?.(new Error(message), { phase, sessionId }),
1349
- ...(centerDecls !== undefined ? { centerDeclarations: centerDecls } : {}),
1350
- });
1351
- const assembled = assemblePrompt(buildAssembleInputs(centerAdoption !== undefined ? artifactDeclarations(centerAdoption.artifact) : undefined));
1352
- if (centerAdoption !== undefined && assembled.centerMounted !== true) {
1353
- const ownsWholePrompt = assembled.constitution === "provider-assembled";
1354
- const digest = centerAdoption.artifact.artifactDigest.slice(0, 23);
1355
- if (ownsWholePrompt) {
1356
- const e = new Error(`center prompt artifact ${digest}… cannot mount: this PromptProvider owns the whole prompt (assembled-identity pass-through) — upgrade the provider to stableBlocks/stableSystem or remove RunnerDeps.promptSource for this deployment`);
1357
- e.code = "prompt.center_unmountable";
1358
- throw e;
1359
- }
1360
- deps.onError?.(new Error(centerAdoptionFresh
1361
- ? `prompt-constitution: center prompt artifact ${digest}… contributed NO section to this prompt — every section it declares collides with a PromptProvider stableBlocks declaration of the same id, and the deployment-local declaration wins. This session runs on the bundled prompt and is deliberately NOT pinned to the artifact (a pin claiming content the model never received would be durable and wrong). Rename the colliding section ids on either side to adopt it; the per-section collisions are reported individually on this channel.`
1362
- : `prompt-constitution: this session is pinned to center prompt artifact ${digest}…, but under the CURRENT PromptProvider every section that artifact declares collides with a stableBlocks declaration of the same id, so none of them compiled into this prompt. The session keeps its pin and continues on the bundled prompt — its epoch's declaredSections record what actually compiled. Rename the colliding section ids on either side to restore the artifact's content.`), { phase: "prompt-constitution", sessionId });
1363
- if (centerAdoptionFresh)
1364
- centerAdoption = undefined;
1365
- }
1366
- let systemPrompt = assembled.systemPrompt;
1367
- if (!systemPrompt.includes(reminderMarkDeclaration(reminderMark))) {
1368
- deps.onError?.(new Error("reminder-mark declaration missing from the assembled system prompt: this run's PromptProvider owns the prompt and did not render the mark declaration, so engine <system-reminder> tags carry a mark the model is never told about (the impersonation defense is inactive for this deployment). Render `reminderMarkDeclaration(ctx.reminderMark)` (exported; the value rides StablePromptContext.reminderMark) in the provider, or accept the disclosure."), { phase: "config", sessionId });
1369
- }
1370
- const renderWithDate = assembled.renderWithDate
1371
- ? (date) => {
1372
- systemPrompt = assembled.renderWithDate(date);
1373
- return systemPrompt;
1374
- }
1375
- : undefined;
1376
- const promptConstitution = assembled.constitution;
1377
- const promptBlocks = assembled.legacyBlocks;
1378
- const collisionAudit = auditToolCollisions(tools);
1379
- for (const y of collisionAudit.yields) {
1380
- onceLedger.onError(new Error(`tool mount: caller tool "${y.name}" shadows another tool with the same canonical name (deliberate last-write-wins yield — the later mount serves)`), { phase: "config", sessionId });
1381
- }
1382
- for (const c of collisionAudit.aliasCollisions) {
1383
- onceLedger.onError(new Error(`tool mount: alias "${c.alias}" is claimed by both "${c.owners[0]}" and "${c.owners[1]}" (design/141 warn face — name resolution serves the later mount)`), { phase: "config", sessionId });
1384
- }
1385
- const saltedHash = (text) => createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex").slice(0, 12);
1386
- const promptManifest = {
1387
- constitution: promptConstitution,
1388
- blocks: promptBlocks.map((b) => ({ id: b.id, chars: b.text.length, hash: saltedHash(b.text) })),
1389
- ...(assembled.sections
1390
- ? { sections: assembled.sections.map((s) => ({ id: s.id, slot: s.slot, carrier: s.carrier, cadence: s.cadence, cacheClass: s.cacheClass, chars: s.text.length, hash: saltedHash(s.text), ...(s.declaredContentHash !== undefined ? { contentHash: s.declaredContentHash } : {}) })) }
1391
- : {}),
1392
- tools: projectToolManifest(tools, (t) => protocolOf(t.name)?.id ?? "caller"),
1393
- };
1394
- const epochDeclaredSections = (assembled.sections ?? [])
1395
- .filter((s) => s.origin === "provider")
1396
- .map((s) => ({ id: s.id, slot: s.slot, chars: s.text.length, ...(s.declaredContentHash !== undefined ? { contentHash: s.declaredContentHash } : {}) }));
1397
- let epochArtifactDigestForSnapshot = "";
1398
- {
1399
- const pinned = await session.getPromptEpoch();
1400
- const resolved = resolveEpochAgainstBundled(pinned, SEMA_DEFAULT_PACK, epochDeclaredSections);
1401
- epochArtifactDigestForSnapshot = resolved.descriptor.artifactDigest;
1402
- if (resolved.migrated) {
1403
- const priorConversation = pinned === undefined ? hasConversationContent(await session.getBranch()) : true;
1404
- const activatedBy = pinned === undefined && !priorConversation ? "session_start" : "legacy_migration";
1405
- await session.appendPromptEpoch({
1406
- ...resolved.descriptor,
1407
- activatedBy,
1408
- ...(centerAdoption !== undefined
1409
- ? {
1410
- centerArtifactDigest: centerAdoption.artifact.artifactDigest,
1411
- ...(centerAdoption.sourceRevision !== undefined ? { sourceRevision: centerAdoption.sourceRevision } : {}),
1412
- }
1413
- : {}),
1414
- });
1415
- }
1416
- }
559
+ prepareMemoryEngineSession({ memoryEngineSession, internals, memoryDelegationEvidence, externalContentTargetActive, lockedPreflight, mcp, sessionId, runId, deps, provenanceForChildrenRef, delegationSettlementRef, spec, envHandToolNames, tools, contentOriginWrapRef });
560
+ const { systemPromptSeat, renderWithDate, systemBlocks, promptManifest, epochDeclaredSections, epochArtifactDigestForSnapshot, buildAssembleInputs, centerAdoptionRef } = await preparePromptAssembly({ session, deps, internals, resume, provider, userSystemPrompt, userAppendSystemPrompt, featureFlags, mcpInstructionsBlock, memoryBlock, envFacts, promptProfile, fableMitigations, tools, model, sessionId, reminderMark, onceLedger });
1417
561
  const { activeTools, fpRef, turnSnapshotRef, harnessTools, toolsDeltaRef, toolMaterializeStatic, deferDirectCall, staticFaceForRef, listingRideRef } = await prepareToolDisclosureMount({ spec, deps, session, resume, deferred, tools, onceLedger, mcp, promptManifest, offloadReachableToolsRef, contentOriginWrapRef, rebuildHarnessToolsRef, harnessRef });
1418
562
  const { agentListing, announcedListingsRef, dateChange } = prepareListings({ spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingRideRef, renderWithDate, envFacts, tzValid, userTz });
1419
563
  const lastBrainContextRef = {};
@@ -1474,8 +618,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1474
618
  tools: harnessTools,
1475
619
  model,
1476
620
  thinkingLevel: thinking ?? "off",
1477
- systemPrompt,
1478
- ...(assembled.systemBlocks ? { systemBlocks: assembled.systemBlocks } : {}),
621
+ systemPrompt: systemPromptSeat.current,
622
+ ...(systemBlocks ? { systemBlocks } : {}),
1479
623
  getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
1480
624
  runtime: brainToRuntime({
1481
625
  ...guardedBrain,
@@ -1508,1864 +652,23 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1508
652
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
1509
653
  }
1510
654
  }
1511
- const policy = lockedPreflight.toolPolicy;
1512
- const auditPolicyNames = (auditee) => {
1513
- const nameGroups = toolPolicyNameSets(auditee);
1514
- if (nameGroups.length > 0) {
1515
- const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
1516
- for (const t of harnessTools)
1517
- known.add(t.name);
1518
- const unmatched = new Set();
1519
- for (const g of nameGroups) {
1520
- for (const [kind, list] of [["deny", g.deny], ["ask", g.ask], ["allow", g.allow]]) {
1521
- for (const n of list ?? []) {
1522
- if (known.has(n))
1523
- continue;
1524
- if (isNamespacedCoveringRuleName(n) && [...known].some((k) => namespacedRuleNameCovers(n, k)))
1525
- continue;
1526
- const retired = RETIRED_TOOL_NAMES.get(n);
1527
- if (retired !== undefined) {
1528
- const err = new Error(`tool policy ${kind}-list entry "${n}" is a RETIRED tool name (${retired}) and matches nothing in this run's roster — legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Update the deployment's rule to the current name.`);
1529
- err.code = "config.legacy_tool_name";
1530
- throw err;
1531
- }
1532
- if (n.includes("__") && protocolOf(n) === undefined) {
1533
- const err = new Error(`tool policy ${kind}-list entry "${n}" carries the "__" namespace separator but no protocol prefix, and matches nothing in this run's roster — protocol tools are named ${NAMESPACED_NAME_SHAPES}, and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with the owning protocol's marker.`);
1534
- err.code = "config.legacy_tool_name";
1535
- throw err;
1536
- }
1537
- unmatched.add(`"${n}" (${kind})`);
1538
- }
1539
- }
1540
- }
1541
- const dedupeKey = [...unmatched].sort().join("|");
1542
- const advised = advisedPolicyNames.get(deps) ?? new Set();
1543
- if (unmatched.size > 0 && !advised.has(dedupeKey)) {
1544
- advised.add(dedupeKey);
1545
- if (advised.size > ADVISED_KEYS_CAP) {
1546
- const oldest = advised.values().next().value;
1547
- if (oldest !== undefined)
1548
- advised.delete(oldest);
1549
- }
1550
- advisedPolicyNames.set(deps, advised);
1551
- try {
1552
- const e = new Error(`toolPolicy name audit: ${[...unmatched].join(", ")} match(es) no tool mounted this run (primary or alias) — the rule cannot fire here. Often a typo or a self-report/wire name skew (e.g. TodoWrite vs the mounted TaskCreate family; "*" has no wildcard meaning in a name-keyed policy). Advisory only: enforcement is unchanged, and a name can be legitimate for another scenario or a per-spawn tool.`);
1553
- e.code = "config.toolpolicy.unmatched_names";
1554
- deps.onError?.(e, { phase: "config", sessionId });
1555
- }
1556
- catch {
1557
- }
1558
- }
1559
- }
1560
- };
1561
- auditPolicyNames(policy);
1562
- let sessionRulePolicy;
1563
- if (deps.sessionPolicyStore && spec.sessionId) {
1564
- const rules = await deps.sessionPolicyStore.getRules(spec.sessionId, spec.principal);
1565
- if (rules) {
1566
- sessionRulePolicy = createSessionRulePolicy(rules, { env: executionEnv, rootPath: taskRootFinal, toolEffects });
1567
- ownSessionRulesRef.current = {
1568
- sessionId: spec.sessionId,
1569
- ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
1570
- rev: rules.rev,
1571
- rules,
1572
- };
1573
- }
1574
- }
1575
- const ancestorRulePolicies = [];
1576
- for (const snap of inheritedAncestorRules ?? []) {
1577
- let ancestorRules = snap.rules;
1578
- if (deps.sessionPolicyStore) {
1579
- try {
1580
- const live = await deps.sessionPolicyStore.getRules(snap.sessionId, snap.principal);
1581
- if (live !== null && live.rev >= snap.rev)
1582
- ancestorRules = live;
1583
- }
1584
- catch {
1585
- }
1586
- }
1587
- ancestorRulePolicies.push(createSessionRulePolicy(ancestorRules, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
1588
- }
1589
- const skillScopePolicy = hasSkillManifest
1590
- ? createActiveSkillScopePolicy({
1591
- scope: skillScope,
1592
- env: executionEnv,
1593
- rootPath: taskRootFinal,
1594
- toolEffects,
1595
- })
1596
- : undefined;
1597
- const frozenConstraintLayers = (seedInheritedGate?.constraintChain ?? [])
1598
- .filter((e) => !("opaque" in e))
1599
- .map((entry) => ({
1600
- check: (creq) => checkToolPolicyProjection(entry, creq) ?? { action: "allow" },
1601
- }));
1602
- const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, ...frozenConstraintLayers, skillScopePolicy].filter((p) => p !== undefined);
1603
- for (const layer of narrowingLayers)
1604
- auditPolicyNames(layer);
1605
- const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
1606
- const basePolicyForResumeEdit = lockedPreflight.basePolicyForResumeEdit ?? lockedPreflight.toolPolicy;
1607
- const hooks = spec.hooks ?? deps.hooks;
1608
- const hookTimeoutMs = resolveHookTimeoutMs(hooks?.timeoutMs, notifyOwnHookCrash, hooks);
1609
- const preToolUseObservational = hooks?.preToolUse !== undefined && hooks.preToolUseObservational === true;
1610
- const ownGatePreToolUse = !preToolUseObservational
1611
- ? hooks?.preToolUse
1612
- : async (toolName, input, ctx) => {
1613
- const r = await hooks.preToolUse(toolName, cloneObserverInput(input), ctx);
1614
- if (r === undefined)
1615
- return undefined;
1616
- try {
1617
- const action = typeof r.action === "string" ? r.action : "(non-string action)";
1618
- deps.onError?.(new Error(`a PreToolUse hook declared observational (Hooks.preToolUseObservational) returned a decision ("${action}") while screening ` +
1619
- `"${toolName}" — the decision was NOT adopted and the call proceeded as if the hook had no opinion. ` +
1620
- `A declared-observational face must return undefined; drop the declaration if its verdicts are meant to count ` +
1621
- `(they then also travel to delegated children as an inherited screening constraint).`), { phase: "hook", sessionId, classification: "observational-hook-verdict-ignored" });
1622
- }
1623
- catch {
1624
- }
1625
- return undefined;
1626
- };
1627
- const sameInstanceAncestorCount = policy === undefined ? 0 : (inheritedParentConstraints ?? []).reduce((n, pc) => (pc.policy === policy ? n + 1 : n), 0);
1628
- const sharedFirstDecision = new Map();
1629
- const SHARED_FIRST_DECISION_CAP = 256;
1630
- const inheritedUnavailableAsks = new Set();
1631
- const INHERITED_UNAVAILABLE_CAP = 256;
1632
- const markInheritedUnavailable = (toolCallId) => {
1633
- if (inheritedUnavailableAsks.size >= INHERITED_UNAVAILABLE_CAP && !inheritedUnavailableAsks.has(toolCallId))
1634
- return false;
1635
- inheritedUnavailableAsks.add(toolCallId);
1636
- return true;
1637
- };
1638
- const inheritedAskGrants = new Map();
1639
- const recordInheritedAskGrant = (toolCallId, approver, presented, waitMs) => {
1640
- if (typeof approver !== "function")
1641
- return;
1642
- if (presented === undefined)
1643
- return;
1644
- if (inheritedAskGrants.size >= INHERITED_UNAVAILABLE_CAP && !inheritedAskGrants.has(toolCallId))
1645
- return;
1646
- const argsJson = askGrantShapeOf(presented);
1647
- if (argsJson === undefined)
1648
- return;
1649
- inheritedAskGrants.set(toolCallId, { approver, argsJson, presented, waitMs });
1650
- };
1651
- const sandboxDeclared = isIsolated(executionEnv);
1652
- const sandboxAdmissionArmed = (() => {
1653
- if (!sandboxDeclared)
1654
- return false;
1655
- const store = resolveCheckpointStore(spec, deps);
1656
- const missing = [];
1657
- if (store === undefined)
1658
- missing.push("a wired CheckpointStore");
1659
- else if (store.redecision?.reopen !== true)
1660
- missing.push("a CheckpointStore that DECLARES redecision.reopen (declaration, never duck-typing)");
1661
- if (spec.durableApproval === undefined && runtimeCaps?.forceDurableGate !== true)
1662
- missing.push("a durable park (spec.durableApproval, or the forceDurableGate entitlement)");
1663
- if (missing.length === 0)
1664
- return true;
1665
- try {
1666
- const e = new Error(`sandbox admission mode NOT armed: the execution env declares an isolated sandbox (capabilities.isolation), but the mode's ` +
1667
- `park prerequisites are missing — ${missing.join("; ")}. Asks keep the conservative default chain (no auto-admission, no boundary park).`);
1668
- e.code = "config.sandbox_admission_unavailable";
1669
- deps.onError?.(e, { phase: "config", sessionId, classification: "sandbox-admission-unavailable" });
1670
- }
1671
- catch {
1672
- }
1673
- return false;
1674
- })();
1675
- const foldAskClasses = new Map();
1676
- const FOLD_ASK_CLASSES_CAP = 256;
1677
- const recordFoldAskClass = (toolCallId, cls, layer) => {
1678
- if (!foldAskClasses.has(toolCallId) && foldAskClasses.size >= FOLD_ASK_CLASSES_CAP)
1679
- return;
1680
- const list = foldAskClasses.get(toolCallId) ?? [];
1681
- list.push({ cls, layer });
1682
- foldAskClasses.set(toolCallId, list);
1683
- };
1684
- const sandboxBoundaryCapable = (toolName) => egressTools.has(toolName) || toolName.includes("__") || ownToolNames.has(toolName) || toolName === SEND_MESSAGE_TOOL_NAME;
1685
- const emitSandboxAdmitted = (info) => {
1686
- emitTrace(deps.tracer, () => ({
1687
- kind: "permission.sandbox_admitted",
1688
- version: 1,
1689
- taskId: hostTaskId,
1690
- toolCallId: info.toolCallId,
1691
- toolName: info.toolName,
1692
- askClasses: info.askClasses.map((a) => a.cls),
1693
- sourceLayers: info.askClasses.map((a) => a.layer),
1694
- boundary: info.boundary,
1695
- ts: Date.now(),
1696
- }));
1697
- };
1698
- const ancestorSandboxAdmissions = new Map();
1699
- const recordAncestorSandboxAdmission = (toolCallId, toolName) => {
1700
- const list = ancestorSandboxAdmissions.get(toolCallId);
1701
- if (list !== undefined)
1702
- list.push({ cls: "sandbox_local", layer: "ancestor_constraint" });
1703
- else
1704
- ancestorSandboxAdmissions.set(toolCallId, [{ cls: "sandbox_local", layer: "ancestor_constraint" }]);
1705
- void toolName;
1706
- };
1707
- const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
1708
- ? {
1709
- check: async (creq, csignal) => {
1710
- const decision = await policy.check(creq, csignal ?? abortController.signal);
1711
- sharedFirstDecision.set(creq.toolCallId, decision);
1712
- if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
1713
- const oldest = sharedFirstDecision.keys().next().value;
1714
- if (oldest !== undefined)
1715
- sharedFirstDecision.delete(oldest);
1716
- }
1717
- return decision;
1718
- },
1719
- }
1720
- : policy;
1721
- const askSourceIdentity = () => ({
1722
- principal: spec.principal,
1723
- sourceTaskId: sessionId,
1724
- ...(internals?.parentTaskId !== undefined ? { fromSubagent: true } : {}),
1725
- ...((internals?.explicitAgentName ?? internals?.agentName) !== undefined
1726
- ? { sourceAgentName: internals?.explicitAgentName ?? internals?.agentName }
1727
- : {}),
1728
- ...(delegation.isDelegatedChild ? { isDelegatedChild: true } : {}),
1729
- });
1730
- const riskAxesOf = (toolName) => {
1731
- const tier = irreversibilityTier.get(toolName);
1732
- const negatives = axisExplicitNegatives.get(toolName);
1733
- const irreversible = tier === "always" || tier === "maybe" ? true : tier === "never" ? false : negatives?.irreversible;
1734
- const egress = egressTools.has(toolName) ? true : negatives?.egress;
1735
- if (irreversible === undefined && egress === undefined)
1736
- return {};
1737
- return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
1738
- };
1739
- const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
1740
- const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
1741
- provider: deps.permissionRuleStore,
1742
- localOwnerDeclared: deps.localOwnerRules === true,
1743
- principal: spec.principal,
1744
- sessionId,
1745
- root: taskRootFinal,
1746
- liveCwd: () => handsCwdRef?.current,
1747
- questionToolMounted,
1748
- questionToolName: ASK_USER_QUESTION_TOOL_NAME,
1749
- onRevisionDefect: (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }),
1750
- onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
1751
- });
1752
- const ruleOffersOf = makeRuleOffersOf({
1753
- laneArmed: permissionRuleLane !== undefined,
1754
- principal: spec.principal,
1755
- localOwnerDeclared: deps.localOwnerRules === true,
1756
- egressTools,
1757
- irreversibilityTier,
1758
- shellGatedBash,
1759
- taskRoot: taskRootFinal,
1760
- cwdRef: handsCwdRef,
1761
- deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
1762
- });
1763
- const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal, ancestorDecider, ancestorTracker) => {
1764
- let editArgs = edit;
1765
- for (let round = 0;; round++) {
1766
- if (round >= 3) {
1767
- return {
1768
- action: "deny",
1769
- message: `the approval-edit chain for "${creq.toolName}" exceeded the re-approval cap (inherited parent policy) — denied fail-closed; re-submit the edited action directly`,
1770
- };
1771
- }
1772
- let re;
1773
- try {
1774
- re = await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal);
1775
- }
1776
- catch (err) {
1777
- return {
1778
- action: "deny",
1779
- message: `inherited parent policy re-check of the approved edit errored for "${creq.toolName}": ${err instanceof Error ? err.message : String(err)}`,
1780
- };
1781
- }
1782
- if (re.action === "deny")
1783
- return re;
1784
- if (re.action === "allow") {
1785
- return { action: "allow", updatedInput: re.updatedInput !== undefined ? re.updatedInput : editArgs };
1786
- }
1787
- if (re.updatedInput !== undefined)
1788
- editArgs = re.updatedInput;
1789
- const judged = await judgeInheritedClassifier({ autoMode: ancestorDecider !== undefined ? { decider: ancestorDecider, denialTracking: ancestorTracker } : undefined, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit" });
1790
- if (judged.kind === "allow")
1791
- return { action: "allow", updatedInput: editArgs };
1792
- if (judged.kind === "deny")
1793
- return judged.result;
1794
- const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere, origin: editOrigin } = judged;
1795
- re = editAsk;
1796
- const rr = await resolveAsk({
1797
- toolName: creq.toolName,
1798
- toolCallId: creq.toolCallId,
1799
- args: editArgs,
1800
- ...ruleOffersOf(creq.toolName, editArgs, { ...(re ?? {}), ancestorResolved: true }),
1801
- message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
1802
- ...askSourceIdentity(),
1803
- ...riskAxesOf(creq.toolName),
1804
- ...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1805
- ...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
1806
- ...inheritedAskCarry(editOrigin, editFallback, isLiveApproverSeat(onAskOf), ancestorTracker),
1807
- ruleEvidence: inheritedAskEvidence,
1808
- }, onAskOf, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
1809
- settleDenialLimitFallback({ fallback: editFallback, mintedHere: editMintedHere, tracker: ancestorTracker, resolved: rr, headless: headlessDenyAtRecheck, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
1810
- if (rr.action !== "allow")
1811
- return rr;
1812
- if (rr.updatedInput === undefined)
1813
- return { action: "allow", updatedInput: editArgs };
1814
- editArgs = rr.updatedInput;
1815
- }
1816
- };
1817
- const parentConstraintWrappers = (inheritedParentConstraints ?? []).map((pc) => pc.preToolUse !== undefined &&
1818
- pc.preToolUse === hooks?.preToolUse &&
1819
- !preToolUseObservational &&
1820
- pc.durableMandate !== true &&
1821
- pc.contentMandate !== true &&
1822
- askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
1823
- pc.hookEnv === hookEnvSource &&
1824
- pc.autoMode?.decider === autoModeDecider
1825
- ? { check: () => ({ action: "allow" }) }
1826
- : policy !== undefined && pc.policy === policy
1827
- ? {
1828
- check: async (creq, csignal) => {
1829
- const first = sharedFirstDecision.get(creq.toolCallId);
1830
- if (first === undefined) {
1831
- return {
1832
- action: "deny",
1833
- message: `inherited parent policy could not be arbitrated for "${creq.toolName}" ` +
1834
- `(shared-instance first decision unavailable); denied fail-closed`,
1835
- };
1836
- }
1837
- if (first.action === "deny")
1838
- return first;
1839
- if (first.action === "allow")
1840
- return { action: "allow" };
1841
- if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
1842
- if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
1843
- return first;
1844
- return {
1845
- action: "deny",
1846
- message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
1847
- `denied fail-closed (tighten-only)`,
1848
- };
1849
- }
1850
- if (pc.durableMandate === true) {
1851
- if (resolveCheckpointStore(spec, deps) !== undefined &&
1852
- (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
1853
- markInheritedUnavailable(creq.toolCallId)) {
1854
- return first;
1855
- }
1856
- return {
1857
- action: "deny",
1858
- message: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
1859
- `ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
1860
- };
1861
- }
1862
- const judged = await judgeInheritedClassifier({ autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call" });
1863
- if (judged.kind === "allow")
1864
- return { action: "allow" };
1865
- if (judged.kind === "deny")
1866
- return judged.result;
1867
- const { ask: inheritedAsk, fallback, mintedHere, origin: inheritedOrigin } = judged;
1868
- if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
1869
- recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
1870
- return { action: "allow" };
1871
- }
1872
- const presentedArgs = creq.args;
1873
- const askT0 = now();
1874
- const resolved = await resolveAsk({
1875
- toolName: creq.toolName,
1876
- toolCallId: creq.toolCallId,
1877
- args: presentedArgs,
1878
- ...ruleOffersOf(creq.toolName, presentedArgs, { ...(inheritedAsk.action === "ask" ? inheritedAsk : {}), ancestorResolved: true }),
1879
- message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
1880
- ...askSourceIdentity(),
1881
- ...riskAxesOf(creq.toolName),
1882
- ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1883
- ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
1884
- ...inheritedAskCarry(inheritedOrigin, fallback, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
1885
- ruleEvidence: inheritedAskEvidence,
1886
- }, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
1887
- const askWaitMs = Math.max(0, now() - askT0);
1888
- if (resolved.action === "deny" && resolved.approverUnavailable === true) {
1889
- if (markInheritedUnavailable(creq.toolCallId))
1890
- return inheritedAsk;
1891
- return {
1892
- action: "deny",
1893
- message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed`,
1894
- };
1895
- }
1896
- settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
1897
- if (resolved.action !== "allow")
1898
- return resolved;
1899
- if (resolved.updatedInput !== undefined) {
1900
- return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal, pc.autoMode?.decider, pc.autoMode?.denialTracking);
1901
- }
1902
- recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs);
1903
- return { action: "allow" };
1904
- },
1905
- }
1906
- : {
1907
- check: async (creq, csignal) => {
1908
- let decision;
1909
- try {
1910
- decision = await pc.policy.check(creq, csignal ?? abortController.signal);
1911
- }
1912
- catch (err) {
1913
- return {
1914
- action: "deny",
1915
- message: `inherited parent policy errored for "${creq.toolName}": ${err instanceof Error ? err.message : String(err)}`,
1916
- };
1917
- }
1918
- if (decision.action !== "ask")
1919
- return decision;
1920
- if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME) {
1921
- if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
1922
- return decision;
1923
- return {
1924
- action: "deny",
1925
- message: `inherited parent policy requires durable handling of the question raised for "${creq.toolName}" and the marker channel is at capacity — ` +
1926
- `denied fail-closed (tighten-only)`,
1927
- };
1928
- }
1929
- if (pc.durableMandate === true) {
1930
- if (resolveCheckpointStore(spec, deps) !== undefined &&
1931
- (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
1932
- markInheritedUnavailable(creq.toolCallId)) {
1933
- return decision;
1934
- }
1935
- return {
1936
- action: "deny",
1937
- message: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
1938
- `ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
1939
- };
1940
- }
1941
- const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
1942
- const judged = await judgeInheritedClassifier({ autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call" });
1943
- if (judged.kind === "allow")
1944
- return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
1945
- if (judged.kind === "deny")
1946
- return judged.result;
1947
- const { ask: inheritedAsk, fallback, mintedHere, origin: inheritedOrigin } = judged;
1948
- if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
1949
- recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
1950
- return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
1951
- }
1952
- const askT0 = now();
1953
- const resolved = await resolveAsk({
1954
- toolName: creq.toolName,
1955
- toolCallId: creq.toolCallId,
1956
- args: presentedArgs,
1957
- ...ruleOffersOf(creq.toolName, presentedArgs, { ...(inheritedAsk.action === "ask" ? inheritedAsk : {}), ancestorResolved: true }),
1958
- message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
1959
- ...askSourceIdentity(),
1960
- ...riskAxesOf(creq.toolName),
1961
- ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1962
- ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
1963
- ...inheritedAskCarry(inheritedOrigin, fallback, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
1964
- ruleEvidence: inheritedAskEvidence,
1965
- }, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
1966
- const askWaitMs = Math.max(0, now() - askT0);
1967
- if (resolved.action === "deny" && resolved.approverUnavailable === true) {
1968
- if (markInheritedUnavailable(creq.toolCallId))
1969
- return inheritedAsk;
1970
- return {
1971
- action: "deny",
1972
- message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed`,
1973
- };
1974
- }
1975
- settleDenialLimitFallback({ fallback, mintedHere, tracker: pc.autoMode?.denialTracking, resolved, headless: headlessDenyAtFold, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
1976
- if (resolved.action !== "allow")
1977
- return resolved;
1978
- if (resolved.updatedInput !== undefined) {
1979
- return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, csignal, pc.autoMode?.decider, pc.autoMode?.denialTracking);
1980
- }
1981
- if (pc.preToolUse === undefined) {
1982
- recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs);
1983
- }
1984
- return decision.updatedInput !== undefined ? { ...resolved, updatedInput: decision.updatedInput } : resolved;
1985
- },
1986
- });
1987
- const rewriteCapableLayers = policy !== undefined || parentConstraintWrappers.length > 0;
1988
- const rewriteEmitterIndex = new Map();
1989
- const rewriteCapableChain = [
1990
- ...(callerPolicyLayer !== undefined ? [callerPolicyLayer] : []),
1991
- ...parentConstraintWrappers,
1992
- ];
1993
- const callerSlotOffset = callerPolicyLayer !== undefined ? 1 : 0;
1994
- const sameInstanceAdapterIndices = new Set();
1995
- (inheritedParentConstraints ?? []).forEach((pc, j) => {
1996
- if (policy !== undefined && pc.policy === policy)
1997
- sameInstanceAdapterIndices.add(j + callerSlotOffset);
1998
- });
1999
- const instrumentedChain = rewriteCapableChain.map((layer, idx) => ({
2000
- check: async (creq, csignal) => {
2001
- const d = await layer.check(creq, csignal);
2002
- if (d.updatedInput !== undefined)
2003
- rewriteEmitterIndex.set(creq.toolCallId, idx);
2004
- if (d.action === "ask") {
2005
- recordFoldAskClass(creq.toolCallId, idx < callerSlotOffset ? policyAskClassOf(policy) : "mandate", idx < callerSlotOffset ? "caller_policy" : "ancestor_constraint");
2006
- }
2007
- return d;
2008
- },
2009
- }));
2010
- const policyLayers = [
2011
- ...narrowingLayers,
2012
- ...instrumentedChain,
2013
- ...(rewriteCapableLayers ? narrowingLayers : []),
2014
- ];
2015
- const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy()];
2016
- const guardedPolicyLayers = rewriteCapableLayers
2017
- ? [...integrityGuardLayers, ...policyLayers, ...integrityGuardLayers]
2018
- : [...integrityGuardLayers, ...policyLayers];
2019
- const foldedPolicy = guardedPolicyLayers.length === 1 ? guardedPolicyLayers[0] : combinePolicies(...guardedPolicyLayers);
2020
- const effectivePolicy = foldedPolicy !== undefined && rewriteCapableLayers
2021
- ? {
2022
- check: async (req, signal) => {
2023
- let decision;
2024
- try {
2025
- decision = await foldedPolicy.check(req, signal);
2026
- }
2027
- catch (err) {
2028
- rewriteEmitterIndex.delete(req.toolCallId);
2029
- throw err;
2030
- }
2031
- const emitterIdx = rewriteEmitterIndex.get(req.toolCallId);
2032
- rewriteEmitterIndex.delete(req.toolCallId);
2033
- if (decision.action === "deny" || decision.updatedInput === undefined)
2034
- return decision;
2035
- const recheckPrefix = rewriteCapableChain.slice(0, emitterIdx ?? rewriteCapableChain.length);
2036
- const postEmitterSameInstanceAdapters = [];
2037
- if (emitterIdx !== undefined && emitterIdx > 0) {
2038
- for (let i = emitterIdx + 1; i < rewriteCapableChain.length; i++) {
2039
- if (sameInstanceAdapterIndices.has(i))
2040
- postEmitterSameInstanceAdapters.push(rewriteCapableChain[i]);
2041
- }
2042
- }
2043
- const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
2044
- const finalReq = { ...req, args: decision.updatedInput };
2045
- let asked;
2046
- try {
2047
- for (const layer of recheckLayers) {
2048
- const d = await layer.check(finalReq, signal);
2049
- if (d.action === "deny") {
2050
- return { ...d, updatedInput: decision.updatedInput };
2051
- }
2052
- if (d.updatedInput !== undefined) {
2053
- return {
2054
- action: "deny",
2055
- message: "policy required a rewrite during final-form re-check — the arguments that would execute were not accepted as-is (fail-closed; rewrite chains are not iterated to a fixpoint)",
2056
- updatedInput: decision.updatedInput,
2057
- };
2058
- }
2059
- if (d.action === "ask" && asked === undefined)
2060
- asked = d;
2061
- }
2062
- }
2063
- finally {
2064
- rewriteEmitterIndex.delete(req.toolCallId);
2065
- }
2066
- if (asked !== undefined && decision.action === "allow") {
2067
- return { ...asked, updatedInput: decision.updatedInput };
2068
- }
2069
- return decision;
2070
- },
2071
- }
2072
- : foldedPolicy;
2073
- const { onAsk, preToolContexts, blockedToolCalls, approvalSettlement, humanBareRejections, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, parkLaneArmed, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, handsEnabled, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, suspendRef, reviewRef, frozenOnAsk, liveInheritedGate, delegation });
655
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, ownToolNames, shellGatedBash, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
656
+ const { onAsk, preToolContexts, blockedToolCalls, approvalSettlement, humanBareRejections, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, suspendRef, reviewRef, frozenOnAsk, liveInheritedGate, delegation });
2074
657
  if (fileHistoryBoundary !== undefined) {
2075
658
  harness.on("tool_call", async () => {
2076
659
  await fileHistoryBoundary.settle();
2077
660
  return undefined;
2078
661
  });
2079
662
  }
2080
- if (effectivePolicy || hooks?.preToolUse || egressTools.size > 0 || irreversibleTools.size > 0 || resourceSuspendEligible || platformSuspendArmed || spec.enablePlanMode === true) {
2081
- const composedCallSignal = (callSignal) => composeCallSignal(abortController.signal, callSignal);
2082
- const adjudicate = effectivePolicy
2083
- ? (req, callSignal) => {
2084
- const signal = composedCallSignal(callSignal);
2085
- return raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}) }, signal)), signal, () => ({
2086
- action: "deny",
2087
- message: "policy check aborted (task timed out or cancelled)",
2088
- }));
2089
- }
2090
- : undefined;
2091
- const approvalPreviewOf = (toolName, args) => resolveApprovalPreview(tools, toolName, args);
2092
- const resolveAskBound = async (decision, req, callSignal) => {
2093
- if (inheritedUnavailableAsks.delete(req.toolCallId)) {
2094
- return {
2095
- action: "deny",
2096
- message: `approval for "${req.toolName}" requires an ancestor task's approval that cannot be resolved here ` +
2097
- `(no live approver reachable, or a durable-park mandate applies), and no durable approval park is ` +
2098
- `available — denied fail-closed (the inherited constraint stands).`,
2099
- decisionReason: "mode",
2100
- approverUnavailable: true,
2101
- };
2102
- }
2103
- const grantReuse = consumeInheritedAskGrant(inheritedAskGrants, onAsk, humanReviewRef, decision, req);
2104
- if (grantReuse !== undefined)
2105
- return grantReuse;
2106
- const t0 = now();
2107
- const resolved = await resolveAsk({
2108
- toolName: req.toolName,
2109
- toolCallId: req.toolCallId,
2110
- args: req.args,
2111
- ...(() => {
2112
- const p = approvalPreviewOf(req.toolName, req.args);
2113
- return { ...(p.preview !== undefined ? { preview: p.preview } : {}), ...(p.withheld !== undefined ? { previewWithheld: p.withheld } : {}) };
2114
- })(),
2115
- ...ruleOffersOf(req.toolName, req.args, decision.action === "ask" ? decision : undefined),
2116
- message: decision.message ?? `approval required for "${req.toolName}"`,
2117
- ...askSourceIdentity(),
2118
- ...riskAxesOf(req.toolName),
2119
- ...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
2120
- ...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
2121
- ...(decision.action === "ask" ? gateAskCarry(decision, isLiveApproverSeat(onAsk), autoModeDenialTracking) : {}),
2122
- ...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
2123
- ...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
2124
- ...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
2125
- }, onAsk, composedCallSignal(callSignal), lateAskSettlementObserver({ toolName: req.toolName, toolCallId: req.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
2126
- const waitMs = Math.max(0, now() - t0);
2127
- if (resolved.approverUnavailable !== true && resolved.resolution !== "task_aborted") {
2128
- humanReviewRef.count += 1;
2129
- humanReviewRef.totalWaitMs += waitMs;
2130
- const toolArg = primaryActivityArg(req.args);
2131
- humanReviewRef.gates.push({
2132
- kind: "human",
2133
- waitMs,
2134
- decision: resolved.action === "allow" ? "allow" : "deny",
2135
- toolName: req.toolName,
2136
- ...(toolArg !== undefined ? { toolArg } : {}),
2137
- });
2138
- }
2139
- if (resolved.action === "deny" && resolved.settledBy === "human" && resolved.humanRefusalNote !== true) {
2140
- humanBareRejections.add(req.toolCallId);
2141
- }
2142
- return resolved;
2143
- };
2144
- const notifyPermissionDenied = createDenyObserverNotifier(hooks, hookTimeoutMs, abortController.signal, notifyOwnHookCrash);
2145
- const notifyHookError = (err) => {
2146
- try {
2147
- deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
2148
- }
2149
- catch {
2150
- }
2151
- };
2152
- const inFlightSpendMicroUsd = () => {
2153
- const own = liveSpendRef.get?.().costMicroUsd ?? 0;
2154
- const seededNested = resume?.seed.nestedStats.costMicroUsd;
2155
- const nestedDelta = nestedStats.costMicroUsd - (typeof seededNested === "number" && Number.isFinite(seededNested) ? seededNested : 0);
2156
- return (Number.isFinite(own) ? own : 0) + Math.max(0, Number.isFinite(nestedDelta) ? nestedDelta : 0);
2157
- };
2158
- const repairBundleForCheckpoint = (parkedSpendMicroUsd) => {
2159
- const carried = internals?.repairBundle !== undefined
2160
- ? structuredClone(internals.repairBundle)
2161
- : resume?.seed.repairBundle !== undefined
2162
- ? structuredClone(resume.seed.repairBundle)
2163
- : undefined;
2164
- if (carried === undefined)
2165
- return undefined;
2166
- if (parkedSpendMicroUsd === undefined || !Number.isFinite(parkedSpendMicroUsd) || parkedSpendMicroUsd <= 0)
2167
- return carried;
2168
- const prior = typeof carried.spentMicroUsd === "number" && Number.isFinite(carried.spentMicroUsd) ? Math.max(0, carried.spentMicroUsd) : 0;
2169
- carried.spentMicroUsd = prior + parkedSpendMicroUsd;
2170
- return carried;
2171
- };
2172
- const screeningParkDisclosedRef = { done: false };
2173
- const serializeCheckpointState = (workspaceHandle, parkedSpendMicroUsd) => ({
2174
- activeTools: [...activeTools],
2175
- outputRef: { value: outputRef.value, set: outputRef.set },
2176
- nestedStats: { ...nestedStats },
2177
- consolidationNotes: undefined,
2178
- readFileState: readFileStateForCheckpoint ? [...readFileStateForCheckpoint.entries()] : undefined,
2179
- readFace: faceCheckpointSection(),
2180
- reminderMark,
2181
- repairBundle: repairBundleForCheckpoint(parkedSpendMicroUsd),
2182
- workspaceHandle,
2183
- handsCwd: handsCwdRef?.current,
2184
- activeWorktree: worktreeSessionRef?.current ? { ...worktreeSessionRef.current } : undefined,
2185
- runningBackgroundTasks: (() => {
2186
- const live = defaultTaskRegistry
2187
- .list({ owner: hostTaskId, scope: taskScope, sessionId })
2188
- .filter((t) => t.status === "pending" || t.status === "running")
2189
- .map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}) }));
2190
- return live.length > 0 ? live : undefined;
2191
- })(),
2192
- pendingSteer: undefined,
2193
- pendingSteerQueue: undefined,
2194
- inheritedGate: (() => {
2195
- const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
2196
- const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
2197
- const constraintChain = (inheritedParentConstraints?.length ?? 0) > 0
2198
- ? inheritedParentConstraints.map((pc) => constraintChainEntryOfLayer(pc))
2199
- : seedInheritedGate?.constraintChain;
2200
- const constraintDigest = (inheritedParentConstraints?.length ?? 0) > 0
2201
- ? constraintChainDigest(constraintChain)
2202
- : seedInheritedGate?.constraintDigest;
2203
- const autoModeIntentCarried = autoModeIntent && (autoModeDecider === undefined || autoModeLatchHealthy(autoModeDecider));
2204
- return inheritedAncestorRules !== undefined ||
2205
- inheritedShellGate !== undefined ||
2206
- autoModeIntentCarried ||
2207
- inheritedAdmittedOrgScopes !== undefined ||
2208
- ownOrgVerdictRef.current !== undefined ||
2209
- orgGovernedProvenance ||
2210
- requiresParentConstraint
2211
- ? {
2212
- ...(inheritedAncestorRules !== undefined ? { ancestorRules: structuredClone(inheritedAncestorRules) } : {}),
2213
- ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
2214
- ...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
2215
- ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
2216
- ...(ownOrgVerdictRef.current !== undefined
2217
- ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
2218
- : {}),
2219
- ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
2220
- requiresParentConstraint,
2221
- ...(requiresParentConstraint && parentConstraintCount !== undefined ? { parentConstraintCount } : {}),
2222
- ...(requiresParentConstraint && constraintChain !== undefined
2223
- ? { constraintChain: structuredClone(constraintChain), ...(constraintDigest !== undefined ? { constraintDigest } : {}) }
2224
- : {}),
2225
- }
2226
- : undefined;
2227
- })(),
2228
- announcedListings: serializeAnnouncedListings(announcedListingsRef),
2229
- gitAnnouncement: gitStatusRef.announced !== undefined ? { ...gitStatusRef.announced } : undefined,
2230
- delegationProvenance: internals?.delegationProvenance !== undefined ? { ...internals.delegationProvenance.ref.current } : undefined,
2231
- isDelegatedChild: hookIdentity.isDelegatedChild ? true : undefined,
2232
- placementRootSessionId: stampPlacementRootSessionId(placementRootResolved),
2233
- externalContentTarget: externalContentTargetActive ? true : undefined,
2234
- });
2235
- const compensatePausedVM = (remoteEnv, snapshotId) => compensateUnparkedPause(remoteEnv, snapshotId, {
2236
- boundMs: PARK_COMPENSATION_TIMEOUT_MS,
2237
- noteFailure: (note) => remoteEnvFailures.push(note),
2238
- disclose: (err) => {
2239
- try {
2240
- deps.onError?.(err, { phase: "config", sessionId });
2241
- }
2242
- catch {
2243
- }
2244
- },
2245
- });
2246
- const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle, cutSignal) => {
2247
- if (!checkpointStore)
2248
- return { tag: "absent" };
2249
- if (memoryEngineSession)
2250
- await memoryEngineSession.harvest("checkpoint");
2251
- if (cutSignal?.aborted) {
2252
- if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
2253
- const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
2254
- if (!back.ok) {
2255
- abortController.abort();
2256
- void harness.abort();
2257
- return { tag: "compensation_failed", reason: back.reason };
2258
- }
2259
- }
2260
- return { tag: "cut" };
2261
- }
2262
- const announceCommittedScreeningPark = () => {
2263
- const committedCount = cp.state.inheritedGate?.parentConstraintCount;
2264
- if (cp.state.inheritedGate?.requiresParentConstraint === true &&
2265
- !screeningParkDisclosedRef.done &&
2266
- (inheritedParentConstraints ?? []).some((pc) => pc.preToolUse !== undefined)) {
2267
- screeningParkDisclosedRef.done = true;
2268
- try {
2269
- deps.onError?.(new Error(`durable park under an inherited PreToolUse SCREENING constraint: this checkpoint records ` +
2270
- `requiresParentConstraint with parentConstraintCount=${committedCount ?? "(unrecorded)"}, and the live ` +
2271
- `closures cannot be persisted. A resume on THIS Runner re-supplies them automatically; a resume on a fresh ` +
2272
- `Runner (restart / another replica) must hand back the whole chain via resumeStream(..., internals), ` +
2273
- `rebuilding the screening entry with createPreToolUseConstraintPolicy(hook, env) — otherwise the row stays ` +
2274
- `pending. If the face only observes, declare Hooks.preToolUseObservational and it stops entering the chain.`), { phase: "degraded", sessionId, classification: "screening-constraint-in-durable-chain" });
2275
- }
2276
- catch {
2277
- }
2278
- }
2279
- };
2280
- const confirmPutOutcome = async (putErr) => {
2281
- if (putErr?.code === "checkpoint.already_exists")
2282
- return "absent";
2283
- try {
2284
- const row = await checkpointStore.get(token);
2285
- if (row == null)
2286
- return "absent";
2287
- const sameCall = (a, b) => a.kind === b.kind && (a.kind !== "tool_approval" || b.kind !== "tool_approval" || a.toolCallId === b.toolCallId);
2288
- const isOurs = row.scope === cp.scope && row.sessionId === cp.sessionId && row.leafId === cp.leafId && sameCall(row.pendingAction, cp.pendingAction);
2289
- return isOurs ? "committed" : "absent";
2290
- }
2291
- catch (readErr) {
2292
- deps.onError?.(readErr, { phase: "config", sessionId });
2293
- return "unknown";
2294
- }
2295
- };
2296
- try {
2297
- await checkpointStore.put(token, cp);
2298
- announceCommittedScreeningPark();
2299
- return { tag: "committed" };
2300
- }
2301
- catch (putErr) {
2302
- deps.onError?.(putErr, { phase: "config", sessionId });
2303
- const confirmed = await confirmPutOutcome(putErr);
2304
- if (confirmed === "committed") {
2305
- try {
2306
- deps.onError?.(new Error(`durable approval checkpoint: the store's write reported a failure but the row EXISTS — the commit ` +
2307
- `landed and its acknowledgement was lost (a network backend can complete the INSERT and drop the ` +
2308
- `connection before replying). The run suspends on that row rather than continuing, which would leave ` +
2309
- `a redeemable pending checkpoint behind. This backend's acknowledgement path is lossy — the operator ` +
2310
- `face carries the store's own message.`), { phase: "degraded", sessionId, classification: "checkpoint-put-confirmation-lost" });
2311
- }
2312
- catch {
2313
- }
2314
- announceCommittedScreeningPark();
2315
- return { tag: "committed" };
2316
- }
2317
- if (confirmed === "unknown") {
2318
- 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";
2319
- try {
2320
- deps.onError?.(new Error(`durable approval checkpoint: ${unknownReason}`), {
2321
- phase: "degraded",
2322
- sessionId,
2323
- classification: "checkpoint-put-outcome-unknown",
2324
- });
2325
- }
2326
- catch {
2327
- }
2328
- abortController.abort();
2329
- void harness.abort();
2330
- return { tag: "unknown", reason: unknownReason };
2331
- }
2332
- 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)";
2333
- if (remoteEnv !== undefined && remoteHandle?.snapshotId !== undefined) {
2334
- const back = await compensatePausedVM(remoteEnv, remoteHandle.snapshotId);
2335
- if (back.ok)
2336
- return { tag: "absent", reason };
2337
- abortController.abort();
2338
- void harness.abort();
2339
- return { tag: "compensation_failed", reason };
2340
- }
2341
- return { tag: "absent", reason };
2342
- }
2343
- };
2344
- const suspendLoopCapHit = (count, cap, detail) => {
2345
- if (cap === undefined)
2346
- return false;
2347
- if (count + 1 <= cap)
2348
- return false;
2349
- suspendLoopRef.hit = true;
2350
- abortController.abort();
2351
- void harness.abort();
2352
- try {
2353
- deps.onError?.(new Error(`suspend loop: task already suspended ${count} time(s) (max ${cap}); refusing to suspend again${detail}`), { phase: "config", sessionId });
2354
- }
2355
- catch {
2356
- }
2357
- return true;
2358
- };
2359
- const suspendableEnv = ownedEnv !== undefined && isSuspendable(ownedEnv) ? ownedEnv : undefined;
2360
- const parkOnlyRemoteEnv = suspendableEnv === undefined && ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && incompleteSuspendAdapter === undefined
2361
- ? ownedEnv
2362
- : undefined;
2363
- const parkOnlyHandle = (env) => {
2364
- const { snapshotId: _lineage, ...identity } = env.workspaceHandle();
2365
- return { ...identity, restoreMode: "park_only" };
2366
- };
2367
- const mintResourceSuspend = async (reason, sliceSpend, hint) => {
2368
- const rs = spec.resourceSuspend;
2369
- if (!checkpointStore)
2370
- return false;
2371
- const scope = rs?.scope ?? checkpointScopeOf(spec);
2372
- if (abortController.signal.aborted)
2373
- return false;
2374
- if (suspendLoopCapHit(priorLedger?.sliceCount ?? 0, maxSlices, " (resource slice limit)"))
2375
- return false;
2376
- const leafId = await session.getLeafId();
2377
- if (!leafId)
2378
- return false;
2379
- if (abortController.signal.aborted)
2380
- return false;
2381
- let remoteHandle;
2382
- if (suspendableEnv !== undefined) {
2383
- if (hasBackgroundShell(suspendableEnv))
2384
- await sweepBackgroundShells(suspendableEnv, defaultTaskRegistry);
2385
- const snap = await suspendableEnv.suspendVM({ abortSignal: abortController.signal });
2386
- if (!snap.ok) {
2387
- remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1));
2388
- deps.onError?.(new Error(`resource suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error }), { phase: "config", sessionId });
2389
- return false;
2390
- }
2391
- remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
2392
- }
2393
- else if (parkOnlyRemoteEnv !== undefined) {
2394
- if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
2395
- await sweepBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry);
2396
- remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
2397
- }
2398
- const token = mintCheckpointToken();
2399
- const gate = { kind: "resource_limit", reason, ...(hint !== undefined ? { resumeAfterMs: hint.resumeAfterMs } : {}) };
2400
- const resourceLedgerOut = debitLedger(priorLedger, sliceSpend, resourceTotal);
2401
- const mintedAt = Date.now();
2402
- const cp = {
2403
- token,
2404
- checkpointId: mintCheckpointId(),
2405
- version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
2406
- scope,
2407
- sessionId,
2408
- leafId,
2409
- gate,
2410
- pendingAction: { kind: "resource_limit", reason },
2411
- state: serializeCheckpointState(remoteHandle),
2412
- status: "pending",
2413
- createdAt: mintedAt,
2414
- deadline: mintedAt + Math.max(sanitizedTtlMs(rs?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS, hint !== undefined ? hint.resumeAfterMs + USAGE_WINDOW_REAP_MARGIN_MS : 0),
2415
- suspendCount: priorSuspendCount,
2416
- humanReview: humanReviewRef.count > 0
2417
- ? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
2418
- : undefined,
2419
- resourceLedger: resourceLedgerOut,
2420
- ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}),
2421
- ...(spec.principal ? { principal: spec.principal } : {}),
2422
- };
2423
- if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed")
2424
- return false;
2425
- publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction);
2426
- try {
2427
- await sessions.pin?.(sessionId);
2428
- }
2429
- catch (pinErr) {
2430
- deps.onError?.(pinErr, { phase: "config", sessionId });
2431
- }
2432
- harness.requestStopAfterTurn();
2433
- return true;
2434
- };
2435
- suspendForResource = resourceSuspendEligible ? mintResourceSuspend : undefined;
2436
- suspendForPlatformLimit = durableSuspendInfraReady ? mintResourceSuspend : undefined;
2437
- suspendForReview = checkpointStore
2438
- ? async (reason) => {
2439
- if (!checkpointStore)
2440
- return false;
2441
- if (abortController.signal.aborted)
2442
- return false;
2443
- if (incompleteSuspendAdapter !== undefined) {
2444
- try {
2445
- deps.onError?.(new Error(`plan_review park skipped: the per-task execution env declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")}, so its workspace could not be restored on resume (the plan-review request was dropped; the run continues without pausing)`), { phase: "config", sessionId });
2446
- }
2447
- catch {
2448
- }
2449
- return false;
2450
- }
2451
- if (suspendLoopCapHit(suspendChainBase(), maxSuspends, " for a plan_review (likely a resume/restart loop)."))
2452
- return false;
2453
- const leafId = await session.getLeafId();
2454
- if (!leafId) {
2455
- try {
2456
- deps.onError?.(new Error("plan_review park skipped: no committed session leaf (the plan-review request was dropped; the run continues without pausing)"), { phase: "config", sessionId });
2457
- }
2458
- catch {
2459
- }
2460
- return false;
2461
- }
2462
- if (abortController.signal.aborted)
2463
- return false;
2464
- let remoteHandle;
2465
- if (suspendableEnv !== undefined) {
2466
- if (hasBackgroundShell(suspendableEnv))
2467
- await sweepBackgroundShells(suspendableEnv, defaultTaskRegistry);
2468
- const snap = await suspendableEnv.suspendVM({ abortSignal: abortController.signal });
2469
- if (!snap.ok) {
2470
- remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1));
2471
- deps.onError?.(new Error(`plan_review suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error }), { phase: "config", sessionId });
2472
- return false;
2473
- }
2474
- remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
2475
- }
2476
- else if (parkOnlyRemoteEnv !== undefined) {
2477
- if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
2478
- await sweepBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry);
2479
- remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
2480
- }
2481
- const token = mintCheckpointToken();
2482
- const scope = checkpointScopeOf(spec);
2483
- const gate = { kind: "plan_review", reason: reason ?? `plan review requested` };
2484
- const reviewLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
2485
- const mintedAt = Date.now();
2486
- const cp = {
2487
- token,
2488
- checkpointId: mintCheckpointId(),
2489
- version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
2490
- scope,
2491
- sessionId,
2492
- leafId,
2493
- gate,
2494
- pendingAction: { kind: "plan_review" },
2495
- state: serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd()),
2496
- status: "pending",
2497
- createdAt: mintedAt,
2498
- suspendedAt: now(),
2499
- deadline: mintedAt + (sanitizedTtlMs(spec.durableApproval?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS),
2500
- suspendCount: suspendChainBase() + 1,
2501
- humanReview: humanReviewRef.count > 0
2502
- ? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
2503
- : undefined,
2504
- resourceLedger: reviewLedger,
2505
- sourceTaskId: sessionId,
2506
- ...(spec.principal ? { principal: spec.principal } : {}),
2507
- ...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}),
2508
- };
2509
- if ((await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).tag !== "committed")
2510
- return false;
2511
- publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, scope, remoteHandle, cp.checkpointId, cp.pendingAction);
2512
- try {
2513
- await sessions.pin?.(sessionId);
2514
- }
2515
- catch (pinErr) {
2516
- deps.onError?.(pinErr, { phase: "config", sessionId });
2517
- }
2518
- abortController.abort();
2519
- void harness.abort();
2520
- return true;
2521
- }
2522
- : undefined;
2523
- const resolveContentAsk = async (req, callSignal) => {
2524
- const contentAskSignal = composedCallSignal(callSignal);
2525
- if (!contentAskRoutable(req.toolCallId) || liveQuestionFace === undefined || mountedQuestionTool === undefined) {
2526
- return { kind: "unavailable", parkDeclined: false };
2527
- }
2528
- const deliveryId = uuidv7();
2529
- const bindOutcome = (entry, questionsHash) => {
2530
- if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId))
2531
- return;
2532
- contentAskBindings.set(req.toolCallId, { ...entry, questionsHash, deliveryId });
2533
- };
2534
- const refuseBeforeDelivery = (code, presentedInput) => ({
2535
- kind: "delivery_failure",
2536
- code,
2537
- ...(presentedInput !== undefined ? { presentedInput } : {}),
2538
- });
2539
- let retained;
2540
- let presented;
2541
- try {
2542
- retained = structuredClone(req.args);
2543
- presented = structuredClone(retained.questions);
2544
- }
2545
- catch {
2546
- return refuseBeforeDelivery("question.malformed_request");
2547
- }
2548
- const retainedQuestionsHash = boundInputHashOf(retained.questions);
2549
- if (!Value.Check(mountedQuestionTool.parameters, retained) || validateAskQuestions(presented) !== undefined) {
2550
- return refuseBeforeDelivery("question.malformed_request", retained);
2551
- }
2552
- if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId)) {
2553
- return { kind: "unavailable", parkDeclined: true };
2554
- }
2555
- if (contentAskSignal.aborted) {
2556
- return { kind: "delivery_failure", code: "question.aborted", presentedInput: retained };
2557
- }
2558
- try {
2559
- const facePromise = (async () => liveQuestionFace({
2560
- toolCallId: req.toolCallId,
2561
- questions: presented,
2562
- ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
2563
- sourceTaskId: sessionId,
2564
- boundInputHash: boundInputHashOf(retained),
2565
- deliveryId,
2566
- }, contentAskSignal))();
2567
- const settlement = await raceAbort(facePromise.then((outcome) => ({ tag: "outcome", outcome }), (error) => ({ tag: "threw", error })), contentAskSignal, () => ({ tag: "aborted" }));
2568
- if (settlement.tag === "aborted") {
2569
- void facePromise.then((late) => {
2570
- if (classifyQuestionOutcome(late).shape !== "answered")
2571
- return;
2572
- lateStrandedAnswers.push({ deliveryId, toolCallId: req.toolCallId });
2573
- discloseStrandedAnswers([{ deliveryId, toolCallId: req.toolCallId }], "the task had already ended when the answer arrived");
2574
- }, () => undefined).catch(() => undefined);
2575
- }
2576
- if (settlement.tag === "outcome") {
2577
- const outcome = settlement.outcome;
2578
- const reading = classifyQuestionOutcome(outcome);
2579
- if (reading.shape === "contradictory") {
2580
- bindOutcome({ kind: "failed", error: new Error("the question channel returned a contradictory outcome (an unavailable answer)") }, retainedQuestionsHash);
2581
- return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
2582
- }
2583
- if (reading.shape === "unavailable")
2584
- return { kind: "unavailable", parkDeclined: true, presentedInput: retained };
2585
- const answerSnapshot = reading.answer;
2586
- bindOutcome({ kind: "answered", answer: answerSnapshot }, retainedQuestionsHash);
2587
- return { kind: "answered", presentedInput: retained };
2588
- }
2589
- const error = settlement.tag === "threw"
2590
- ? settlement.error
2591
- : Object.assign(new Error("the task ended while this question was still out"), { name: "AbortError" });
2592
- bindOutcome({ kind: "failed", error }, retainedQuestionsHash);
2593
- return {
2594
- kind: "delivery_failure",
2595
- code: settlement.tag === "aborted" || contentAskSignal.aborted ? "question.aborted" : "question.human_channel_failed",
2596
- presentedInput: retained,
2597
- };
2598
- }
2599
- catch (err) {
2600
- bindOutcome({ kind: "failed", error: err }, retainedQuestionsHash);
2601
- return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
2602
- }
2603
- };
2604
- const suspendAsk = parkLaneArmed && checkpointStore !== undefined
2605
- ? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, carry) => {
2606
- const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
2607
- if (syncFirstEligible &&
2608
- runtimeCaps?.forceDurableGate !== true &&
2609
- liveFaceUnavailable !== true &&
2610
- !inheritedUnavailableAsks.has(req.toolCallId)) {
2611
- return undefined;
2612
- }
2613
- const cutSignal = composedCallSignal(carry?.signal);
2614
- let token;
2615
- let gate;
2616
- let cp;
2617
- let remoteHandle;
2618
- const remoteEnv = suspendableEnv;
2619
- try {
2620
- const parked = tryCloneArgs(postHookArgs);
2621
- if (!parked.ok) {
2622
- throw new ParkRefusal(`the arguments of "${req.toolName}" cannot be captured onto an approval checkpoint (unclonable value: ${parked.reason}) — ` +
2623
- `a durable approval row must hold an inert snapshot of exactly what a human approves and a resume executes`, { cause: parked.cause });
2624
- }
2625
- let backendFidelity;
2626
- try {
2627
- backendFidelity = resolveDeclaredFidelity(checkpointStore, "checkpointStore");
2628
- }
2629
- catch (err) {
2630
- throw new ParkRefusal(`the checkpoint backend declares an unrecognized encoding fidelity, so the width of the snapshot this row may hold is unknown (${describeThrown(err)}) — ` +
2631
- `refusing to park rather than guess which values would survive it`, { cause: err });
2632
- }
2633
- const captured = encodeAtFidelity(backendFidelity, parked.value);
2634
- if (!captured.ok) {
2635
- throw new ParkRefusal(`the arguments of "${req.toolName}" cannot be encoded by this checkpoint backend (${describeThrown(captured.cause)}) — ` +
2636
- `a durable approval row must hold an inert snapshot of exactly what a human approves and a resume executes`, { cause: captured.cause });
2637
- }
2638
- const parkedArgs = captured.value;
2639
- const projectionMovedArgs = backendFidelity !== "structured-clone" && !samePlainValue(parked.value, parkedArgs);
2640
- if (projectionMovedArgs && basePolicyForResumeEdit === undefined) {
2641
- throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and no policy is wired to re-adjudicate the stored shape ` +
2642
- `(no basePolicyForResumeEdit override and no caller toolPolicy) — refusing to file a row whose executable form nothing adjudicated`);
2643
- }
2644
- if (projectionMovedArgs && basePolicyForResumeEdit !== undefined) {
2645
- const presented = encodeAtFidelity(backendFidelity, parkedArgs);
2646
- if (!presented.ok) {
2647
- throw new ParkRefusal(`the stored form of "${req.toolName}"'s arguments could not be presented for re-adjudication (${describeThrown(presented.cause)})`, { cause: presented.cause });
2648
- }
2649
- const reprojectedRaced = await raceSettlementAgainstSignal(Promise.resolve(basePolicyForResumeEdit.check({
2650
- toolName: req.toolName,
2651
- args: presented.value,
2652
- toolCallId: req.toolCallId,
2653
- budget: budgetSnapshot,
2654
- ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}),
2655
- }, cutSignal)), cutSignal);
2656
- if (reprojectedRaced.tag === "aborted")
2657
- return undefined;
2658
- if (reprojectedRaced.tag === "threw")
2659
- throw reprojectedRaced.error;
2660
- const reprojected = refuseOutOfContractDecision(reprojectedRaced.value);
2661
- const demanded = reprojected.updatedInput !== undefined ? tryCloneArgs(reprojected.updatedInput) : undefined;
2662
- const rewroteTheFiledValue = reprojected.updatedInput !== undefined &&
2663
- !(demanded?.ok === true &&
2664
- samePlainValue(demanded.value, parkedArgs) &&
2665
- samePlainValue(reprojected.updatedInput, parkedArgs));
2666
- if (reprojected.action === "deny" || rewroteTheFiledValue) {
2667
- throw new ParkRefusal(`the arguments of "${req.toolName}" change shape when stored by this checkpoint backend, and the deployment's tool policy does not accept the stored shape` +
2668
- `${reprojected.action === "deny" && reprojected.message ? `: ${reprojected.message}` : ""} — ` +
2669
- `refusing to file a row whose executable form nothing adjudicated`);
2670
- }
2671
- }
2672
- if (offloadStore && isVolatileOffloadStore(offloadStore) && !offloadStore.isEmpty()) {
2673
- throw new ParkRefusal("durable suspend requires a durable toolResultStore (the default InMemoryToolResultStore " +
2674
- "holds offloaded results that are lost across a cross-replica resume); inject " +
2675
- "RunnerDeps.toolResultStore or disable offload.");
2676
- }
2677
- if (ownedEnv !== undefined && remoteEnv === undefined && parkOnlyRemoteEnv === undefined) {
2678
- throw new ParkRefusal(incompleteSuspendAdapter !== undefined
2679
- ? `durable suspend is not supported with this per-task executionEnvFactory env: it declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")}, so a snapshot taken now could never be restored. Implement the full RemoteExecutionEnv restore surface, or declare capabilities.suspendable:false if the workspace is externally durable (the park-only lane).`
2680
- : "durable suspend is not supported with a non-remote per-task executionEnvFactory env: the " +
2681
- "minted env is destroyed on suspend, so a resumed file/shell tool would act on a fresh " +
2682
- "(empty) env. Use a RemoteExecutionEnv factory (it is paused, not destroyed) or a static, " +
2683
- "caller-owned RunnerDeps.executionEnv.");
2684
- }
2685
- const leafId = await session.getLeafId();
2686
- if (!leafId) {
2687
- throw new ParkRefusal("cannot suspend: session has no committed leaf to resume from");
2688
- }
2689
- const { messages } = await session.buildContext();
2690
- const { batchToolCallIds, completedCallIds } = batchContextAt(messages, req.toolCallId);
2691
- if (cutSignal.aborted) {
2692
- return undefined;
2693
- }
2694
- if (suspendLoopCapHit(suspendChainBase(), maxSuspends, ` for tool "${req.toolName}" — likely a resume/restart loop.`))
2695
- return undefined;
2696
- if (remoteEnv !== undefined) {
2697
- if (hasBackgroundShell(remoteEnv))
2698
- await sweepBackgroundShells(remoteEnv, defaultTaskRegistry);
2699
- const snap = await remoteEnv.suspendVM({ abortSignal: cutSignal });
2700
- if (!snap.ok) {
2701
- remoteEnvFailures.push(remoteEnvFailureNote("suspendVM", snap.error, 1));
2702
- throw new Error(`suspendVM failed (${snap.error.code}): ${snap.error.message}`, { cause: snap.error });
2703
- }
2704
- remoteHandle = { ...remoteEnv.workspaceHandle(), snapshotId: snap.value };
2705
- if (cutSignal.aborted) {
2706
- const back = await compensatePausedVM(remoteEnv, snap.value);
2707
- if (!back.ok) {
2708
- abortController.abort();
2709
- void harness.abort();
2710
- }
2711
- return undefined;
2712
- }
2713
- }
2714
- else if (parkOnlyRemoteEnv !== undefined) {
2715
- if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
2716
- await sweepBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry);
2717
- remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
2718
- }
2719
- token = mintCheckpointToken();
2720
- const riskDescriptor = buildRiskDescriptor({
2721
- toolName: req.toolName,
2722
- args: parkedArgs,
2723
- safety,
2724
- ...(shadowedRule !== undefined ? { shadowedRule } : {}),
2725
- ...(probeReason !== undefined ? { probeReason } : {}),
2726
- ...(probeCause !== undefined ? { probeCause } : {}),
2727
- shellGated: (req.toolName === "Bash" && shellGatedBash) || (req.toolName === "Monitor" && shellGatedMonitor),
2728
- ...(effectiveShellGate !== "off" ? { shellGateDoctrine: effectiveShellGate } : {}),
2729
- });
2730
- gate =
2731
- safety !== undefined || realApproval !== undefined
2732
- ? {
2733
- kind: "irreversible_ask",
2734
- reason: safety !== undefined
2735
- ? `human approval required before safety-tightened tool "${req.toolName}"`
2736
- : `real human approval required for tool "${req.toolName}" (non-budgetable: ${realApproval.origin})`,
2737
- toolName: req.toolName,
2738
- ...(safety !== undefined ? { safetyAxis: safety } : {}),
2739
- ...(realApproval !== undefined ? { realApproval } : {}),
2740
- riskDescriptor,
2741
- }
2742
- : durableApproval
2743
- ? {
2744
- kind: "human",
2745
- reason: `human approval required for tool "${req.toolName}"`,
2746
- toolName: req.toolName,
2747
- riskDescriptor,
2748
- }
2749
- : {
2750
- kind: "irreversible_ask",
2751
- reason: `human approval required before irreversible tool "${req.toolName}"`,
2752
- toolName: req.toolName,
2753
- riskDescriptor,
2754
- };
2755
- const scope = durableApproval?.scope || checkpointScopeOf({ principal: spec.principal });
2756
- const ttlMs = sanitizedTtlMs(durableApproval?.ttlMs);
2757
- const checkpointState = serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd());
2758
- if (realApproval !== undefined && checkpointState.readFace !== undefined) {
2759
- checkpointState.readFace = { ...checkpointState.readFace, realApproval: true };
2760
- }
2761
- const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
2762
- const mintedAt = Date.now();
2763
- cp = {
2764
- token,
2765
- checkpointId: mintCheckpointId(),
2766
- version: faceCheckpointState() ? FACE_CHECKPOINT_VERSION : realApproval !== undefined ? REAL_APPROVAL_CHECKPOINT_VERSION : f012CheckpointState() ? F012_CHECKPOINT_VERSION : orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
2767
- scope,
2768
- sessionId,
2769
- leafId,
2770
- gate,
2771
- pendingAction: {
2772
- kind: "tool_approval",
2773
- toolCallId: req.toolCallId,
2774
- toolName: req.toolName,
2775
- args: parkedArgs,
2776
- ...(() => {
2777
- const p = approvalPreviewOf(req.toolName, parkedArgs);
2778
- const bidi = carriesBidiControls(parkedArgs) || carriesBidiControls(p.preview);
2779
- return {
2780
- ...(p.preview !== undefined ? { preview: p.preview } : {}),
2781
- ...(p.withheld !== undefined ? { previewWithheld: p.withheld } : {}),
2782
- ...(bidi ? { hasBidiControls: true } : {}),
2783
- };
2784
- })(),
2785
- ...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
2786
- ...askCarryRowMembers(carry),
2787
- ...ruleOffersOf(req.toolName, parkedArgs, {
2788
- ...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
2789
- ...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
2790
- ...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
2791
- ...(matchedAskRule !== undefined ? { matchedAskRule } : {}),
2792
- ...(probeMandated === true ? { probeMandated: true } : {}),
2793
- ...(inheritedUnavailableAsks.has(req.toolCallId) ? { inheritedUnresolved: true } : {}),
2794
- ...(segmentCoverage !== undefined ? { segmentCoverage } : {}),
2795
- }),
2796
- boundInputHash: boundInputHashOf(parkedArgs),
2797
- batchToolCallIds,
2798
- completedCallIds,
2799
- },
2800
- state: checkpointState,
2801
- deadline: ttlMs !== undefined
2802
- ? mintedAt + ttlMs
2803
- : durableApproval !== undefined
2804
- ? undefined
2805
- : mintedAt + DEFAULT_UNATTENDED_APPROVAL_TTL_MS,
2806
- status: "pending",
2807
- createdAt: mintedAt,
2808
- suspendedAt: now(),
2809
- humanReview: humanReviewRef.count > 0
2810
- ? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
2811
- : undefined,
2812
- suspendCount: suspendChainBase() + 1,
2813
- resourceLedger: approvalLedger,
2814
- sourceTaskId: sessionId,
2815
- ...(spec.principal ? { principal: spec.principal } : {}),
2816
- ...(durableApproval !== undefined ? { durableApproval: { ...durableApproval } } : {}),
2817
- };
2818
- }
2819
- catch (err) {
2820
- const cutWon = cutSignal.aborted;
2821
- const preselected = cutWon
2822
- ? undefined
2823
- : {
2824
- parkFailed: err instanceof ParkRefusal
2825
- ? err.message
2826
- : "the durable approval park could not be prepared (the deployment's error face carries the exception)",
2827
- };
2828
- try {
2829
- deps.onError?.(err, { phase: "config", sessionId });
2830
- }
2831
- catch {
2832
- }
2833
- return preselected;
2834
- }
2835
- const committed = await commitSuspendSaga(token, cp, remoteEnv, remoteHandle, cutSignal);
2836
- if (committed.tag !== "committed") {
2837
- if (committed.tag === "cut")
2838
- return undefined;
2839
- if (committed.tag === "absent") {
2840
- if (cutSignal.aborted)
2841
- return undefined;
2842
- return committed.reason !== undefined ? { parkFailed: committed.reason } : undefined;
2843
- }
2844
- return { parkFailed: committed.reason };
2845
- }
2846
- publishCommittedSuspend({ suspendRef, reviewRef }, token, gate, cp.scope, remoteHandle, cp.checkpointId, cp.pendingAction);
2847
- try {
2848
- await sessions.pin?.(sessionId);
2849
- }
2850
- catch (pinErr) {
2851
- deps.onError?.(pinErr, { phase: "config", sessionId });
2852
- }
2853
- abortController.abort();
2854
- void harness.abort();
2855
- return { token, gate };
2856
- }
2857
- : undefined;
2858
- for (const t of tools) {
2859
- const eff = t.effect;
2860
- if (eff !== undefined && !toolEffects.has(t.name))
2861
- toolEffects.set(t.name, eff);
2862
- }
2863
- const writeProtectionCheck = createWriteProtectionCheck(deps.writeProtectedPaths, deps.memoryEngineDir !== undefined ? { dataRoot: deps.memoryEngineDir } : undefined);
2864
- const writeProtectionArmed = writeProtectionCheck !== undefined && tools.some((t) => PATH_CONFINABLE_WRITE_TOOLS.has(t.name));
2865
- toolCallGateArmedRef.armed =
2866
- effectivePolicy !== undefined ||
2867
- hooks?.preToolUse !== undefined ||
2868
- egressTools.size > 0 ||
2869
- irreversibleTools.size > 0 ||
2870
- spec.enablePlanMode === true ||
2871
- complianceDenies.has("web_fetch") ||
2872
- writeProtectionArmed;
2873
- if (toolCallGateArmedRef.armed) {
2874
- harness.on("tool_call", async (e) => {
2875
- blockedToolCalls.delete(e.toolCallId);
2876
- inheritedAskGrants.delete(e.toolCallId);
2877
- humanBareRejections.delete(e.toolCallId);
2878
- {
2879
- const complianceDeny = complianceCallDenial(complianceDenies, e.toolName);
2880
- if (complianceDeny !== undefined) {
2881
- if (blockedTracked)
2882
- blockedToolCalls.add(e.toolCallId);
2883
- if (notifyPermissionDenied) {
2884
- await notifyPermissionDenied({ toolName: e.toolName, input: cloneObserverInput(e.input), toolCallId: e.toolCallId, reason: complianceDeny, source: "safety", identity: hookIdentity });
2885
- }
2886
- return { block: true, reason: formatHookFeedback(complianceDeny, reminderMark), preToolContext: [] };
2887
- }
2888
- }
2889
- if (planModeRef.active && (toolEffects.get(e.toolName) ?? "write") !== "read") {
2890
- if (blockedTracked)
2891
- blockedToolCalls.add(e.toolCallId);
2892
- const planDenyReason = `Plan mode is active — "${e.toolName}" is a write/mutating tool and is read-only-blocked. ` +
2893
- `Research with read-only tools, then call ${PRESENT_PLAN_TOOL_NAME} with your plan to get it approved before acting.`;
2894
- if (notifyPermissionDenied) {
2895
- await notifyPermissionDenied({ toolName: e.toolName, input: cloneObserverInput(e.input), toolCallId: e.toolCallId, reason: planDenyReason, source: "planMode", identity: hookIdentity });
2896
- }
2897
- return {
2898
- block: true,
2899
- reason: formatHookFeedback(planDenyReason, reminderMark),
2900
- preToolContext: [],
2901
- };
2902
- }
2903
- let result;
2904
- let humanBareRejection = false;
2905
- try {
2906
- result = await runToolGate({
2907
- onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
2908
- event: e,
2909
- ...(e.signal !== undefined ? { callSignal: e.signal } : {}),
2910
- identity: hookIdentity,
2911
- reminderMark,
2912
- preToolUse: ownGatePreToolUse,
2913
- hookTimeoutMs,
2914
- ...(handsCwdRef !== undefined ? { trackedCwd: () => handsCwdRef.current } : {}),
2915
- ...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
2916
- adjudicate,
2917
- resolveAsk: resolveAskBound,
2918
- suspendAsk,
2919
- resolveContentAsk,
2920
- egress: egressTools.has(e.toolName),
2921
- peerMessage: e.toolName === SEND_MESSAGE_TOOL_NAME,
2922
- ...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
2923
- irreversibility: irreversibilityTier.get(e.toolName),
2924
- reversibilityProbe: reversibilityProbes.get(e.toolName),
2925
- abortSignal: abortController.signal,
2926
- ...(notifyPermissionDenied ? { permissionDenied: notifyPermissionDenied } : {}),
2927
- onHookError: notifyHookError,
2928
- shellGated: (e.toolName === "Bash" && shellGatedBash) || (e.toolName === "Monitor" && shellGatedMonitor),
2929
- ...(autoModeDecider ? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}) } } : {}),
2930
- onHeadlessDenialLimit: stopForDenialLimit,
2931
- ...(permissionRuleLane
2932
- ? {
2933
- persistedRules: {
2934
- admits: permissionRuleLane.admits,
2935
- onResolved: (info) => emitTrace(deps.tracer, () => ({
2936
- kind: "permission.persisted_rule_allowed",
2937
- version: 2,
2938
- taskId: hostTaskId,
2939
- toolName: info.toolName,
2940
- toolCallId: info.toolCallId,
2941
- rules: [...info.rules],
2942
- ts: Date.now(),
2943
- })),
2944
- },
2945
- }
2946
- : {}),
2947
- ...(permissionRuleOrgLane
2948
- ? {
2949
- orgRules: {
2950
- adjudicate: permissionRuleOrgLane.adjudicate,
2951
- contentAskToolMounted: questionToolMounted,
2952
- onUnavailable: (info) => emitTrace(deps.tracer, () => ({
2953
- kind: "permission.org_snapshot_unavailable",
2954
- version: 1,
2955
- taskId: hostTaskId,
2956
- toolName: info.toolName,
2957
- toolCallId: info.toolCallId,
2958
- message: info.message,
2959
- ts: Date.now(),
2960
- })),
2961
- },
2962
- }
2963
- : {}),
2964
- isMarkedUnresolvable: (toolCallId) => inheritedUnavailableAsks.has(toolCallId),
2965
- ...(sandboxAdmissionArmed
2966
- ? {
2967
- sandboxAdmission: {
2968
- askClassesOf: (toolCallId) => foldAskClasses.get(toolCallId),
2969
- boundaryCapable: sandboxBoundaryCapable(e.toolName),
2970
- onAdmitted: emitSandboxAdmitted,
2971
- },
2972
- }
2973
- : {}),
2974
- });
2975
- }
2976
- finally {
2977
- inheritedUnavailableAsks.delete(e.toolCallId);
2978
- inheritedAskGrants.delete(e.toolCallId);
2979
- foldAskClasses.delete(e.toolCallId);
2980
- humanBareRejection = humanBareRejections.delete(e.toolCallId);
2981
- }
2982
- const ancestorAdmitted = ancestorSandboxAdmissions.get(e.toolCallId);
2983
- ancestorSandboxAdmissions.delete(e.toolCallId);
2984
- if (ancestorAdmitted !== undefined && !result.block && !result.suspend) {
2985
- emitSandboxAdmitted({ toolCallId: e.toolCallId, toolName: e.toolName, askClasses: ancestorAdmitted, boundary: "sandbox_internal" });
2986
- result.preToolContext.push(`permission.sandbox_admitted: this call's inherited pending approval was auto-admitted because the deployment declares an isolated sandbox execution env and the request stays inside it. Recorded for audit.`);
2987
- }
2988
- if (!result.block && !result.suspend && result.preToolContext.length > 0) {
2989
- preToolContexts.set(e.toolCallId, result.preToolContext);
2990
- }
2991
- if (blockedTracked && (result.block || result.suspend)) {
2992
- blockedToolCalls.add(e.toolCallId);
2993
- }
2994
- const settlement = screenGateSettlement(result, result.block === true);
2995
- for (const defect of settlement.defects)
2996
- deps.onError?.(new Error(defect), { phase: "config", sessionId });
2997
- if (settlement.record !== undefined)
2998
- approvalSettlement.set(e.toolCallId, settlement.record);
2999
- if (result.block) {
3000
- const haltRemaining = maybeHumanRejectionHalt({
3001
- bare: humanBareRejection,
3002
- settledBy: result.settledBy,
3003
- isDelegatedChild: delegation.isDelegatedChild === true,
3004
- toolName: e.toolName,
3005
- toolCallId: e.toolCallId,
3006
- });
3007
- if (haltRemaining !== undefined) {
3008
- batchHaltRef.current = { rejectedToolCallId: e.toolCallId };
3009
- return { block: true, reason: result.reason, haltRemaining };
3010
- }
3011
- return { block: true, reason: result.reason };
3012
- }
3013
- return result.updatedInput !== undefined ? { updatedInput: result.updatedInput } : undefined;
3014
- });
3015
- }
3016
- }
3017
- if (hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure) {
3018
- harness.on("tool_result", async (e) => {
3019
- if (blockedToolCalls.delete(e.toolCallId)) {
3020
- return undefined;
3021
- }
3022
- let content = e.content;
3023
- let changed = false;
3024
- if (e.isError) {
3025
- if (hooks?.postToolUseFailure) {
3026
- let clonedDetails = e.details;
3027
- try {
3028
- clonedDetails = structuredClone(e.details);
3029
- }
3030
- catch {
3031
- }
3032
- const failureSeat = await runHookSeat("postToolUseFailure", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUseFailure(e.toolName, e.input, {
3033
- error: e.content
3034
- .filter((c) => c.type === "text")
3035
- .map((c) => c.text)
3036
- .join("\n"),
3037
- isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
3038
- content: e.content.map((c) => ({ ...c })),
3039
- details: clonedDetails,
3040
- }, { toolCallId: e.toolCallId, toolName: e.toolName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity }));
3041
- if (failureSeat.expired) {
3042
- notifyOwnHookCrash(hookSeatExpiredError("postToolUseFailure", hookTimeoutMs, failureSeat.cause, "the observation was abandoned and its additionalContext dropped; the failure result itself is unchanged"));
3043
- }
3044
- const patch = failureSeat.expired ? undefined : failureSeat.value;
3045
- if (patch?.additionalContext) {
3046
- content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
3047
- changed = true;
3048
- }
3049
- }
3050
- }
3051
- else if (hooks?.postToolUse) {
3052
- const successSeat = await runHookSeat("postToolUse", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUse(e.toolName, e.input, { content: e.content, details: e.details, isError: e.isError }, { toolCallId: e.toolCallId, toolName: e.toolName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity }));
3053
- if (successSeat.expired) {
3054
- notifyOwnHookCrash(hookSeatExpiredError("postToolUse", hookTimeoutMs, successSeat.cause, "the seat was abandoned; the tool result is delivered to the model UNMODIFIED (no updatedOutput, no additionalContext)"));
3055
- }
3056
- const patch = successSeat.expired ? undefined : successSeat.value;
3057
- if (patch?.updatedOutput) {
3058
- content = patch.updatedOutput;
3059
- changed = true;
3060
- }
3061
- if (patch?.additionalContext) {
3062
- content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
3063
- changed = true;
3064
- }
3065
- }
3066
- const pre = preToolContexts.get(e.toolCallId);
3067
- if (pre) {
3068
- preToolContexts.delete(e.toolCallId);
3069
- content = [...content, ...pre.map((t) => ({ type: "text", text: formatHookFeedback(t, reminderMark) }))];
3070
- changed = true;
3071
- }
3072
- return changed ? { content } : undefined;
3073
- });
3074
- }
3075
- const editAt = editBudget(model);
3076
- const guardAt = guardBudget(model);
3077
- const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
3078
- const charsPerToken = model.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
3079
- harness.on("context", async ({ messages, recheck, signal }) => {
3080
- batchHaltRef.current = undefined;
3081
- const pendingProjectionObservations = [];
3082
- try {
3083
- const healed = dropEmptyFailureAssistants(messages);
3084
- const replay = replayClearedProjection(healed, microCompact.ledger);
3085
- const replayed = replay.messages;
3086
- const capped = await capAggregateToolResults(replayed, {
3087
- store: offloadStore,
3088
- sessionId,
3089
- onCapped: (info) => pendingProjectionObservations.push(() => emitTrace(deps.tracer, () => ({
3090
- kind: "tool_result.capped",
3091
- version: 1,
3092
- taskId: hostTaskId,
3093
- ...(info.tool !== undefined ? { tool: info.tool } : {}),
3094
- sizeChars: info.sizeChars,
3095
- storeFallback: info.storeFallback,
3096
- ts: Date.now(),
3097
- }))),
3098
- });
3099
- const mediaCapped = capAggregateMediaBytes(capped, {
3100
- limitBytes: deps.mediaByteCapBytes ?? AGGREGATE_MEDIA_BUDGET_BYTES,
3101
- onStripped: (info) => {
3102
- const onMediaStripped = deps.onMediaStripped;
3103
- if (onMediaStripped !== undefined)
3104
- pendingProjectionObservations.push(() => onMediaStripped(info));
3105
- },
3106
- });
3107
- let ccPass;
3108
- const edited = microCompact.machine === "off" ? mediaCapped : clearStaleToolResults(mediaCapped, {
3109
- budgetTokens: editAt,
3110
- machine: microCompact.machine,
3111
- ...(microCompact.machine === "cc" || microCompact.clearOnRejection ? { recognizeCcMarkers: true } : {}),
3112
- ...(microCompact.machine === "cc"
3113
- ? {
3114
- clearSource: replayed,
3115
- onCleared: (pass) => { ccPass = pass; },
3116
- }
3117
- : {}),
3118
- anchoredTotalTokens: estimateContextTokens(mediaCapped, charsPerToken).tokens,
3119
- charsPerToken,
3120
- ...(offloadStore
3121
- ? {
3122
- offload: {
3123
- persist: createOffloadPersist(offloadStore, sessionId, deps.onNotice),
3124
- },
3125
- }
3126
- : {}),
3127
- });
3128
- if (ccPass !== undefined) {
3129
- recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: hostTaskId, trigger: "frontier" });
3130
- }
3131
- const chain = await runGuardChain({
3132
- edited, replayed, index: replay.index, microCompact, guardAt, charsPerToken, offloadStore,
3133
- sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal,
3134
- pendingProjectionObservations,
3135
- });
3136
- if ("earlyReturn" in chain) {
3137
- return chain.earlyReturn;
3138
- }
3139
- const { working, trimDroppedMessages } = chain;
3140
- let trimmed = chain.trimmed;
3141
- trimmed = applyGitFrameGuard({
3142
- before: working,
3143
- trimmed,
3144
- budgetTokens: guardAt,
3145
- ref: gitStatusRef,
3146
- charsPerToken,
3147
- onDegrade: (message) => {
3148
- try {
3149
- deps.onError?.(new Error(message), { phase: "degraded", sessionId, classification: "env-git-snapshot" });
3150
- }
3151
- catch {
3152
- }
3153
- },
3154
- });
3155
- const swept = dropOrphanToolResults(trimmed);
3156
- if (swept.dropped.length > 0) {
3157
- try {
3158
- deps.onError?.(new Error(`context belt: dropped ${swept.dropped.length} orphan toolResult(s) whose tool-call assistant was not in the request view — ` +
3159
- swept.dropped
3160
- .map((d) => `toolCallId=${d.toolCallId} at index ${d.index} (prev=${d.prevRole}, next=${d.nextRole})`)
3161
- .join("; ")), { phase: "hook", sessionId });
3162
- }
3163
- catch {
3164
- }
3165
- }
3166
- requestLossyRef.current =
3167
- replayed !== healed ||
3168
- capped !== replayed ||
3169
- mediaCapped !== capped ||
3170
- edited !== mediaCapped ||
3171
- working !== edited ||
3172
- trimDroppedMessages ||
3173
- swept.dropped.length > 0 ||
3174
- gitStatusRef.overBudgetShrunk === true;
3175
- microCompact.projectionRef.current = { messages: swept.messages, keyOf: replay.index.keyOf };
3176
- return { messages: swept.messages };
3177
- }
3178
- finally {
3179
- flushProjectionObservations(pendingProjectionObservations);
3180
- }
3181
- });
3182
- const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
3183
- const cacheFingerprint = cacheBreakDetector
3184
- ? { systemPrompt, tools: toolsToFingerprintInputs(harnessTools) }
3185
- : undefined;
3186
- fpRef.current = cacheFingerprint;
3187
- const turnSnapshot = buildTurnPromptSnapshot({
3188
- provider: model.provider,
3189
- modelId: model.id,
3190
- artifactDigest: epochArtifactDigestForSnapshot,
3191
- promptProfile,
3192
- fableMitigations,
3193
- layoutGroups: (assembled.systemBlocks ?? [{ groupId: "body", cacheControlBoundary: true }]).map((b) => ({ groupId: b.groupId, cacheControlBoundary: b.cacheControlBoundary })),
3194
- stableSystemText: systemPrompt,
3195
- toolWire: toolsToFingerprintInputs(harnessTools),
3196
- thinkingLevel: (thinking ?? "off"),
3197
- maxTokens: model.maxTokens,
3198
- hasOutputSchema: spec.outputSchema !== undefined,
3199
- sessionId,
3200
- taskId: spec.taskId,
3201
- }, model.api, (text) => `salted:${createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex")}`);
3202
- turnSnapshotRef.current = turnSnapshot;
3203
- promptManifest.snapshot = { cacheIdentity: turnSnapshot.cacheIdentity, elements: { ...turnSnapshot.elements } };
3204
- promptManifest.lowering = turnSnapshot.lowering;
3205
- const promptOverheadTokens = Math.ceil((systemPrompt.length +
3206
- harnessTools.reduce((a, t) => a + t.name.length + (t.description?.length ?? 0) + JSON.stringify(t.parameters ?? {}).length, 0)) / charsPerToken);
3207
- const announceAttachmentSkip = (path, reason) => {
3208
- deps.onError?.(new Error(`working-file attachment skipped — ${path}: ${reason}`), {
3209
- phase: "degraded",
3210
- sessionId,
3211
- classification: "attachment-read",
3212
- });
3213
- };
3214
- const readTaskFile = handsEnabled
3215
- ? async (path) => {
3216
- try {
3217
- let key = path;
3218
- if (attachmentRootCanonical !== undefined) {
3219
- const rk = await resolveKey(executionEnv, attachmentRootCanonical, path, undefined, undefined, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, readDenyMatcher, resolvedReadFace);
3220
- if (!rk.ok) {
3221
- announceAttachmentSkip(path, rk.violation.message);
3222
- if (rk.violation.code === "read_path_denied" && rk.violation.pattern !== undefined) {
3223
- return { withheld: { pattern: rk.violation.pattern } };
3224
- }
3225
- return null;
3226
- }
3227
- key = rk.key;
3228
- }
3229
- const r = await executionEnv.readBinaryFile(key);
3230
- if (!r.ok) {
3231
- announceAttachmentSkip(path, r.error.message);
3232
- return null;
3233
- }
3234
- const d = decodeTextBytes(r.value);
3235
- if (d.malformed) {
3236
- announceAttachmentSkip(path, "malformed text encoding (truncated UTF-16 body)");
3237
- return null;
3238
- }
3239
- return d.text;
3240
- }
3241
- catch (err) {
3242
- announceAttachmentSkip(path, err instanceof Error ? err.message : String(err));
3243
- return null;
3244
- }
3245
- }
3246
- : undefined;
3247
- const normalizeAttachmentPath = handsEnabled
3248
- ? async (path) => {
3249
- if (attachmentRootCanonical === undefined)
3250
- return path;
3251
- try {
3252
- const rk = await resolveKey(executionEnv, attachmentRootCanonical, path, undefined, handsCwdRef?.current, [...additionalRootsCanonical, ...additionalReadRootsCanonical], undefined, undefined, resolvedReadFace);
3253
- return rk.ok ? rk.key : path;
3254
- }
3255
- catch {
3256
- return path;
3257
- }
3258
- }
3259
- : undefined;
3260
- const isDedupStubResult = handsEnabled ? isReadDedupStubResult : undefined;
3261
- const recentlyReadFiles = handsEnabled && readFileStateForCheckpoint
3262
- ? () => [...readFileStateForCheckpoint.entries()]
3263
- .filter(([, v]) => v.seededFromContext !== true)
3264
- .sort((a, b) => (b[1].lastReadAt ?? 0) - (a[1].lastReadAt ?? 0))
3265
- .map(([path]) => path)
3266
- : undefined;
3267
- const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
3268
- ? (attachedComplete, preserveReadState) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete, preserveReadState)
3269
- : undefined;
3270
- const changedFilesReadDenied = denyNarrowingPolicy === undefined
3271
- ? undefined
3272
- : async (path) => {
3273
- try {
3274
- const d = refuseOutOfContractDecision(await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal));
3275
- return d.action === "deny";
3276
- }
3277
- catch {
3278
- return false;
3279
- }
3280
- };
3281
- const detectExternalChanges = handsEnabled && readFileStateForCheckpoint
3282
- ? async (maxFiles) => {
3283
- const candidates = [...readFileStateForCheckpoint.entries()]
3284
- .filter(([, e]) => e.lastReadAt !== undefined)
3285
- .filter(([, e]) => e.truncated !== true)
3286
- .sort((a, b) => (b[1].lastReadAt ?? 0) - (a[1].lastReadAt ?? 0))
3287
- .slice(0, Math.max(0, maxFiles));
3288
- const changed = [];
3289
- const evicted = [];
3290
- for (const [path, entry] of candidates) {
3291
- if (changedFilesReadDenied !== undefined && (await changedFilesReadDenied(path)))
3292
- continue;
3293
- try {
3294
- const info = await executionEnv.fileInfo(path, abortController.signal);
3295
- if (!info.ok) {
3296
- if (info.error.code === "not_found") {
3297
- readFileStateForCheckpoint.delete(path);
3298
- evicted.push(path);
3299
- }
3300
- continue;
3301
- }
3302
- if (info.value.mtimeMs > (entry.lastReadAt ?? 0) + CHANGED_FILES_MTIME_EPS_MS) {
3303
- changed.push({ path, mtimeMs: info.value.mtimeMs });
3304
- }
3305
- }
3306
- catch {
3307
- }
3308
- }
3309
- return { changed, evicted };
3310
- }
3311
- : undefined;
3312
- const listBackgroundTasks = () => defaultTaskRegistry
3313
- .list({ owner: hostTaskId, scope: taskScope, sessionId })
3314
- .filter((t) => t.status === "pending" || t.status === "running" || t.status === "parked")
3315
- .map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}), status: t.status }));
3316
- const overheadState = { promptChars: 0 };
3317
- const centerCompactionCandidate = deps.promptSource !== undefined && centerAdoption !== undefined
3318
- ? () => {
3319
- const state = deps.promptSource.sourceState?.() ?? "unavailable";
3320
- const cand = deps.promptSource.current();
3321
- const toBundled = state === "disabled";
3322
- if (!toBundled && (cand === undefined || cand.artifact.artifactDigest === centerAdoption.artifact.artifactDigest))
3323
- return undefined;
3324
- let a2;
3325
- try {
3326
- a2 = assemblePrompt(buildAssembleInputs(toBundled ? undefined : artifactDeclarations(cand.artifact)));
3327
- }
3328
- catch {
3329
- return undefined;
3330
- }
3331
- if (!toBundled && a2.centerMounted !== true)
3332
- return undefined;
3333
- const declared2 = (a2.sections ?? [])
3334
- .filter((sec) => sec.origin === "provider")
3335
- .map((sec) => ({ id: sec.id, slot: sec.slot, chars: sec.text.length, ...(sec.declaredContentHash !== undefined ? { contentHash: sec.declaredContentHash } : {}) }));
3336
- const overheadTokensAfter = preparedHolder.current.promptOverheadTokens -
3337
- Math.ceil(overheadState.promptChars / charsPerToken) +
3338
- Math.ceil(a2.systemPrompt.length / charsPerToken);
3339
- return {
3340
- ...(toBundled
3341
- ? { clear: true }
3342
- : {
3343
- centerArtifactDigest: cand.artifact.artifactDigest,
3344
- ...(cand.sourceRevision !== undefined ? { sourceRevision: cand.sourceRevision } : {}),
3345
- }),
3346
- declaredSections: declared2,
3347
- overheadTokensAfter,
3348
- apply: (committedArtifactDigest) => {
3349
- systemPrompt = a2.systemPrompt;
3350
- harnessRef.current?.setSystemPrompt(a2.systemPrompt, a2.systemBlocks);
3351
- if (fpRef.current)
3352
- fpRef.current.systemPrompt = a2.systemPrompt;
3353
- turnSnapshotRef.current?.refreshStableSystem(a2.systemPrompt, committedArtifactDigest);
3354
- preparedHolder.current.epochDeclaredSections = declared2;
3355
- preparedHolder.current.promptOverheadTokens = overheadTokensAfter;
3356
- overheadState.promptChars = a2.systemPrompt.length;
3357
- centerAdoption = toBundled
3358
- ? undefined
3359
- : { artifact: cand.artifact, ...(cand.sourceRevision !== undefined ? { sourceRevision: cand.sourceRevision } : {}) };
3360
- },
3361
- };
3362
- }
3363
- : undefined;
3364
- overheadState.promptChars = systemPrompt.length;
3365
- const effectiveReadFaceObserved = carrierReadFace();
3366
- const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
3367
- await onceLedger.settle(session, announcedListingsRef);
663
+ const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, humanBareRejections, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash });
664
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, inheritedParentConstraints, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, autoModeIntent, autoModeDecider, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
665
+ const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, suspendRef, reviewRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
666
+ const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedBash, shellGatedMonitor, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, suspendRef, reviewRef });
667
+ prepareGateStations({ askLane, parkAsk, tools, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, humanBareRejections, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, approvalSettlement, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedBash, shellGatedMonitor, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
668
+ const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef });
3368
669
  const preparedHolder = {};
670
+ const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, readDenyAdditionsNormalized, preparedHolder });
671
+ await onceLedger.settle(session, announcedListingsRef);
3369
672
  const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
3370
673
  rollback.commit();
3371
674
  const prepared = buildPrepared();