@sema-agent/core 7.21.1 → 7.23.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 (110) hide show
  1. package/CHANGELOG.md +290 -1
  2. package/dist/agents/send-message-tool.js +1 -0
  3. package/dist/agents/subagent.d.ts +1 -0
  4. package/dist/agents/subagent.js +2 -1
  5. package/dist/core/ask-origin.d.ts +1 -0
  6. package/dist/core/ask-origin.js +3 -2
  7. package/dist/core/auto-compaction.d.ts +2 -2
  8. package/dist/core/auto-mode-rebuild.js +6 -2
  9. package/dist/core/auto-mode.d.ts +40 -18
  10. package/dist/core/auto-mode.js +63 -45
  11. package/dist/core/checkpoint-store.d.ts +9 -13
  12. package/dist/core/context-edit.js +1 -1
  13. package/dist/core/engine-notice.d.ts +62 -1
  14. package/dist/core/engine-notice.js +14 -1
  15. package/dist/core/gate-bounded-await.d.ts +13 -3
  16. package/dist/core/gate-fold.js +3 -3
  17. package/dist/core/governance-codes.d.ts +1 -1
  18. package/dist/core/governance-codes.js +6 -0
  19. package/dist/core/mcp.d.ts +65 -0
  20. package/dist/core/mcp.js +190 -41
  21. package/dist/core/memory-engine/consolidation.d.ts +5 -0
  22. package/dist/core/memory-engine/consolidation.js +7 -3
  23. package/dist/core/memory-engine/engine.d.ts +8 -0
  24. package/dist/core/memory-engine/engine.js +25 -19
  25. package/dist/core/memory-engine/layout.d.ts +47 -9
  26. package/dist/core/memory-engine/layout.js +157 -57
  27. package/dist/core/memory-engine/session-incarnation.d.ts +27 -0
  28. package/dist/core/memory-engine/session-incarnation.js +63 -0
  29. package/dist/core/parent-spec-seat.d.ts +15 -3
  30. package/dist/core/parent-spec-seat.js +5 -0
  31. package/dist/core/park-selfcheck.js +1 -1
  32. package/dist/core/path-identity.d.ts +11 -4
  33. package/dist/core/path-identity.js +5 -2
  34. package/dist/core/permission-rule-model.d.ts +1 -2
  35. package/dist/core/read-root-candidate.js +11 -1
  36. package/dist/core/retention.d.ts +8 -1
  37. package/dist/core/retention.js +2 -0
  38. package/dist/core/runner/assemble-result.d.ts +2 -7
  39. package/dist/core/runner/assemble-result.js +25 -8
  40. package/dist/core/runner/classifier-ladder.js +1 -0
  41. package/dist/core/runner/contracts.d.ts +26 -15
  42. package/dist/core/runner/inherited-ask-grants.d.ts +2 -7
  43. package/dist/core/runner/inherited-ask-grants.js +1 -0
  44. package/dist/core/runner/mcp-redial.d.ts +109 -0
  45. package/dist/core/runner/mcp-redial.js +60 -0
  46. package/dist/core/runner/memory-consolidation-driver.d.ts +2 -0
  47. package/dist/core/runner/prepare-ask-lane.js +1 -0
  48. package/dist/core/runner/prepare-caps-and-workflow.js +9 -12
  49. package/dist/core/runner/prepare-context-lane.d.ts +43 -1
  50. package/dist/core/runner/prepare-context-lane.js +25 -4
  51. package/dist/core/runner/prepare-hands-readface.d.ts +3 -0
  52. package/dist/core/runner/prepare-hands-readface.js +13 -3
  53. package/dist/core/runner/prepare-memory.js +3 -3
  54. package/dist/core/runner/prepare-protocol-tools.d.ts +6 -1
  55. package/dist/core/runner/prepare-protocol-tools.js +20 -38
  56. package/dist/core/runner/prepare-task.js +4 -4
  57. package/dist/core/runner/prepare-turn-wiring.js +2 -2
  58. package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -0
  59. package/dist/core/runner/prepare-wiring-manifest.js +2 -1
  60. package/dist/core/runner/run-harness-handlers.js +4 -2
  61. package/dist/core/runner/run-identity-wiring.d.ts +3 -1
  62. package/dist/core/runner/run-identity-wiring.js +4 -1
  63. package/dist/core/runner/run-recovery-lanes.js +6 -3
  64. package/dist/core/runner/run-settle-and-teardown.d.ts +3 -1
  65. package/dist/core/runner/run-settle-and-teardown.js +6 -2
  66. package/dist/core/runner/run-telemetry-and-budget-seats.js +11 -1
  67. package/dist/core/runner/run-terminal-adoption.d.ts +4 -1
  68. package/dist/core/runner/run-terminal-adoption.js +2 -6
  69. package/dist/core/runner/runtask.js +8 -5
  70. package/dist/core/runner/stream-lifecycle-verbs.d.ts +5 -1
  71. package/dist/core/runner/stream-lifecycle-verbs.js +23 -2
  72. package/dist/core/runner/write-protection-station.d.ts +7 -25
  73. package/dist/core/runner/write-protection-station.js +18 -0
  74. package/dist/core/runner-deps.d.ts +16 -7
  75. package/dist/core/skill-listing-budget.d.ts +5 -4
  76. package/dist/core/task-registry.d.ts +0 -6
  77. package/dist/core/task-result.d.ts +31 -12
  78. package/dist/core/task-spec.d.ts +2 -1
  79. package/dist/core/task-stream.d.ts +26 -0
  80. package/dist/core/tool-spec.d.ts +5 -1
  81. package/dist/core/trace.d.ts +5 -0
  82. package/dist/core/types.d.ts +3 -3
  83. package/dist/core/types.js +1 -1
  84. package/dist/core/wiring-manifest.d.ts +23 -0
  85. package/dist/core/wiring-manifest.js +3 -0
  86. package/dist/core/write-protect.d.ts +64 -12
  87. package/dist/core/write-protect.js +88 -34
  88. package/dist/engine/execution-env/node-execution-env.d.ts +19 -2
  89. package/dist/engine/execution-env/node-execution-env.js +5 -1
  90. package/dist/index.d.ts +5 -4
  91. package/dist/index.js +2 -2
  92. package/dist/orchestration/workflow-governance.js +1 -1
  93. package/dist/orchestration/workflow-primitives.d.ts +2 -2
  94. package/dist/tools/fs/bash-program-position.d.ts +26 -5
  95. package/dist/tools/fs/bash-program-position.js +63 -16
  96. package/dist/tools/fs/bash-readonly-classifier.d.ts +27 -17
  97. package/dist/tools/fs/bash-readonly-classifier.js +134 -55
  98. package/dist/tools/fs/fs-bash.js +43 -18
  99. package/dist/tools/fs/fs-read.js +2 -1
  100. package/dist/tools/fs/fs-shared.d.ts +33 -7
  101. package/dist/tools/fs/fs-shared.js +12 -1
  102. package/dist/tools/fs/index.d.ts +4 -2
  103. package/dist/tools/fs/read-deny.d.ts +25 -11
  104. package/dist/tools/fs/read-deny.js +5 -1
  105. package/dist/tools/fs/safety.d.ts +38 -4
  106. package/dist/tools/fs/safety.js +31 -3
  107. package/package.json +2 -2
  108. package/test/export-surface.snapshot.json +11 -1
  109. package/wire-changes/core@7.22.0.json +637 -0
  110. package/wire-changes/core@7.23.0.json +316 -0
