@sema-agent/core 7.50.2 → 7.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +263 -0
  2. package/dist/agents/child-failure.d.ts +5 -16
  3. package/dist/agents/child-failure.js +4 -24
  4. package/dist/agents/subagent-steps.js +2 -2
  5. package/dist/agents/subagent.d.ts +4 -13
  6. package/dist/agents/subagent.js +94 -204
  7. package/dist/agents/tool-filter.d.ts +2 -2
  8. package/dist/brain/anthropic.d.ts +1 -1
  9. package/dist/brain/anthropic.js +7 -5
  10. package/dist/brain/open-responses.d.ts +1 -1
  11. package/dist/brain/open-responses.js +3 -2
  12. package/dist/brain/openai.d.ts +1 -1
  13. package/dist/brain/openai.js +3 -2
  14. package/dist/brain/request-route.d.ts +2 -2
  15. package/dist/brain/route-adjudicator.d.ts +16 -7
  16. package/dist/brain/route-adjudicator.js +23 -7
  17. package/dist/brain/route-conformance.js +23 -5
  18. package/dist/brain/stream-engine.js +9 -3
  19. package/dist/core/a2a.js +6 -8
  20. package/dist/core/allow-entry.d.ts +45 -26
  21. package/dist/core/allow-entry.js +48 -24
  22. package/dist/core/arg-summary.d.ts +8 -85
  23. package/dist/core/arg-summary.js +4 -386
  24. package/dist/core/ask-origin.d.ts +35 -33
  25. package/dist/core/ask-origin.js +9 -11
  26. package/dist/core/checkpoint-requirements.d.ts +6 -0
  27. package/dist/core/checkpoint-requirements.js +1 -0
  28. package/dist/core/checkpoint-store.d.ts +15 -34
  29. package/dist/core/checkpoint-store.js +5 -4
  30. package/dist/core/classifier-unavailable-arm.d.ts +12 -8
  31. package/dist/core/classifier-unavailable-arm.js +7 -2
  32. package/dist/core/credential-vocabulary.d.ts +3 -2
  33. package/dist/core/credential-vocabulary.js +27 -18
  34. package/dist/core/delegation-frames.d.ts +40 -15
  35. package/dist/core/delegation-frames.js +34 -0
  36. package/dist/core/engine-notice.d.ts +35 -11
  37. package/dist/core/gate-fold.js +9 -7
  38. package/dist/core/gate-lanes.js +6 -4
  39. package/dist/core/gate-outcome.d.ts +81 -15
  40. package/dist/core/gate-outcome.js +168 -67
  41. package/dist/core/governance-codes.d.ts +7 -1
  42. package/dist/core/governance-codes.js +9 -0
  43. package/dist/core/hooks.d.ts +4 -4
  44. package/dist/core/hooks.js +4 -3
  45. package/dist/core/mcp-connection-pool.d.ts +2 -0
  46. package/dist/core/mcp-connection-pool.js +2 -1
  47. package/dist/core/mcp-failure.js +2 -1
  48. package/dist/core/mcp-server-spec.d.ts +27 -5
  49. package/dist/core/mcp.d.ts +13 -38
  50. package/dist/core/mcp.js +28 -23
  51. package/dist/core/media-byte-cap.d.ts +25 -0
  52. package/dist/core/media-byte-cap.js +18 -2
  53. package/dist/core/memory-engine/consolidation-driver.js +1 -1
  54. package/dist/core/mode-policy.d.ts +25 -1
  55. package/dist/core/mode-policy.js +28 -13
  56. package/dist/core/own-property-receipt.d.ts +18 -0
  57. package/dist/core/own-property-receipt.js +39 -0
  58. package/dist/core/permission-rule-model.d.ts +32 -11
  59. package/dist/core/permission-rule-model.js +12 -7
  60. package/dist/core/persisted-rule-arms.d.ts +3 -3
  61. package/dist/core/prompt-section-seat.d.ts +54 -0
  62. package/dist/core/prompt-section-seat.js +49 -0
  63. package/dist/core/protocol-http-auth.d.ts +44 -0
  64. package/dist/core/protocol-http-auth.js +45 -0
  65. package/dist/core/read-face-seats.d.ts +30 -0
  66. package/dist/core/read-face-seats.js +38 -0
  67. package/dist/core/redaction-passes.d.ts +82 -0
  68. package/dist/core/redaction-passes.js +378 -0
  69. package/dist/core/remote-env.d.ts +4 -3
  70. package/dist/core/resume-startup-cause.d.ts +56 -0
  71. package/dist/core/resume-startup-cause.js +43 -0
  72. package/dist/core/rule-denial-sentence.d.ts +2 -2
  73. package/dist/core/runner/announce-once-ledger.js +2 -2
  74. package/dist/core/runner/classifier-ladder.d.ts +17 -13
  75. package/dist/core/runner/classifier-ladder.js +12 -10
  76. package/dist/core/runner/construction-doors.js +9 -20
  77. package/dist/core/runner/contracts.d.ts +4 -4
  78. package/dist/core/runner/gate-exit.js +2 -2
  79. package/dist/core/runner/memory-consolidation-write-judge.d.ts +53 -9
  80. package/dist/core/runner/memory-consolidation-write-judge.js +39 -1
  81. package/dist/core/runner/permission-rule-lanes.d.ts +8 -13
  82. package/dist/core/runner/person-path-rows.d.ts +4 -3
  83. package/dist/core/runner/policy-throw.js +4 -4
  84. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -3
  85. package/dist/core/runner/prepare-caps-and-workflow.js +8 -6
  86. package/dist/core/runner/prepare-gate-stations.js +4 -4
  87. package/dist/core/runner/prepare-hands-readface.js +1 -19
  88. package/dist/core/runner/prepare-inherited-gate.js +10 -9
  89. package/dist/core/runner/prepare-policy-chain.d.ts +3 -5
  90. package/dist/core/runner/prepare-policy-chain.js +10 -13
  91. package/dist/core/runner/prepare-project-context.d.ts +4 -4
  92. package/dist/core/runner/prepare-project-context.js +2 -1
  93. package/dist/core/runner/prepare-prompt-assembly.js +1 -0
  94. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -1
  95. package/dist/core/runner/prepare-prompt-inputs.js +4 -4
  96. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -3
  97. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  98. package/dist/core/runner/prepare-task.js +6 -4
  99. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -13
  100. package/dist/core/runner/prepare-turn-wiring.js +8 -7
  101. package/dist/core/runner/run-harness-handlers.js +2 -2
  102. package/dist/core/runner/run-leg.js +10 -9
  103. package/dist/core/runner/run-settle-and-teardown.js +5 -5
  104. package/dist/core/runner/run-turn-boundary.js +3 -2
  105. package/dist/core/runner/runtask.d.ts +1 -2
  106. package/dist/core/runner/runtask.js +8 -12
  107. package/dist/core/runner/session-rule-policy.d.ts +15 -11
  108. package/dist/core/runner/session-rule-policy.js +43 -33
  109. package/dist/core/runner/stream-lifecycle-verbs.js +2 -2
  110. package/dist/core/runner/stream-settle-backstop.js +2 -1
  111. package/dist/core/runner/sub-agent-deny-lists.d.ts +12 -8
  112. package/dist/core/runner/sub-agent-deny-lists.js +23 -12
  113. package/dist/core/runner/terminal-account.js +3 -3
  114. package/dist/core/runner/tool-output-projection.js +3 -8
  115. package/dist/core/runner/tool-result-media-seat.d.ts +19 -0
  116. package/dist/core/runner/tool-result-media-seat.js +13 -0
  117. package/dist/core/runner/turn-attachments.d.ts +13 -12
  118. package/dist/core/runner/turn-attachments.js +11 -7
  119. package/dist/core/runner-deps.d.ts +4 -3
  120. package/dist/core/safety-merge-corpus.js +21 -21
  121. package/dist/core/secret-env.js +1 -1
  122. package/dist/core/session-policy-store.d.ts +89 -72
  123. package/dist/core/session-policy-store.js +100 -93
  124. package/dist/core/shell-read-silence.d.ts +38 -4
  125. package/dist/core/shell-read-silence.js +102 -0
  126. package/dist/core/side-query.d.ts +7 -8
  127. package/dist/core/skill-grant-binding.d.ts +32 -11
  128. package/dist/core/skill-grant-binding.js +11 -3
  129. package/dist/core/skill-grant.d.ts +4 -3
  130. package/dist/core/skill-grant.js +5 -4
  131. package/dist/core/skills-directory.d.ts +12 -1
  132. package/dist/core/skills-directory.js +17 -8
  133. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
  134. package/dist/core/store-contracts/checkpoint-store-contract.js +3 -0
  135. package/dist/core/swappable-deps.d.ts +15 -1
  136. package/dist/core/swappable-deps.js +25 -1
  137. package/dist/core/synthetic-read-verdict.d.ts +16 -14
  138. package/dist/core/synthetic-read-verdict.js +28 -8
  139. package/dist/core/task-notification.d.ts +13 -3
  140. package/dist/core/task-notification.js +12 -1
  141. package/dist/core/task-spec.d.ts +14 -15
  142. package/dist/core/tool-name-readings.d.ts +0 -4
  143. package/dist/core/tool-name-readings.js +0 -1
  144. package/dist/core/tool-policy.d.ts +40 -9
  145. package/dist/core/tool-policy.js +25 -7
  146. package/dist/core/types.d.ts +3 -3
  147. package/dist/core/types.js +1 -1
  148. package/dist/core/untrusted-egress.d.ts +60 -11
  149. package/dist/core/untrusted-egress.js +120 -9
  150. package/dist/core/untrusted-text.js +16 -0
  151. package/dist/core/wiring-manifest.d.ts +22 -8
  152. package/dist/core/wiring-manifest.js +12 -4
  153. package/dist/engine/harness/agent-harness.d.ts +2 -1
  154. package/dist/engine/harness/agent-harness.js +4 -1
  155. package/dist/engine/harness/types.d.ts +3 -0
  156. package/dist/engine/llm/types.d.ts +7 -9
  157. package/dist/engine/loop/agent-loop.d.ts +8 -3
  158. package/dist/engine/loop/agent-loop.js +26 -9
  159. package/dist/engine/loop/types.d.ts +10 -0
  160. package/dist/index.d.ts +17 -5
  161. package/dist/index.js +14 -5
  162. package/dist/orchestration/run-workflow-tool.js +8 -8
  163. package/dist/orchestration/workflow.js +2 -2
  164. package/dist/prompt-assembly/assemble.d.ts +3 -1
  165. package/dist/prompt-assembly/assemble.js +6 -0
  166. package/dist/prompts/cowork.d.ts +42 -0
  167. package/dist/prompts/cowork.js +529 -0
  168. package/dist/prompts/default.d.ts +14 -1
  169. package/dist/prompts/default.js +3 -0
  170. package/dist/stores/file/session-policy-store.d.ts +6 -3
  171. package/dist/stores/file/session-policy-store.js +5 -16
  172. package/dist/tools/artifact/artifact-tool.js +2 -0
  173. package/dist/tools/fs/image-admission.d.ts +57 -4
  174. package/dist/tools/fs/image-admission.js +85 -0
  175. package/dist/tools/fs/path-containment.d.ts +2 -0
  176. package/dist/tools/fs/path-containment.js +5 -0
  177. package/dist/tools/fs/shell-read-check.d.ts +31 -6
  178. package/dist/tools/fs/shell-read-check.js +2 -4
  179. package/dist/tools/fs/shell-read-environment.d.ts +10 -25
  180. package/dist/tools/fs/shell-read-environment.js +6 -18
  181. package/dist/tools/fs/shell-read-establish.d.ts +3 -2
  182. package/dist/tools/fs/shell-read-establish.js +5 -5
  183. package/dist/tools/fs/shell-read-judge.d.ts +38 -8
  184. package/dist/tools/fs/shell-read-judge.js +12 -10
  185. package/dist/tools/web.js +2 -2
  186. package/package.json +1 -1
  187. package/test/export-surface.snapshot.json +94 -10
  188. package/wire-changes/core@7.51.0.json +2421 -0