@@ -7,8 +7,9 @@ import { createOffloadPersist } from "../tool-result-store.js";
7
7
  import { emitTrace } from "../trace.js";
8
8
  import { deliverEngineNotice } from "../types.js";
9
9
  import { applyGitFrameGuard } from "./git-status-frame.js";
10
+ import { applyContextShrinkToReadFileState } from "../../tools/fs/index.js";
10
11
  export function prepareContextLane(input) {
11
- const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef } = input;
12
+ const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, readFileState } = input;
12
13
  const editAt = editBudget(model);
13
14
  const guardAt = guardBudget(model);
14
15
  const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
@@ -57,12 +58,13 @@ export function prepareContextLane(input) {
57
58
  : {}),
58
59
  });
59
60
  if (ccPass !== undefined) {
60
- recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: hostTaskId, trigger: "frontier" });
61
+ recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: hostTaskId, trigger: "frontier", ...(readFileState !== undefined ? { readFileState } : {}) });
61
62
  }
62
63
  const chain = await runGuardChain({
63
64
  edited, replayed, index: replay.index, microCompact, guardAt, charsPerToken, offloadStore,
64
65
  sessionId, taskId: hostTaskId, deps, trimPressureRef, recheck, signal,
65
66
  pendingProjectionObservations,
67
+ ...(readFileState !== undefined ? { readFileState } : {}),
66
68
  });
67
69
  if ("earlyReturn" in chain) {
68
70
  return chain.earlyReturn;
@@ -138,15 +140,34 @@ function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, kno
138
140
  ...(offloadStore ? { offloadPersist: createOffloadPersist(offloadStore, sessionId, deps.onNotice) } : {}),
139
141
  };
140
142
  }