@@ -32,6 +32,8 @@ export interface PrepareProjectContextInput {
32
32
  session: Pick<StoredSession, "getBranch">;
33
33
  /** borrowed-readonly — the acquired session's id (seam ctx cache key; operator-line context). */
34
34
  sessionId: string;
35
+ /** borrowed-readonly — the run's id (`Prepared.runId`): the seam ctx's `runId`. */
36
+ runId: string;
35
37
  /** borrowed-readonly — `spec.taskId ?? sessionId`: the shared-memory tools' trusted task identity. */
36
38
  hostTaskId: string;
37
39
  /** borrowed-readonly — the FINAL task root (post-restore): the seam's `cwd`. */
@@ -63,13 +65,11 @@ export interface PrepareProjectContextInput {
63
65
  roster: RosterBuilder;
64
66
  /** borrowed-readonly — the frozen task-start tool face; only `exclude` is read (pair yield ①). */
65
67
  toolFaceSnapshot: Pick<ToolFaceSnapshot, "selector">;
66
- /** borrowed-readonly — the once-per-session announcement ledger; only its deduplicated `onError`
67
- * sink is invoked (a pair's mount shape is a session-level fact, announced once). */
68
+ /** borrowed-readonly — the once-per-session ledger; only its deduplicated `onError` sink is invoked (a pair's mount shape is announced once). */
68
69
  onceLedger: AnnounceOnceSink;
69
70
  /** borrowed-readonly — the memory phase's engine-pair ToolSpecs (present iff the pair may mount). */
70
71
  memoryTools: ToolSpec[] | undefined;
71
- /** borrowed-readonly — the memory phase's session seat; only `!== undefined` is read (the
72
- * blocked-mount disclosure fires only when a session mounted). */
72
+ /** borrowed-readonly — the memory phase's session seat; only `!== undefined` is read (the blocked-mount disclosure fires only then). */
73
73
  memoryEngineSession: Prepared["memoryEngineSession"];
74
74
  /** borrowed-readonly — the engine pair's early conjuncts (computed before the memory phase). */
75
75
  memorySearchToolsPlanned: boolean;
@@ -14,7 +14,7 @@ import { RosterBuilder } from "../tool-roster.js";
14
14
  import { mountedToolNames } from "../tool-name-aliases.js";
15
15
  import { asThrownError } from "../thrown-value.js";
16
16
  export async function prepareProjectContext(input) {
17
- const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, allowedTools, allowedToolTables, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget } = input;
17
+ const { deps, spec, internals, session, sessionId, runId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, allowedTools, allowedToolTables, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget } = input;
18
18
  const tools = roster.tools;
19
19
  let memoryBlock = memoryBlockFromEngine;
20
20
  let instructionSources;
@@ -50,6 +50,7 @@ export async function prepareProjectContext(input) {
50
50
  isSubagent: delegation.isNonForkChild,
51
51
  ...(internals?.agentName ? { agentName: internals.agentName } : {}),
52
52
  sessionId,
53
+ runId,
53
54
  phase: projectMemoryPhase,
54
55
  }));