143
+ export function landStaleResultClears(args) {
144
+ const clearedToolCallIds = [];
145
+ for (const c of args.cleared) {
146
+ args.ledger.entries.set(c.key, { marker: c.marker, groupCount: c.groupCount, fp: c.fp });
147
+ if (c.toolCallId !== undefined)
148
+ clearedToolCallIds.push(c.toolCallId);
149
+ }
150
+ if (args.readFileState !== undefined) {
151
+ applyContextShrinkToReadFileState(args.readFileState, { kind: "tool-results-cleared", clearedToolCallIds });
152
+ }
153
+ }
141
154
  function recordFrontierClears(args) {
155
+ const landed = [];
142
156
  for (const { index } of args.pass.clears) {
143
157
  const at = args.index.keyAt(index);
144
158
  const clearedMsg = args.edited[index];
145
159
  const markerText = clearedMsg.content?.[0]?.text;
146
160
  if (at !== undefined && typeof markerText === "string") {
147
- args.ledger.entries.set(at.key, { marker: markerText, groupCount: at.groupCount, fp: at.fp });
161
+ landed.push({
162
+ key: at.key,
163
+ marker: markerText,
164
+ groupCount: at.groupCount,
165
+ fp: at.fp,
166
+ ...(typeof clearedMsg.toolCallId === "string" ? { toolCallId: clearedMsg.toolCallId } : {}),
167
+ });
148
168
  }
149
169
  }
170
+ landStaleResultClears({ ledger: args.ledger, cleared: landed, ...(args.readFileState !== undefined ? { readFileState: args.readFileState } : {}) });
150
171
  emitTrace(args.tracer, () => ({
151
172
  kind: "context.mc_clear",
152
173
  version: 1,
@@ -198,7 +219,7 @@ export async function runGuardChain(args) {
198
219
  ...(args.offloadStore ? { offload: { persist: createOffloadPersist(args.offloadStore, args.sessionId, deps.onNotice) } } : {}),
199
220
  });
200
221
  if (blockingPass !== undefined) {
201
- recordFrontierClears({ pass: blockingPass, index: args.index, edited: blockingEdited, ledger: microCompact.ledger, tracer: deps.tracer, taskId, trigger: "blocking" });
222
+ recordFrontierClears({ pass: blockingPass, index: args.index, edited: blockingEdited, ledger: microCompact.ledger, tracer: deps.tracer, taskId, trigger: "blocking", ...(args.readFileState !== undefined ? { readFileState: args.readFileState } : {}) });
202
223
  working = blockingEdited;
203
224
  anchoredWorking = estimateContextTokens(working, charsPerToken).tokens;
204
225
  }
@@ -284,6 +284,9 @@ export interface PrepareHandsReadFaceResult {
284
284
  pattern: string;
285
285
  caseSensitive: boolean;
286
286
  }>;
287
+ /** owned — the built-in deny TIERS this leg MATERIALLY activated, off the same snapshot the compile
288
+ * judged with; empty ⇒ none. Relayed to the wiring manifest, never re-derived from `deps` there. */
289
+ readDenyBuiltinTiersActive: readonly string[];
287
290
  /** write-back (D-3 hold) — the hands-LESS legs' resolved face (the read posture is a
288
291
  * property of the RUN, not the mount). Driver assigns onto its outer `let`. */
289
292
  handsLessResolvedFace: ContainmentFace | undefined;
@@ -3,7 +3,7 @@ import { StubExecutionEnv } from "../stub-env.js";
3
3
  import { hasBackgroundShell } from "../background-shell.js";
4
4
  import { boundaryIsTheSandbox } from "../remote-env.js";
5
5
  import { defaultTaskRegistry } from "../task-registry.js";
6
- import { bashReadBoundaryProbe, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, resolveReadDenyBuiltins, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
6
+ import { bashReadBoundaryProbe, bashReversibilityProbe, activeReadDenyBuiltinTiers, compileReadDeny, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
7
7
  import { resolveKey, sha256 } from "../../tools/fs/safety.js";
8
8
  import { normalizeFileText } from "../../tools/fs/encoding.js";
9
9
  import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
@@ -98,6 +98,7 @@ export function resolveHandsLessReadFace(input) {
98
98
  let handsLessResolvedFace;
99
99
  let handsLessResolvedWriteFace;
100
100
  let readDenyAdditionsNormalized = [];
101
+ let readDenyBuiltinTiersActive = [];
101
102
  {
102
103
  const liveFace = resolveContainmentFace({
103
104
  kind: "read",
@@ -120,7 +121,7 @@ export function resolveHandsLessReadFace(input) {
120
121
  const seedWrite = resume?.seed.writeFace;
121
122
  handsLessResolvedWriteFace = resume !== undefined && (seedWrite === undefined || seedWrite.face === "roots") ? "roots" : liveWriteFace;
122
123
  const denyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seed?.denyEntries ?? [])];
123
- resolveReadDenyBuiltins({
124
+ readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers({
124
125
  ...(deps.readDenyBuiltinTiers !== undefined ? { tiers: deps.readDenyBuiltinTiers } : {}),
125
126
  ...(deps.readDenyBuiltinExclude !== undefined ? { exclude: deps.readDenyBuiltinExclude } : {}),
126
127
  });
@@ -139,6 +140,7 @@ export function resolveHandsLessReadFace(input) {
139
140
  resolvedReadFace: undefined,
140
141
  resolvedWriteFace: undefined,
141
142
  readDenyAdditionsNormalized,
143
+ readDenyBuiltinTiersActive,
142
144
  handsLessResolvedFace,
143
145
  handsLessResolvedWriteFace,
144
146
  additionalRootsCanonical: [],
@@ -168,6 +170,7 @@ export async function prepareHandsMount(input) {
168
170
  let resolvedReadFace;
169
171
  let resolvedWriteFace;
170
172
  let readDenyAdditionsNormalized = [];
173
+ let readDenyBuiltinTiersActive = [];
171
174
  let handsLessResolvedFace;
172
175
  const handsLessResolvedWriteFace = undefined;
173
176
  const shellGatedTools = new Set();
@@ -245,6 +248,7 @@ export async function prepareHandsMount(input) {
245
248
  ...(deps.readDenyBuiltinExclude !== undefined ? { exclude: [...deps.readDenyBuiltinExclude] } : {}),
246
249
  };
247
250
  readDenyMatcher = compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg);
251
+ readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers(readDenyBuiltinCfg);
248
252
  readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
249
253
  let liveReadFace = resolveContainmentFace({
250
254
  kind: "read",
@@ -323,11 +327,16 @@ export async function prepareHandsMount(input) {
323
327
  sessionSeats.reanchor(sessionId, readFileState, ownCommittedTailEntryId);
324
328
  }
325
329
  : undefined;
330
+ const compiledDeny = readDenyMatcher;
331
+ const denySeatJudge = compiledDeny !== undefined && compiledDeny.entries.length > 0
332
+ ? (p) => compiledDeny.matchPath(p)?.pattern ?? null
333
+ : undefined;
326
334
  const shellReadBoundary = () => ({
327
335
  roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
328
336
  ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
329
337
  ...(executionEnv.homeDir !== undefined ? { homeDir: executionEnv.homeDir } : {}),
330
- denyMatch: (p) => readDenyMatcher?.matchPath(p)?.pattern ?? null,
338
+ ...(denySeatJudge !== undefined ? { denyMatch: denySeatJudge } : {}),
339
+ denyEntries: (compiledDeny?.entries ?? []).map((e) => e.pattern),
331
340
  ...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
332
341
  });
333
342
  const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
@@ -421,6 +430,7 @@ export async function prepareHandsMount(input) {
421
430
  resolvedWriteFace,
422
431
  handsLessResolvedWriteFace,
423
432
  readDenyAdditionsNormalized,
433
+ readDenyBuiltinTiersActive,
424
434
  handsLessResolvedFace,
425
435
  additionalRootsCanonical,
426
436
  additionalReadRootsCanonical,
@@ -171,7 +171,7 @@ export async function prepareMemory(input) {
171
171
  const choosePersonalBackend = () => typeof pinned === "string" && pinned ? new FileMemoryEngineBackend(personalMemoryDir, { controlDir: personalControlDir }) : backend;
172
172
  const createPersonalEngine = (personalBackend) => {
173
173
  return {
174
- engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident, provenance: input.memoryProvenance, ...captureStoreOpt(personalBackend, personalControlDir), ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}) }),
174
+ engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident, provenance: input.memoryProvenance, ...captureStoreOpt(personalBackend, personalControlDir), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}), ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}) }),
175
175
  backend: personalBackend,
176
176
  };
177
177
  };
@@ -359,7 +359,7 @@ export async function prepareMemory(input) {
359
359
  controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
360
360
  onIncident: onEngineIncident,
361
361
  provenance: input.memoryProvenance,
362
- ...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
362
+ ...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}),
363
363
  ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
364
364
  });
365
365
  const personal = createPersonalEngine(personalBackendChosen);
@@ -439,7 +439,7 @@ export async function prepareMemory(input) {
439
439
  controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
440
440
  onIncident: onEngineIncident,
441
441
  provenance: input.memoryProvenance,
442
- ...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
442
+ ...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)), ...(deps.memorySessionIncarnation !== undefined ? { sessionIncarnation: deps.memorySessionIncarnation } : {}),
443
443
  ...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
444
444
  });