55
56
  }
@@ -65,6 +65,7 @@ export async function preparePromptAssembly(input) {
65
65
  memoryBlock,
66
66
  buildEnvBlock: (date) => buildEnvironmentContext({ ...envFacts, date }) || undefined,
67
67
  date: envFacts.date,
68
+ envFacts,
68
69
  ...(model.promptGuidance && model.promptGuidance.length > 0 ? { modelGuidance: model.promptGuidance.join("\n\n") } : {}),
69
70
  onWarn: (message, phase) => deps.onError?.(new Error(message), { phase, sessionId }),
70
71
  ...(centerDecls !== undefined ? { centerDeclarations: centerDecls } : {}),
@@ -78,7 +78,7 @@ export interface PreparePromptInputsInput {
78
78
  };
79
79
  /** borrowed-readonly — the resolved role; only its `systemPrompt` preset is read (second source). */
80
80
  resolvedRole: Pick<ResolvedRole, "systemPrompt">;
81
- /** borrowed-readonly — the materialized MCP roster; only `serverInstructions` is read (fenced). */
81
+ /** borrowed-readonly — the materialized MCP roster; only `serverInstructions` (the disposed entries) is read. */
82
82
  mcp: Pick<MaterializedMcp, "serverInstructions">;
83
83
  /** borrowed-readonly — the resolved model (id for the env line + the cutoff lookup). */
84
84
  model: Model;
@@ -1,13 +1,13 @@
1
- import { defaultPromptProvider, formatLocalDate, isValidTimeZone } from "../../prompts/default.js";
1
+ import { formatLocalDate, isValidTimeZone, promptProviderInForce } from "../../prompts/default.js";
2
2
  import { isIsolated } from "../remote-env.js";
3
3
  import { readDateAnchor } from "./announce-once-ledger.js";
4
4
  import { probeGitStatusLane } from "./git-status-frame.js";
5
- import { fenceMcpServerInstructions } from "./turn-attachments.js";
5
+ import { renderMcpInstructionsEntry } from "./turn-attachments.js";
6
6
  const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
7
7
  import { compactionEnabled } from "../context-settings.js";
8
8
  export async function preparePromptInputs(input) {
9
9
  const { spec, deps, failClosedReason, lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, session, advertisedWritableDirs } = input;
10
- const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
10
+ const provider = promptProviderInForce(spec.promptProvider, deps.promptProvider);
11
11
  const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
12
12
  const promptHooks = spec.hooks ?? deps.hooks;
13
13
  const promptHooksEnabled = Boolean(promptHooks?.preToolUse || promptHooks?.postToolUse);
@@ -34,7 +34,7 @@ export async function preparePromptInputs(input) {
34
34
  const userSystemPrompt = spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals?.defaultSystemPrompt;
35
35
  const userAppendSystemPrompt = spec.appendSystemPrompt;
36
36
  const mcpInstructionsBlock = mcp.serverInstructions.length
37
- ? `# MCP Server Instructions\n${mcp.serverInstructions.map((s) => fenceMcpServerInstructions(s.server, s.text)).join("\n\n")}`
37
+ ? `# MCP Server Instructions\n${mcp.serverInstructions.map(renderMcpInstructionsEntry).join("\n\n")}`
38
38
  : undefined;
39
39
  const userTz = spec.clientContext?.timeZone;
40
40
  const tzValid = userTz !== undefined && isValidTimeZone(userTz);
@@ -35,12 +35,13 @@ export interface PrepareProtocolToolsInput {
35
35
  * materialize input, the RefreshMcpTools mount predicate, and whether a real client was acquired. */
36
36
  lockedPreflight: Pick<LockedPreflight, "mcp">;
37
37
  /** borrowed-readonly — the REBOUND spec. Read: `principal` (the MCP header / the A2A identity), `tools`
38
- * (the caller names both reserved-name guards check against), `a2a` (the peer entries). Never mutated. */
39
- spec: Pick<TaskSpec, "principal" | "tools" | "a2a">;
38
+ * (the caller names both reserved-name guards check against), `a2a` (the peer entries), `promptProvider` (with
39
+ * `deps.promptProvider`: the provider in force, whose section seat disposes of each server's instructions). */
40
+ spec: Pick<TaskSpec, "principal" | "tools" | "a2a" | "promptProvider">;
40
41
  /** borrowed-readonly — the deployment seats the materializers and their operator faces read: `onElicit`
41
42
  * and `mcpImageResizer` ride into the MCP intake; `onError` takes the A2A warnings (no ledger — a peer is
42
43
  * re-read every prepare); `mcpRevocations` + `onNotice` make the revocation wiring. */
43
- deps: Pick<RunnerDeps, "onElicit" | "mcpImageResizer" | "onError" | "mcpRevocations" | "onNotice">;
44
+ deps: Pick<RunnerDeps, "onElicit" | "mcpImageResizer" | "onError" | "mcpRevocations" | "onNotice" | "promptProvider">;
44
45
  /** borrowed-readonly — the run tree's MCP connection pool (the leg leases its connections from it) and whether THIS leg created it
45
46
  * (then this leg's dispose also closes it). Absent for a leg that declares no server. */
46
47
  mcpPool?: {
@@ -9,6 +9,7 @@ import { REFRESH_MCP_TOOLS_TOOL_NAME, toolFace } from "../tool-catalog-entries.j
9
9
  import { MCP_NAMESPACE, A2A_NAMESPACE } from "../protocol-table.js";
10
10
  import { mintNamespacePrefix } from "../protocol-naming.js";
11
11
  import { applyMcpToolFaces } from "./tool-face-overlay.js";
12
+ import { promptProviderInForce } from "../../prompts/default.js";
12
13
  import { thrownText } from "../thrown-value.js";
13
14
  export async function prepareProtocolTools(input) {
14
15
  const { lockedPreflight, spec, deps, mcpPool, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, toolMarkSources, abortController, rollback } = input;
@@ -24,7 +25,7 @@ export async function prepareProtocolTools(input) {
24
25
  throw Object.assign(new Error(`MCP server name ${JSON.stringify(overPeer.name.slice(0, 40) + "…")} is ${overPeer.name.length} characters; a peer rides every roster row of its tools and the roster's bound on a name is ${TOOL_WIRE_NAME_MAX_CHARS}.`), { code: "config.tool_name_too_long" });
25
26
  }
26
27
  const mcp = lockedPreflight.mcp?.length
27
- ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId), mcpPool === undefined ? undefined : { pool: mcpPool.pool, ownsPool: mcpPool.owned })
28
+ ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId), mcpPool === undefined ? undefined : { pool: mcpPool.pool, ownsPool: mcpPool.owned }, promptProviderInForce(spec.promptProvider, deps.promptProvider))
28
29
  :
29
30
  { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], reconnect: async (server) => ({ server, prefix: mintNamespacePrefix(MCP_NAMESPACE, server), outcome: "not_declared", reason: "this task declared no MCP servers", toolCount: 0, added: [], removed: [] }), dispose: async () => { } };
30
31
  if (lockedPreflight.mcp?.length)
@@ -183,7 +184,7 @@ function mcpRevocationWiring(deps, runId) {
183
184
  return undefined;
184
185
  const ledger = deps.mcpRevocations;
185
186
  return {
186
- isRevoked: (name) => ledger.isRevoked(name),
187
+ isRevoked: (name, mount) => (mount === undefined ? ledger.isRevoked(name) : ledger.isRevoked(name, mount)),
187
188
  onProbeFailure: (e) => deliverEngineNotice(deps.onNotice, {
188
189
  code: "mcp.revocation_probe_failed",
189
190
  message: `the mcpRevocations.isRevoked probe threw — MCP dispatch fails OPEN (no server treated as revoked) until the probe recovers: ${thrownText(e)}`,
@@ -101,6 +101,7 @@ import { rosterCanPersistMemory } from "../tool-registry.js";
101
101
  import { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
102
102
  import { resolveTaskRootCanonical } from "./task-root-canonical.js";
103
103
  import { createStructuredProjector } from "./tool-output-projection.js";
104
+ import { toolResultMediaRefusedSeat } from "./tool-result-media-seat.js";
104
105
  import { asThrownError, thrownText } from "../thrown-value.js";
105
106
  import { createAttachmentDelivery, refuseUserAttachmentPathsWithoutWorkspace, screenUserAttachments } from "../user-attachment.js";
106
107
  function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId, overlay) {
@@ -656,7 +657,7 @@ export async function prepareTask(request, deps, sessions, resume, internals, ru
656
657
  deps.onError?.(asThrownError(err), { phase: "config", sessionId });
657
658
  }
658
659
  }
659
- const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, allowedTools, allowedToolTables, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
660
+ const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, runId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, allowedTools, allowedToolTables, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget: doors.skillListingBudget });
660
661
  const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, skillSpecs, skillSeatOf, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
661
662
  const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, dateAnchor, gitStatusRef } = await preparePromptInputs({ spec, deps, contextOverlay: legContextOverlay(), failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, resolvedWriteFace: resolvedWriteFace?.inForce(), toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical: (await readRoots.ready().then(readRoots)).filter((r) => !additionalRootsCanonical.includes(r)), sessionId, session, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
662
663
  judgeRunToolLists({ spec, tools: roster.tools, declaredPeers: { mcp: lockedPreflight.mcp, a2a: a2a.statuses }, own: { allow: toolFaceLists.allow, exclude: toolFaceLists.exclude }, layers: toolFaceSnapshot.selector.layers, legs: toolLegs, judgedUpstream: toolListsJudged !== undefined, ownWhere: internals?.ownToolListsWhere, onceLedger, sessionId });
@@ -705,6 +706,7 @@ export async function prepareTask(request, deps, sessions, resume, internals, ru
705
706
  const harness = new AgentHarness({
706
707
  abortResultDetails: () => (gateStopRef.terminal !== undefined ? { code: gateStopRef.terminal.code } : parkContaminationMarker({ pausedRef })),
707
708
  onToolInputValidationFault: validateInputFaultNotice(deps.onError, sessionId),
709
+ onToolMediaRefused: toolResultMediaRefusedSeat(deps.onNotice, { sessionId, runId }),
708
710
  ...(spec.limits?.maxOutputTokens !== undefined && spec.limits.maxOutputTokens > 0
709
711
  ? { maxOutputTokens: spec.limits.maxOutputTokens }
710
712
  : {}),
@@ -771,7 +773,7 @@ export async function prepareTask(request, deps, sessions, resume, internals, ru
771
773
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
772
774
  }
773
775
  }
774
- const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, ruleLanesBeyondRun, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight: installedPreflight, pathRuleAnchors: { leg: legAnchors, chainTop }, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, personRows, personRowsBeyondRun, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, readFace: () => resolvedReadFace, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
776
+ const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, allowEntryLanes, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, grantBindingOf, permissionRuleLane, ruleLaneFacts, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight: installedPreflight, pathRuleAnchors: { leg: legAnchors, chainTop }, headLeads, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, personRows, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, skillSpecs, allowedToolTables, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, readFace: () => resolvedReadFace, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
775
777
  const { onAsk, preToolContexts, blockedToolCalls, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, askSeat: doors.askSeat, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, legWriteGuard, mcp, irreversibleTools, egressTools, shellReadStationArmed: handsReadFace.shellReadStationArmed, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox, checkpointScope });
776
778
  const writeProtectionCheck = writeProtection.mount?.check;
777
779
  writeTargetJudgeRef.current = writeProtection.judgeWriteTarget;
@@ -793,9 +795,9 @@ export async function prepareTask(request, deps, sessions, resume, internals, ru
793
795
  const postureFrameRef = { report: (err) => deps.onError?.(err, { phase: "degraded", sessionId }) };
794
796
  const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas: rosterSeat, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
795
797
  const preparedHolder = {};
796
- const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, syntheticReadVerdict, syntheticWriteVerdict, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, readRoots, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants: skillGrantLane, callerPolicy: installedToolPolicy, inheritedPolicies: (inheritedParentConstraints ?? []).map((c) => c.policy), persistedRuleLane: permissionRuleLane, ruleLanesBeyondRun, orgRuleLane: permissionRuleOrgLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
798
+ const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, syntheticReadVerdict, consolidationWriteJudge, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, readRoots, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants: skillGrantLane, callerPolicy: installedToolPolicy, inheritedPolicies: (inheritedParentConstraints ?? []).map((c) => c.policy), persistedRuleLane: permissionRuleLane, ruleLaneFacts, personRowsBeyondRun, orgRuleLane: permissionRuleOrgLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
797
799
  await onceLedger.settle(session, announcedListingsRef);
798
- const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, toolOccurrences, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(attachmentRootCanonical !== undefined ? { handsRoot: attachmentRootCanonical } : {}), ...(stampCallTarget !== undefined ? { stampCallTarget } : {}), toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), exemptRoots: legExemptRoots, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), syntheticWriteVerdict, ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit && withheldDiagnosticsOf ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent, withheldOf: withheldDiagnosticsOf } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(userAttachmentsSeat !== undefined ? { userAttachments: userAttachmentsSeat } : {}), attachmentDelivery: createAttachmentDelivery(), ...(modelFileReaderSeat.current !== undefined ? { modelFileReader: modelFileReaderSeat.current } : {}), syntheticReadVerdict, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
800
+ const buildPrepared = () => ({ harness, session, sessionId, runId, registryAccess: { owner: hostTaskId, scope: taskScope }, reminderMark, reminderDisclosureCounts, fullShellReachable, approvalPosture: { get mode() { return permissionMode.current; }, askEffective: askSeatEffective(wiringManifest) }, ...(outputStyleFrame !== undefined ? { outputStyleFrame } : {}), permissionMode, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, toolOccurrences, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(attachmentRootCanonical !== undefined ? { handsRoot: attachmentRootCanonical } : {}), ...(stampCallTarget !== undefined ? { stampCallTarget } : {}), toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), exemptRoots: legExemptRoots, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, skillGrants: skillGrantLane, skillSpecs, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), consolidationWriteJudge, ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, endModeCeiling, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, stopForDenialLimit, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit && withheldDiagnosticsOf ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent, withheldOf: withheldDiagnosticsOf } } : {}), stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, frameSettlement, ...(userAttachmentsSeat !== undefined ? { userAttachments: userAttachmentsSeat } : {}), attachmentDelivery: createAttachmentDelivery(), ...(modelFileReaderSeat.current !== undefined ? { modelFileReader: modelFileReaderSeat.current } : {}), syntheticReadVerdict, ...(peerLaneRefs.boundaryRound !== undefined ? { peerBoundaryRound: peerLaneRefs.boundaryRound } : {}), gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}), ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) });
799
801
  refuseLateRosterChanges(callerRoster.roster);