445
445
  await armCaptureOptOut(engine);
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "../../internal/harness.js";
2
- import { type MaterializedMcp, type McpDroppedTool } from "../mcp.js";
2
+ import { type MaterializedMcp, type McpDroppedTool, type McpReconnectResult } from "../mcp.js";
3
3
  import { type MaterializedA2a } from "../a2a.js";
4
4
  import type { LockedPreflight } from "../locked-config.js";
5
5
  import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
@@ -90,6 +90,11 @@ export interface PrepareProtocolToolsResult {
90
90
  toolCallGateArmedRef: {
91
91
  armed: boolean;
92
92
  };
93
+ /** owned — the run handle's `reconnectMcpServer` implementation, present ONLY when this leg declared
94
+ * MCP servers (a leg with none has nothing to re-dial, and the stream verb refuses typed rather
95
+ * than answering about a materialization that was never made). The driver projects it onto
96
+ * `Prepared.mcpRedial`, from which the run's wiring binds the verb's seat. */
97
+ mcpRedial?: (server: string) => Promise<McpReconnectResult>;
93
98
  }
94
99
  /** The M5 phase body — prepareTask's protocol-tools stretch, verbatim (see the module header). */
95
100
  export declare function prepareProtocolTools(input: PrepareProtocolToolsInput): Promise<PrepareProtocolToolsResult>;
@@ -1,5 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  import { materializeMcpTools } from "../mcp.js";
3
+ import { applyMcpRosterDelta, commitMcpRosterChanges, redialMcpServer } from "./mcp-redial.js";
3
4
  import { materializeA2aTools } from "../a2a.js";
4
5
  import { deliverEngineNotice } from "../types.js";
5
6
  import { RosterBuilder } from "../tool-roster.js";
@@ -16,7 +17,8 @@ export async function prepareProtocolTools(input) {
16
17
  }
17
18
  const mcp = lockedPreflight.mcp?.length
18
19
  ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
19
- : { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
20
+ :
21
+ { 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 () => { } };
20
22
  if (lockedPreflight.mcp?.length)
21
23
  rollback.push(function mcpDispose() { return mcp.dispose(); });
22
24
  for (const w of mcp.warnings)
@@ -58,6 +60,10 @@ export async function prepareProtocolTools(input) {
58
60
  const rebuildHarnessToolsRef = {};
59
61
  const contentOriginWrapRef = {};
60
62
  const toolCallGateArmedRef = { armed: false };
63
+ const rosterApply = { toolFaceSnapshot, withFaces, remoteToolOffload, foldProtocolAxes: (axes, label) => foldProtocolAxes(axes, label), toolEffects, axisExplicitNegatives, roster };
64
+ const rosterCommit = { rebuildHarnessToolsRef, rosterSeat, toolCallGateArmedRef, irreversibleTools, egressTools };
65
+ const redialInput = { ...rosterApply, ...rosterCommit, mcp, deps, sessionId, runId, irreversibilityTier };
66
+ const mcpRedial = lockedPreflight.mcp?.length ? (server) => redialMcpServer(redialInput, server) : undefined;
61
67
  if (lockedPreflight.mcp?.length) {
62
68
  roster.mount({
63
69
  ...toolFace("refresh-mcp-tools"),
@@ -73,42 +79,20 @@ export async function prepareProtocolTools(input) {
73
79
  const lines = [];
74
80
  let changed = false;
75
81
  let anyActiveFailure = false;
82
+ const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
76
83
  for (const r of results) {
77
- if (r.status !== "refreshed" || r.tools === undefined) {
84
+ if (r.tools === undefined) {
78
85
  lines.push(`${r.server}: ${r.status}${r.error !== undefined ? ` (${r.error})` : ""}`);
79
86
  if (r.status === "failed")
80
87
  anyActiveFailure = true;
81
88
  continue;
82
89
  }
83
- const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
84
- const pushable = r.tools.filter((t) => !excludedSet.has(t.name));
85
- const excludedNow = r.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
86
- const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(r.prefix)));
87
- const restoreDomain = (m, snap) => {
88
- for (const name of [...m.keys()])
89
- if (name.startsWith(r.prefix))
90
- m.delete(name);
91
- for (const [name, v] of snap)
92
- m.set(name, v);
93
- };
94
- const priorDomainEffects = domainSnapshot(toolEffects);
95
- const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
96
- for (const name of priorDomainEffects.keys())
97
- toolEffects.delete(name);
98
- for (const name of priorDomainNegatives.keys())
99
- axisExplicitNegatives.delete(name);
100
- try {
101
- foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
102
- }
103
- catch (foldErr) {
104
- restoreDomain(toolEffects, priorDomainEffects);
105
- restoreDomain(axisExplicitNegatives, priorDomainNegatives);
106
- lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
90
+ const applied = applyMcpRosterDelta(rosterApply, { server: r.server, prefix: r.prefix, tools: r.tools, axes: r.axes ?? [], dropped: r.dropped ?? [] });
91
+ if (!applied.applied) {
92
+ lines.push(`${r.server}: failed (${applied.foldError})`);
107
93
  anyActiveFailure = true;
108
94
  continue;
109
95
  }
110
- const refreshedMounts = withFaces(pushable, r.server).map((t) => remoteToolOffload(t));
111
- roster.replaceDomain(r.prefix, refreshedMounts, { source: "mcp", mountedBy: "optional", origin: { peer: r.server } });
112
96
  changed = true;
113
97
  const detail = [];
114
98
  const shownAdded = r.added.filter((n) => !excludedSet.has(n));
@@ -116,18 +100,16 @@ export async function prepareProtocolTools(input) {
116
100
  detail.push(`added: ${shownAdded.join(", ")}`);
117
101
  if (r.removed.length > 0)
118
102
  detail.push(`removed: ${r.removed.join(", ")}`);
119
- if (excludedNow.length > 0)
120
- detail.push(`excluded by deployment config (not mounted): ${excludedNow.join(", ")}`);
103
+ if (applied.excluded.length > 0)
104
+ detail.push(`excluded by deployment config (not mounted): ${applied.excluded.join(", ")}`);
121
105
  if ((r.dropped?.length ?? 0) > 0)
122
106
  detail.push(`dropped: ${r.dropped.map((d) => `${d.tool} (${d.reason.length > 90 ? `${d.reason.slice(0, 90)}…` : d.reason})`).join("; ")}`);
123
- lines.push(`${r.server}: refreshed — ${pushable.length} tool${pushable.length === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
107
+ lines.push(`${r.server}: refreshed — ${applied.pushed} tool${applied.pushed === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
124
108
  }
125
- if (changed)
126
- await rebuildHarnessToolsRef.current?.();
127
- if (changed)
128
- rosterSeat.republish();
129
- if (changed && !toolCallGateArmedRef.armed && (irreversibleTools.size > 0 || egressTools.size > 0)) {
130
- lines.push("WARNING: refreshed tools declare irreversible/egress safety hints, but this task started with no approval gate registered (no policy/hooks and no gated tools at start) — these hints cannot arm a gate mid-task (RB-46); the refreshed tools run ungated on this deployment shape.");
109
+ if (changed) {
110
+ const ungated = await commitMcpRosterChanges(rosterCommit);
111
+ if (ungated !== undefined)
112
+ lines.push(ungated);
131
113
  }
132
114
  const text = lines.length === 0 ? "No connected MCP servers to refresh." : lines.join("\n");
133
115
  return {
@@ -184,7 +166,7 @@ export async function prepareProtocolTools(input) {
184
166
  for (const t of a2a.tools)
185
167
  roster.mount(remoteToolOffload(t), { source: "a2a", mountedBy: "optional", origin: { peer: A2A_NAMESPACE.parse(t.name)?.peer ?? t.name } });
186
168
  foldProtocolAxes(a2a.toolAxes, "A2A");
187
- return { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef };
169
+ return { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, ...(mcpRedial !== undefined ? { mcpRedial } : {}) };
188
170
  }
189
171
  function mcpRevocationWiring(deps, runId) {
190
172
  if (deps.mcpRevocations === undefined)
@@ -436,7 +436,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
436
436
  roster.mount(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined), { source: "synthetic", mountedBy: "optional" });
437
437
  }
438
438
  const rosterSeat = new ToolRosterDeltaSeat();
439
- const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
439
+ const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef, mcpRedial } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
440
440
  const callIssuedAtRef = {};
441
441
  const workCutRef = { continuation: false, abortedBeforeTurnEnd: false };
442
442
  const advertisedWritableDirs = handsEnabled
@@ -690,7 +690,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
690
690
  }
691
691
  }
692
692
  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, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
693
- const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined });
693
+ const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtectionCheck, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined });
694
694
  if (fileHistoryBoundary !== undefined) {
695
695
  harness.on("tool_call", async () => {
696
696
  await fileHistoryBoundary.settle();
@@ -702,11 +702,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
702
702
  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, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
703
703
  const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
704
704
  prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
705
- const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef });
705
+ const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
706
706
  const preparedHolder = {};
707
707
  const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, 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, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
708
708
  await onceLedger.settle(session, announcedListingsRef);
709
- 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, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, 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 ? { 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, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
709
+ const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, 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 ? { 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, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
710
710
  rollback.commit();
711
711
  const prepared = buildPrepared();
712
712
  preparedHolder.current = prepared;
@@ -6,7 +6,7 @@ import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-det
6
6
  import { defaultTaskRegistry } from "../task-registry.js";
7
7
  import { refuseOutOfContractDecision } from "../tool-policy.js";
8
8
  import { decodeTextBytes } from "../../tools/fs/encoding.js";
9
- import { applyCompactionToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
9
+ import { applyContextShrinkToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
10
10
  import { resolveKey } from "../../tools/fs/safety.js";
11
11
  import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
12
12
  import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
@@ -98,7 +98,7 @@ export function prepareTurnWiring(input) {
98
98
  .map(([path]) => path)
99
99
  : undefined;
100
100
  const onCompactionApplied = handsEnabled && readFileStateForCheckpoint
101
- ? (attachedComplete, preserveReadState) => applyCompactionToReadFileState(readFileStateForCheckpoint, attachedComplete, preserveReadState)
101
+ ? (attachedComplete, preserveReadState) => applyContextShrinkToReadFileState(readFileStateForCheckpoint, { kind: "summarized", attachedComplete, ...(preserveReadState !== undefined ? { preserveKeys: preserveReadState } : {}) })
102
102
  : undefined;
103
103
  const changedFilesReadDenied = denyNarrowingPolicy === undefined
104
104
  ? undefined
@@ -108,6 +108,10 @@ export interface PrepareWiringManifestInput {
108
108
  /** borrowed-readonly — the leg's minted tool roster: rides the manifest's `tools` section and is the
109
109
  * source of the hand band's write-capable names for the ungated-write warning (what actually MOUNTED). */
110
110
  toolRoster: ToolRoster;
111
+ /** borrowed-readonly — from the read-face phase: the built-in read-deny TIERS that leg's own
112
+ * resolution materially activated; relayed onto the manifest (present iff non-empty), never
113
+ * re-derived from `deps` here (which by this phase sits past that phase's await). */
114
+ readDenyBuiltinTiersActive: readonly string[];
111
115
  /** borrowed-readonly — from the LSP phase's manager fold: gates the manifest's LSP section (absent ⇒ byte-identical manifest). */ lspManagerWired: boolean;
112
116
  /** borrowed-readonly — from the LSP phase: is the diagnostics lane live? The `lane` word's only input. */ lspDiagnosticsLive: boolean;
113
117
  /** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
@@ -113,7 +113,7 @@ function announceDurableGateUnavailable(args) {
113
113
  });
114
114
  }
115
115
  export function prepareWiringManifest(input) {
116
- const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, lspManagerWired, lspDiagnosticsLive } = input;
116
+ const { spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive } = input;
117
117
  const onAsk = spec.onAsk ?? deps.onAsk;
118
118
  const handWriteTools = toolRoster.entries.filter((e) => e.mountedBy === "hands" && (e.effect === "write" || e.effect === "idempotent")).map((e) => e.name);
119
119
  const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
@@ -208,6 +208,7 @@ export function prepareWiringManifest(input) {
208
208
  autoMode: { ...autoModeArmReason },
209
209
  mcp: mcpManifestEntries(lockedPreflight.mcp, mcp.statuses),
210
210
  ...(writeProtection !== undefined ? { writeProtectionTargetView: writeProtection.targetView } : {}),
211
+ readDenyBuiltinTiersActive,
211
212
  autoConsolidationOnRecommendation: autoRunOnRecommendationEffective(deps),
212
213
  tools: toolRoster,
213
214
  hooks: wiredHookEntries(hooks, spec.hooks !== undefined ? "spec" : "deps", preToolUseObservational),
@@ -2,6 +2,7 @@ import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js"
2
2
  import { forwardSafely } from "./forward-sink.js";
3
3
  import { emitTrace } from "../trace.js";
4
4
  import { primaryActivityArg } from "../arg-summary.js";
5
+ import { redactSecrets } from "../untrusted-egress.js";
5
6
  import { readDegradation } from "../../brain/degrading.js";
6
7
  import { OUTPUT_TOOL_NAME } from "./synthetic-tools.js";
7
8
  import { usageCostMicroUsd } from "./usage-accounting.js";
@@ -157,8 +158,9 @@ export function createHarnessHandlers(input) {
157
158
  rs.counters.repetitionCuts += 1;
158
159
  else
159
160
  rs.counters.repetitionSpared += 1;
161
+ const segment = redactSecrets(e.segment);
160
162
  if (rs.counters.repetitionEvents.length < rs.counters.REPETITION_EVENTS_CAP) {
161
- rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment: e.segment });
163
+ rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment });
162
164
  }
163
165
  emitTrace(rs.telemetry.tracer, () => ({
164
166
  kind: "repetition.detected",
@@ -169,7 +171,7 @@ export function createHarnessHandlers(input) {
169
171
  rule: e.rule,
170
172
  period: e.period,
171
173
  reps: e.reps,
172
- segment: e.segment,
174
+ segment,
173
175
  ts: Date.now(),
174
176
  }));
175
177
  }
@@ -3,7 +3,7 @@ import { type PendingSessionNotifications, type SystemInjectionPriority, type Ta
3
3
  import type { PushQueue } from "../push-queue.js";
4
4
  import type { TaskEvent, TaskSpec } from "../types.js";
5
5
  import type { Stats } from "./assemble-result.js";
6
- import type { CaptureOptOutRef, LiveHandle, ManualCompactRef, NotificationLaneBindings, NotifyRef, Prepared, RunInternals, RunnerDepsSeat, TaskIdRef } from "./contracts.js";
6
+ import type { CaptureOptOutRef, LiveHandle, ManualCompactRef, McpRedialRef, NotificationLaneBindings, NotifyRef, Prepared, RunInternals, RunnerDepsSeat, TaskIdRef } from "./contracts.js";
7
7
  export interface RunIdentityWiringInput {
8
8
  /** borrowed-readonly — the task spec: the task id and the memory / notification seats it names. */
9
9
  spec: TaskSpec;
@@ -27,6 +27,8 @@ export interface RunIdentityWiringInput {
27
27
  notifyRef: NotifyRef | undefined;
28
28
  /** borrowed-mutable — the capture opt-out bridge: `flip` is bound here when a memory session mounted. */
29
29
  captureOptOutRef: CaptureOptOutRef | undefined;
30
+ /** borrowed-mutable — the MCP re-dial bridge: `redial` is bound here when the leg declared MCP servers. */
31
+ mcpRedialRef: McpRedialRef | undefined;
30
32
  /** borrowed-readonly — the notification lane's ONE injection entry (the R0 lane's). */
31
33
  injectTaskNotification: (notification: TaskNotificationPayload, opts?: {
32
34
  priority?: SystemInjectionPriority;
@@ -3,7 +3,7 @@ import { createSafeNotifier } from "../safe-notify.js";
3
3
  import { discloseDroppedPending, renderTaskNotificationXml, taskNotificationDedupKey } from "../task-notification.js";
4
4
  import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
5
5
  export function runIdentityWiring(input) {
6
- const { spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, notificationLane, pendingSessionNotifications, runner } = input;
6
+ const { spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, mcpRedialRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, notificationLane, pendingSessionNotifications, runner } = input;
7
7
  notificationLane.harness = prepared.harness;
8
8
  notificationLane.sessionId = prepared.sessionId;
9
9
  if (taskIdRef)
@@ -66,6 +66,9 @@ export function runIdentityWiring(input) {
66
66
  if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
67
67
  captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
68
68
  }
69
+ if (mcpRedialRef && prepared.mcpRedial !== undefined) {
70
+ mcpRedialRef.redial = prepared.mcpRedial;
71
+ }
69
72
  try {
70
73
  internals?.onNotifyInjectorReady?.(injectTaskNotification);
71
74
  }
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
2
2
  import { planRejectionClears } from "../context-edit.js";
3
+ import { landStaleResultClears } from "./prepare-context-lane.js";
3
4
  import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact } from "../auto-compaction.js";
4
5
  import { emitTrace } from "../trace.js";
5
6
  import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
@@ -145,9 +146,11 @@ export function runRecoveryLanes(input) {
145
146
  }));
146
147
  }
147
148
  else {
148
- for (const e of plan.cleared) {
149
- prepared.microCompact.ledger.entries.set(e.key, { marker: e.marker, groupCount: e.groupCount, fp: e.fp });
150
- }
149
+ landStaleResultClears({
150
+ ledger: prepared.microCompact.ledger,
151
+ cleared: plan.cleared,
152
+ ...(prepared.readFileState !== undefined ? { readFileState: prepared.readFileState } : {}),
153
+ });
151
154
  emitTrace(rs.telemetry.tracer, () => ({
152
155
  kind: "context.mc_clear",
153
156
  version: 1,
@@ -23,7 +23,7 @@ import type { AssistantMessage } from "../../internal/llm.js";
23
23
  import type { ResolvedReasoning } from "../../brain/reasoning.js";
24
24
  import type { PushQueue } from "../push-queue.js";
25
25
  import type { SessionStore } from "../session.js";
26
- import { type CompactOutcome, type DelegationLifecycleEvent, type TaskEvent, type TaskResult, type TaskSpec } from "../types.js";
26
+ import { type CompactOutcome, type DelegationLifecycleEvent, type OwedUserInputAccount, type TaskEvent, type TaskResult, type TaskSpec } from "../types.js";
27
27
  import { type Stats } from "./assemble-result.js";
28
28
  import type { EndOfTaskCompaction, InstallingPhaseResult, ManualCompactRef, Prepared, ResumeRun, RunInternals, RunnerDepsSeat, RunState, SuspendReap, TaskIdRef } from "./contracts.js";
29
29
  export interface RunSettleAndTeardownInput {
@@ -94,6 +94,8 @@ export interface RunSettleAndTeardownInput {
94
94
  };
95
95
  /** borrowed-readonly — the end-of-task compaction's outcome (the terminal-adoption lane's `finish` call). */
96
96
  comp: EndOfTaskCompaction;
97
+ /** borrowed-readonly — the adoption lane's unsettled undrained-input account, closed below (after the reopen). */
98
+ owedUserInput: OwedUserInputAccount;
97
99
  /** borrowed-readonly — the stream's result setter: the assembled TaskResult is published through it exactly once. */
98
100
  setResult: (r: TaskResult) => void;
99
101
  /** borrowed-readonly — the stream's suggestions seat: the fire-and-forget suggestion pass's promise is handed to it. */
@@ -9,7 +9,7 @@ import { hasDestroy, isIsolated } from "../remote-env.js";
9
9
  import { resolveTaskModel } from "../roles.js";
10
10
  import { defaultTaskRegistry } from "../task-registry.js";
11
11
  import { emitTrace } from "../trace.js";
12
- import { deliverEngineNotice } from "../types.js";
12
+ import { deliverEngineNotice, settleOwedUserInput } from "../types.js";
13
13
  import { assembleResult } from "./assemble-result.js";
14
14
  import { discloseUnconsumedHalt } from "./halt-attribution.js";
15
15
  import { forwardSafely } from "./forward-sink.js";
@@ -21,7 +21,7 @@ import { settleTeardownLeg } from "./teardown-bounded.js";
21
21
  import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
22
22
  import { amendTerminal, terminalProjection } from "./terminal-projection.js";
23
23
  export async function runSettleAndTeardown(input) {
24
- const { spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef, emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, setResult, onSuggestions, onSuspend, runner, sessions } = input;
24
+ const { spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef, emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, owedUserInput, setResult, onSuggestions, onSuspend, runner, sessions } = input;
25
25
  try {
26
26
  if (comp?.compacted) {
27
27
  queue.push({
@@ -141,6 +141,7 @@ export async function runSettleAndTeardown(input) {
141
141
  });
142
142
  }
143
143
  discloseUnconsumedHalt({ accepted: loopLatch.userHalted, consumption: prepared.harness.haltConsumption(), onNotice: runner.deps.onNotice, sessionId: prepared.sessionId, runId: rs.telemetry.runId, taskId: spec.taskId });
144
+ let rowReturned = false;
144
145
  if (resume !== undefined &&
145
146
  resume.pendingActionStarted !== true &&
146
147
  result.terminal.kind !== "completed" &&
@@ -149,12 +150,14 @@ export async function runSettleAndTeardown(input) {
149
150
  resume.onEnvRestoreFailed !== undefined) {
150
151
  try {
151
152
  await resume.onEnvRestoreFailed("env_failed");
153
+ rowReturned = true;
152
154
  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`);
153
155
  }
154
156
  catch {
155
157
  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`);
156
158
  }
157
159
  }
160
+ settleOwedUserInput(owedUserInput, rowReturned, { taskId: spec.taskId ?? prepared.sessionId, sessionId: prepared.sessionId, runId: prepared.runId }, runner.deps.onNotice);
158
161
  prepared.sealReadStateSeat?.(ownCommittedTailRef.current);
159
162
  {
160
163
  const wss = prepared.workspaceStateSettle;
@@ -299,6 +302,7 @@ export async function runSettleAndTeardown(input) {
299
302
  }
300
303
  }
301
304
  finally {
305
+ settleOwedUserInput(owedUserInput, !prepared.suspendProgressRef.executedApproved, { taskId: spec.taskId ?? prepared.sessionId, sessionId: prepared.sessionId, runId: prepared.runId }, runner.deps.onNotice);
302
306
  const ownedEnvDying = prepared.ownedEnv !== undefined &&
303
307
  hasDestroy(prepared.ownedEnv) &&
304
308
  isIsolated(prepared.ownedEnv) &&
@@ -1,3 +1,4 @@
1
+ import { primaryActivityArg } from "../arg-summary.js";
1
2
  import { remainingBudgetMicroUsd, remainingTokens } from "../checkpoint-store.js";
2
3
  import { computeCostMicroUsd, isModelPriced, malformedPricingField, modelCostToPricing } from "../pricing.js";
3
4
  import { resolveModel } from "../roles.js";
@@ -19,7 +20,16 @@ export function runTelemetryAndBudgetSeats(input) {
19
20
  prepared.humanReviewRef.count += 1;
20
21
  prepared.humanReviewRef.totalWaitMs += waitMs;
21
22
  const gateToolName = "toolName" in resume.cp.gate ? resume.cp.gate.toolName : undefined;
22
- prepared.humanReviewRef.gates.push({ kind: resume.cp.gate.kind, waitMs, ...(decision !== undefined ? { decision } : {}), ...(gateToolName !== undefined ? { toolName: gateToolName } : {}) });
23
+ const gatedCall = resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction : undefined;
24
+ const gateToolArg = gatedCall === undefined ? undefined : primaryActivityArg(gatedCall.args);
25
+ prepared.humanReviewRef.gates.push({
26
+ kind: resume.cp.gate.kind,
27
+ waitMs,
28
+ ...(decision !== undefined ? { decision } : {}),
29
+ ...(gateToolName !== undefined ? { toolName: gateToolName } : {}),
30
+ ...(gatedCall !== undefined ? { toolCallId: gatedCall.toolCallId } : {}),
31
+ ...(gateToolArg !== undefined ? { toolArg: gateToolArg } : {}),
32
+ });
23
33
  }
24
34
  if (resume !== undefined && resume.outcome.gate === "plan_review" && resume.outcome.decision === "reject") {
25
35
  prepared.planModeRef.active = true;