800
802
  rollback.commit();
801
803
  commitModeSeats(abortController.signal);
@@ -7,7 +7,9 @@ import { type TurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js"
7
7
  import type { PromptTextDeclaration } from "../../prompts/default.js";
8
8
  import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-detector.js";
9
9
  import type { ToolPolicy } from "../tool-policy.js";
10
- import { type SyntheticReadJudge, type SyntheticWriteJudge } from "../synthetic-read-verdict.js";
10
+ import { type SyntheticReadJudge } from "../synthetic-read-verdict.js";
11
+ import type { ConsolidationWriteJudge } from "../memory-engine/consolidation-driver.js";
12
+ import { type ConsolidationWriteJudgeLeg } from "./memory-consolidation-write-judge.js";
11
13
  import type { RunnerDeps, TaskSpec } from "../types.js";
12
14
  import type { CwdRef } from "../../tools/fs/fs-shared.js";
13
15
  import { type ContainmentFace } from "../../tools/fs/index.js";
@@ -17,8 +19,9 @@ import { type ReadFileState } from "../../tools/fs/safety.js";
17
19
  import type { Prepared } from "./contracts.js";
18
20
  export interface PrepareTurnWiringInput {
19
21
  /** borrowed-readonly — the deployment seats: `cacheBreakDetection` (the detector knob), `onError` (the attachment-skip
20
- * degraded line), `promptSource` (the center candidate's state and current artifact). Receiver preserved. */
21
- deps: Pick<RunnerDeps, "cacheBreakDetection" | "onError" | "promptSource">;
22
+ * degraded line), `promptSource` (the center candidate's state and current artifact), `permissionRuleStore` and
23
+ * `localOwnerRules` (the consolidation write judge's owner of the rows). Receiver preserved. */
24
+ deps: Pick<RunnerDeps, "cacheBreakDetection" | "onError" | "promptSource" | "permissionRuleStore" | "localOwnerRules">;
22
25
  /** borrowed-mutable — the LIVE system prompt (the prompt-assembly phase's accessor cell). Read: the fingerprint, the
23
26
  * snapshot's stable text, the overhead term. Writer here: the center candidate's `apply` (the boundary swap). */
24
27
  systemPromptSeat: {
@@ -46,8 +49,9 @@ export interface PrepareTurnWiringInput {
46
49
  systemBlocks: AssembledPrompt["systemBlocks"];
47
50
  /** borrowed-readonly — the resolved thinking level, or undefined (a snapshot element). */
48
51
  thinking: ThinkingLevel | undefined;
49
- /** borrowed-readonly — the REBOUND spec; only `outputSchema` (presence) and `taskId` (the snapshot's identity). */
50
- spec: Pick<TaskSpec, "outputSchema" | "taskId">;
52
+ /** borrowed-readonly — the REBOUND spec; only `outputSchema` (presence), `taskId` (the snapshot's identity) and `principal`
53
+ * (whose rows the consolidation write judge reads). */
54
+ spec: Pick<TaskSpec, "outputSchema" | "taskId" | "principal">;
51
55
  /** borrowed-readonly — the acquired session id (the snapshot; the attachment-skip attribution). */
52
56
  sessionId: string;
53
57
  /** borrowed-mutable — the snapshot ref the tool-disclosure phase minted. Writers: THIS phase seeds `current`; the candidate's
@@ -84,17 +88,16 @@ export interface PrepareTurnWiringInput {
84
88
  /** borrowed-readonly — the skill lane: the run's skill deny rows (a layer of the judge; only `denying` is read). */
85
89
  skillGrants: Prepared["skillGrants"];
86
90
  /** borrowed-readonly — the deployment's caller policy as resolved through the lock layer, or undefined (a layer of the
87
- * judge, consulted with an aborted signal — never an approver). */
91
+ * judge, asked with a request that states nobody can be asked about it — never an approver). */
88
92
  callerPolicy: ToolPolicy | undefined;
89
93
  /** borrowed-readonly — each live inherited parent policy (a layer of the judge, consulted like the caller policy). */
90
94
  inheritedPolicies: readonly ToolPolicy[];
91
95
  /** borrowed-readonly — the person's persisted-rule lane, or undefined (a layer of the judge). */
92
96
  persistedRuleLane: Prepared["persistedRuleLane"];
93
- /** borrowed-readonly — the person's and the org's lanes over the reader the run's cut does not end (the write face is asked after it). */
94
- ruleLanesBeyondRun: {
95
- readonly personal: Prepared["persistedRuleLane"];
96
- readonly org: Prepared["permissionRuleOrg"];
97
- };
97
+ /** borrowed-readonly — the leg's facts the rule lanes judge against: the consolidation write judge builds its two lanes on them. */
98
+ ruleLaneFacts: ConsolidationWriteJudgeLeg["lanes"];
99
+ /** borrowed-readonly — a reader of the person's rows the run's cut does not end (the consolidation judge is asked after the run). */
100
+ personRowsBeyondRun: ConsolidationWriteJudgeLeg["rows"];
98
101
  /** borrowed-readonly — the org rule lane, or undefined (a layer of the judge). */
99
102
  orgRuleLane: Prepared["permissionRuleOrg"];
100
103
  /** borrowed-readonly — the leg's target-reading stamp, or undefined (hands-less): the visibility judge stamps its request. */
@@ -162,8 +165,8 @@ export interface PrepareTurnWiringResult {
162
165
  /** owned — the run's ONE synthetic-Read deny judge over every layer that can refuse a model's `Read` (the
163
166
  * changed-files door reads it; a face that delivers a file the model did not ask for reads the same one). */
164
167
  syntheticReadVerdict: SyntheticReadJudge;
165
- /** owned — the same layers' WRITE face (`Prepared.syntheticWriteVerdict`): an `ask` is an objection there. */
166
- syntheticWriteVerdict: SyntheticWriteJudge;
168
+ /** owned — the consolidation write judge (`Prepared.consolidationWriteJudge`): the same layers' WRITE face, an `ask` an objection. */
169
+ consolidationWriteJudge: ConsolidationWriteJudge;
167
170
  /** owned — the post-compact background-task listing (`Prepared.listBackgroundTasks`). */
168
171
  listBackgroundTasks: Prepared["listBackgroundTasks"];
169
172
  /** owned — the compaction-boundary center adoption candidate, or undefined (no source, or no adoption). */
@@ -4,7 +4,8 @@ import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
4
4
  import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
5
5
  import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
6
6
  import { defaultTaskRegistry } from "../task-registry.js";
7
- import { createSyntheticReadJudge, createSyntheticWriteJudge } from "../synthetic-read-verdict.js";
7
+ import { createSyntheticReadJudge } from "../synthetic-read-verdict.js";
8
+ import { createConsolidationWriteJudge } from "./memory-consolidation-write-judge.js";
8
9
  import { toolCallFaceOf } from "../tool-roster.js";
9
10
  import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
10
11
  import { decodeTextBytes } from "../../tools/fs/encoding.js";
@@ -14,7 +15,7 @@ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
14
15
  import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
15
16
  import { thrownText } from "../thrown-value.js";
16
17
  export function prepareTurnWiring(input) {
17
- const { deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, readRoots, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants, callerPolicy, inheritedPolicies, persistedRuleLane, ruleLanesBeyondRun, orgRuleLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
18
+ const { deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, readRoots, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, skillGrants, callerPolicy, inheritedPolicies, persistedRuleLane, ruleLaneFacts, personRowsBeyondRun, orgRuleLane, stampCallTarget, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder } = input;
18
19
  const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
19
20
  const cacheFingerprint = cacheBreakDetector
20
21
  ? { systemPrompt: systemPromptSeat.current, tools: toolsToFingerprintInputs(harnessTools) }
@@ -119,10 +120,10 @@ export function prepareTurnWiring(input) {
119
120
  ...(stampCallTarget !== undefined ? { stampCallTarget } : {}),
120
121
  };
121
122
  const syntheticReadVerdict = createSyntheticReadJudge({ ...syntheticLayers, signal: abortController.signal });
122
- const syntheticWriteVerdict = createSyntheticWriteJudge({ ...syntheticLayers, ...(ruleLanesBeyondRun.personal !== undefined ? { persistedRuleLane: ruleLanesBeyondRun.personal } : {}), ...(ruleLanesBeyondRun.org !== undefined ? { orgRuleLane: ruleLanesBeyondRun.org } : {}) }, () => {
123
- const p = preparedHolder.current;
124
- return { face: toolCallFaceOf(p?.toolRoster, "Write"), cwd: p?.cwdRef?.current, root: p?.handsRoot, advertisedWritableDirs: p?.advertisedWritableDirs, exemptRoots: p?.exemptRoots };
125
- });
123
+ const consolidationWriteJudge = createConsolidationWriteJudge({ ruleStoreProvider: deps.permissionRuleStore, principal: spec.principal, localOwnerDeclared: deps.localOwnerRules === true, ...(callerPolicy !== undefined ? { callerPolicy } : {}) }, { rows: personRowsBeyondRun, lanes: ruleLaneFacts, layers: { skillGrants, ...(denyNarrowingPolicy !== undefined ? { denyNarrowingPolicy } : {}), inheritedPolicies, ...(stampCallTarget !== undefined ? { stampCallTarget } : {}) }, facts: () => {
124
+ const p = preparedHolder.current;
125
+ return { face: toolCallFaceOf(p?.toolRoster, "Write"), cwd: p?.cwdRef?.current, root: p?.handsRoot, advertisedWritableDirs: p?.advertisedWritableDirs, exemptRoots: p?.exemptRoots };
126
+ } });
126
127
  const changedFilesReadDenied = async (paths) => {
127
128
  const deadlineAt = Date.now() + ORG_ADJUDICATION_TIMEOUT_MS;
128
129
  return Promise.all(paths.map(async (path) => (await syntheticReadVerdict({ file_path: path }, "changed-files", { deadlineAt })).verdict === "deny"));
@@ -215,5 +216,5 @@ export function prepareTurnWiring(input) {
215
216
  const effectiveReadFaceObserved = carrierReadFace();
216
217
  const effectiveWriteFaceObserved = carrierWriteFace();
217
218
  const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
218
- return { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, syntheticReadVerdict, syntheticWriteVerdict, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved };
219
+ return { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, syntheticReadVerdict, consolidationWriteJudge, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved };
219
220
  }
@@ -3,7 +3,7 @@ import { forwardSafely } from "./forward-sink.js";
3
3
  import { emitsChildRowFrames } from "../delegation-frames.js";
4
4
  import { emitTrace } from "../trace.js";
5
5
  import { primaryActivityArg } from "../arg-summary.js";
6
- import { redactSecrets } from "../untrusted-egress.js";
6
+ import { egressText } from "../untrusted-egress.js";
7
7
  import { readDegradation } from "../../brain/degrading.js";
8
8
  import { OUTPUT_TOOL_NAME } from "./synthetic-tools.js";
9
9
  import { usageCostMicroUsd } from "./usage-accounting.js";
@@ -177,7 +177,7 @@ export function createHarnessHandlers(input) {
177
177
  rs.counters.repetitionCuts += 1;
178
178
  else
179
179
  rs.counters.repetitionSpared += 1;
180
- const segment = redactSecrets(e.segment);
180
+ const segment = egressText("same-principal", e.segment);
181
181
  if (rs.counters.repetitionEvents.length < rs.counters.REPETITION_EVENTS_CAP) {
182
182
  rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment });
183
183
  }
@@ -29,6 +29,7 @@ import { settleExecutionRecord } from "./execution-record.js";
29
29
  import { holdPrompt } from "./prompt-block.js";
30
30
  import { frozenInputSnapshot } from "./gate-exit.js";
31
31
  import { withGateTimeout } from "../gate-bounded-await.js";
32
+ import { readStationAnswer } from "../shell-read-silence.js";
32
33
  import { resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
33
34
  import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
34
35
  import { toImageContent } from "./image.js";
@@ -39,7 +40,8 @@ import { announceToolCall, emitSettled, toolResultMsg } from "./tool-end-body.js
39
40
  import { announceListingTruncation } from "../skill-listing-budget.js";
40
41
  import { attachmentEnvelopeTags, collectDateChange, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingFrame, renderOrphanedBackgroundTasks, renderSkillsListingFrame, stampWriteAnchor } from "./turn-attachments.js";
41
42
  import { flushOwedAnnouncedSnapshot } from "./announce-once-ledger.js";
42
- import { asThrownError, thrownMarksOf, thrownText } from "../thrown-value.js";
43
+ import { asThrownError, thrownMarksOf } from "../thrown-value.js";
44
+ import { egressThrownText, egressValue } from "../untrusted-egress.js";
43
45
  import { ruleSourcePhrase } from "../rule-denial-sentence.js";
44
46
  import { wireOf } from "../tool-name-readings.js";
45
47
  export async function runLeg(input) {
@@ -756,22 +758,21 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
756
758
  });
757
759
  if (judged !== null) {
758
760
  let verdict;
759
- let answered = true;
760
761
  try {
761
762
  verdict = await withGateTimeout(judged, ORG_ADJUDICATION_TIMEOUT_MS, prepared.abortController.signal, "shellReadStation");
762
763
  }
763
764
  catch {
764
- answered = false;
765
765
  verdict = prepared.shellReadCheck.unanswered(readCall, "the read station could not answer on the resumed leg");
766
766
  }
767
- if (!answered || verdict.disposition === "refuse" || (unseen && verdict.disposition !== "vouch")) {
768
- const readDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${verdict.sentence ?? "the read boundary does not vouch for what it reads on this leg"} This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
769
- await settleEnd(true, { content: readDenial, ...(verdict.details !== undefined ? { details: verdict.details } : {}) }, "read_boundary");
767
+ const reading = readStationAnswer(verdict, { toolName: pendingAction.toolName, toolCallId: pendingAction.toolCallId });
768
+ if (reading.kind === "refuse" || reading.kind === "silence" || (unseen && reading.kind !== "vouch")) {
769
+ const readDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${reading.verdict.sentence ?? "the read boundary does not vouch for what it reads on this leg"} This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
770
+ await settleEnd(true, { content: readDenial, ...(reading.verdict.details !== undefined ? { details: reading.verdict.details } : {}) }, "read_boundary");
770
771
  const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, readDenial, true));
771
772
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
772
773
  return;
773
774
  }
774
- verdict.admit?.();
775
+ reading.verdict.admit?.();
775
776
  }
776
777
  }
777
778
  const grantRefusal = parkedSkillApprovalRefusal(pendingAction, executedArgs, prepared.skillSpecs, SKILL_TOOL_NAME, "execution");
@@ -798,8 +799,8 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
798
799
  res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
799
800
  }
800
801
  catch (err) {
801
- const execError = `Error: ${thrownText(err)}`;
802
- const marks = thrownMarksOf(err);
802
+ const execError = `Error: ${egressThrownText("same-principal", err)}`;
803
+ const marks = egressValue("same-principal", thrownMarksOf(err));
803
804
  const failed = await finalize({ result: { content: [{ type: "text", text: execError }], details: marks ?? {} }, isError: true });
804
805
  const failedText = failed.result.content.flatMap((c) => (c.type === "text" ? [c.text] : [])).join("\n");
805
806
  await settleEnd(true, { content: failedText, ...(marks !== undefined ? { details: marks } : {}) });
@@ -5,6 +5,7 @@ import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } fr
5
5
  import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
6
6
  import { hasBackgroundShell, sweepRunBoundaryBackgroundShells } from "../background-shell.js";
7
7
  import { resolveCheckpointStore } from "../checkpoint-store.js";
8
+ import { reopenReasonOf, reopenedUnstartedSentence, resumeStartupCauseOf } from "../resume-startup-cause.js";
8
9
  import { hookSeatExpiredError, runHookSeat } from "../hooks.js";
9
10
  import { modelCostToPricing } from "../pricing.js";
10
11
  import { hasDestroy, isIsolated } from "../remote-env.js";
@@ -22,7 +23,6 @@ import { generatePromptSuggestions } from "./prompt-suggestions.js";
22
23
  import { settleTeardownLeg } from "./teardown-bounded.js";
23
24
  import { owedDateAnchor } from "./announce-once-ledger.js";
24
25
  import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
25
- import { consolidationWriteJudgeOfLeg } from "./memory-consolidation-write-judge.js";
26
26
  import { amendTerminal, terminalProjection } from "./terminal-projection.js";
27
27
  import { mintCompactedEvent } from "../auto-compaction.js";
28
28
  import { asThrownError } from "../thrown-value.js";
@@ -141,10 +141,11 @@ export async function runSettleAndTeardown(input) {
141
141
  result.terminal.kind !== "paused" &&
142
142
  !(resumeDecisionWasNegative(resume) && resume.decisionDelivered === true) &&
143
143
  resume.onEnvRestoreFailed !== undefined) {
144
+ const reopenReason = reopenReasonOf(resumeStartupCauseOf(result.terminal.kind === "failed" ? result.terminal.code : undefined));
144
145
  try {
145
- await resume.onEnvRestoreFailed("env_failed");
146
+ await resume.onEnvRestoreFailed(reopenReason);
146
147
  rowReturned = true;
147
- amendTerminal(result, "resume.reopened_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, so the checkpoint was REOPENED (pending): the same approval can be resumed again, do not ask for a fresh one`);
148
+ amendTerminal(result, "resume.reopened_unstarted", (m) => `${m ?? "the resume leg did not complete"} — ${reopenedUnstartedSentence(reopenReason)}`);
148
149
  }
149
150
  catch {
150
151
  amendTerminal(result, "resume.reopen_failed_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, and reopening the checkpoint FAILED: its state is unprovable from here. Re-read it before deciding; do NOT issue a fresh approval on the assumption the old one is dead`);
@@ -239,8 +240,7 @@ export async function runSettleAndTeardown(input) {
239
240
  await settleTeardownLeg(() => mes.harvest(), "memoryEngineSession.harvest (settle leg)", (e) => runner.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
240
241
  const scope = mes.handle.writeScope;
241
242
  if (scope !== null && autoRunOnRecommendationEffective(runner.deps) && scopeHasUnconsumedRecommendation(mes.engine, scope)) {
242
- const writeJudge = consolidationWriteJudgeOfLeg(prepared.syntheticWriteVerdict);
243
- void autoRunConsolidationFromRecommendation({ deps: runner.deps, engine: mes.engine, scope, sessionId: prepared.sessionId, writeJudge, onNotice: runner.deps.onNotice, onError: runner.deps.onError });
243
+ void autoRunConsolidationFromRecommendation({ deps: runner.deps, engine: mes.engine, scope, sessionId: prepared.sessionId, writeJudge: prepared.consolidationWriteJudge, onNotice: runner.deps.onNotice, onError: runner.deps.onError });
244
244
  }
245
245
  }
246
246
  emitTrace(rs.telemetry.tracer, () => ({
@@ -17,7 +17,8 @@ import { appendBoundaryFrames, compactionRestateOptions } from "./compaction-res
17
17
  import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
18
18
  import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
19
19
  import { contextUsageSections } from "../../prompt-assembly/section-usage.js";
20
- import { asThrownError, thrownText } from "../thrown-value.js";
20
+ import { asThrownError } from "../thrown-value.js";
21
+ import { egressThrownText } from "../untrusted-egress.js";
21
22
  const BATCH_CONTEXT_MIN_KEEP_BYTES = 160;
22
23
  const BATCH_TRUNCATION_MARKER = "\n…[truncated]";
23
24
  export function createTurnBoundary(input) {
@@ -686,7 +687,7 @@ export function createTurnBoundary(input) {
686
687
  else if (!prepared.abortController.signal.aborted) {
687
688
  compactionBreaker.failures += 1;
688
689
  runnerHooks.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
689
- const msg = thrownText(err);
690
+ const msg = egressThrownText("same-principal", err);
690
691
  const failReason = msg.length > 512 ? `${msg.slice(0, 512)}…` : msg;
691
692
  emitTrace(rs.telemetry.tracer, () => ({
692
693
  kind: "compaction.failed",
@@ -101,8 +101,7 @@ export declare class Runner implements RunnerSelfSeat {
101
101
  private acquireSessionLock;
102
102
  /**
103
103
  * Hot-swap the deployment seats a running Runner may change without a restart — the ONE door for every
104
- * hot-swappable seat ({@link SWAPPABLE_DEP_SEATS}: the model catalog generation `models` + its `tiers`
105
- * bindings, and the deployment's two containment-face declarations `readFace` / `writeFace`), and the ONLY sanctioned way to
104
+ * hot-swappable seat (the closed set {@link SWAPPABLE_DEP_SEATS}, each seat's meaning beside it), and the ONLY sanctioned way to
106
105
  * change any of them after construction. It replaces the former `swapModels` verb (retired, no alias):
107
106
  * one verb per seat restated the same laws with a list of verbs as the only record of what is swappable.
108
107
  *
@@ -10,7 +10,7 @@ import { emitTaskOutcome } from "../task-outcome.js";
10
10
  import { authCarrierFingerprint, displayUrlOf, routeKeyOf } from "../../brain/route-adjudicator.js";
11
11
  import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
12
12
  import { expandTiers } from "../roles.js";
13
- import { screenSwappableDeps } from "../swappable-deps.js";
13
+ import { announceReadDenySwap, screenSwappableDeps } from "../swappable-deps.js";
14
14
  import { runSideQuery } from "../side-query.js";
15
15
  import { PushQueue } from "../push-queue.js";
16
16
  import { TtlSessionStore } from "../session-store.js";
@@ -236,19 +236,14 @@ export class Runner {
236
236
  };
237
237
  if (catalogSwap && tiers === undefined)
238
238
  delete written.tiers;
239
- const writtenFaceSeats = written;
240
- for (const seat of faceSeats) {
241
- if (candidate[seat] !== undefined)
242
- writtenFaceSeats[seat] = candidate[seat];
243
- else
244
- delete writtenFaceSeats[seat];
245
- }
246
239
  const priorContextSettings = this.deps.contextSettings;
247
- if (seats.includes("contextSettings")) {
248
- if (candidate.contextSettings !== undefined)
249
- written.contextSettings = candidate.contextSettings;
240
+ for (const seat of seats) {
241
+ if (seat === "models" || seat === "tiers")
242
+ continue;
243
+ if (candidate[seat] !== undefined)
244
+ Object.assign(written, { [seat]: candidate[seat] });
250
245
  else
251
- delete written.contextSettings;
246
+ Reflect.deleteProperty(written, seat);
252
247
  }
253
248
  assertContextSettingsWords(written.contextSettings, written.models, "swapDeps");
254
249
  this.deps = written;
@@ -293,6 +288,7 @@ export class Runner {
293
288
  else
294
289
  deliverEngineNotice(this.deps.onNotice, { code: "config.write_face_swapped", message, detail });
295
290
  }
291
+ announceReadDenySwap(this.deps.onNotice, seats, this.deps);
296
292
  }
297
293
  sideQuery(spec) {
298
294
  return runWithReasoningWireFacts(() => { }, () => runSideQuery(spec, { brain: this.deps.brain, models: this.deps.models, roles: this.deps.roles }));
@@ -1,7 +1,8 @@
1
1
  import type { ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import type { ToolEffect } from "../types.js";
3
- import { type NamedToolPolicy } from "../tool-policy.js";
3
+ import type { NamedToolPolicy } from "../tool-policy.js";
4
4
  import { type SessionPermissionRules, type SessionPolicyStore, type StoredSessionRules } from "../session-policy-store.js";
5
+ import type { PathRuleBases } from "../permission-rule-model.js";
5
6
  /** The FULL path-confinable write-tool set: every catalog tool declaring a write path target — `Write` / `Edit`
6
7
  * (target `file_path`) and `NotebookEdit`, whose write target is `notebook_path` and is extracted by the shared
7
8
  * tool-aware {@link writeTargetPath} (notebook_path outranks a decoy `file_path` for that tool).
@@ -105,19 +106,21 @@ export declare function createLiveSessionRules(store: SessionPolicyStore, floor:
105
106
  }): (toolCallId: string, signal?: AbortSignal) => Promise<SessionPermissionRules>;
106
107
  /**
107
108
  * Build the per-session deny-narrowing policy. For each tool call:
108
- * - `toolDeny` → deny the listed tools.
109
- * - `toolAllow` (if set) → deny any tool NOT listed (a narrowing allowlist).
110
- * - `commandAllow`/`commandDeny` → delegate to {@link createCoarseCommandNamePolicy} (argv[0] names; only
111
- * speaks about shell tools) with `defaultAction:"deny"` in BOTH modes — so this policy structurally
112
- * emits no ask. Allowlist mode denies the un-listed and the un-parseable; blocklist mode denies the
113
- * listed and the un-parseable (a parseable command absent from the deny list stays allowed).
109
+ * - `toolDeny` → deny the calls its entries reach — the one list matcher's deny reading (`judgeAllowEntries`): a tool name
110
+ * under any name the call answers to, a protocol namespace, a shell command rule over every program the command line runs
111
+ * (a line it cannot read against the rule is refused too), a path rule over every view of the call's target.
112
+ * - `toolAllow` (if set) → deny any call no entry admits (the matcher's allow reading in the `narrowing` posture: every
113
+ * segment of a command line named by an entry; never the allow layer's auto-mode strip), naming the uncovered segment.
114
+ * - an entry that reads as nothing → in no row (it guards and grants nothing; the prepare-time audit names it).
115
+ * - a snapshot the shape reading refuses → deny every call, saying what it holds (never its text).
114
116
  * - `allowDirs` (if set) → a WRITE tool must land inside one of the dirs (resolved via `canonicalizeTarget`,
115
117
  * so a symlink can't carry across a write out); a write-capable tool that cannot be path-confined (e.g. `bash`)
116
118
  * is DENIED while `allowDirs` is set (fail-closed).
117
- * Returns `allow` when nothing constrains the call. `env`/`rootPath` are the task's real exec env + root, so
118
- * matching sees the actual filesystem (a remote/E2B task resolves in its container). A relative
119
- * TARGET prefers the call's live tracked cwd ({@link ToolCallRequest.cwd}) when the engine stamps one;
120
- * `allowDirs` entries always resolve against `rootPath`.
119
+ * The policy structurally emits no ask. Returns `allow` when nothing constrains the call. `env`/`rootPath` are the task's real
120
+ * exec env + root, so matching sees the actual filesystem (a remote/E2B task resolves in its container). A relative TARGET
121
+ * prefers the call's live tracked cwd ({@link ToolCallRequest.cwd}) when the engine stamps one; `allowDirs` entries always
122
+ * resolve against `rootPath`, and a path rule's pattern against `anchors` (the leg's root and home — absent, `rootPath`
123
+ * alone, so a home-relative pattern is unreadable and refuses in the deny direction).
121
124
  *
122
125
  * The rules are taken from a {@link SessionRuleSource} at EVERY call
123
126
  * ({@link SessionRuleSource} carries the law); a plain rules object is the constant source, which is what
@@ -127,4 +130,5 @@ export declare function createSessionRulePolicy(rules: SessionPermissionRules |
127
130
  env: ExecutionEnv;
128
131
  rootPath?: string;
129
132
  toolEffects?: ReadonlyMap<string, ToolEffect>;
133
+ anchors?: PathRuleBases;
130
134
  }): NamedToolPolicy;