@sema-agent/server 7.55.0 → 7.57.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 (129) hide show
  1. package/MIGRATION.md +3 -3
  2. package/README.md +14 -7
  3. package/README.zh-CN.md +10 -6
  4. package/USAGE.md +207 -59
  5. package/deploy/sema-up/chart/values.yaml +1 -1
  6. package/dist/approval-ask-audit-store.d.ts +100 -1
  7. package/dist/approval-ask-audit-store.js +103 -2
  8. package/dist/approval-card.d.ts +148 -13
  9. package/dist/approval-card.js +82 -13
  10. package/dist/approval-content-kind.d.ts +22 -0
  11. package/dist/approval-content-kind.js +5 -0
  12. package/dist/approval.d.ts +31 -0
  13. package/dist/approval.js +18 -0
  14. package/dist/auto-mode-face.d.ts +111 -0
  15. package/dist/auto-mode-face.js +99 -0
  16. package/dist/bench/s1/arms.js +5 -5
  17. package/dist/bench/s1/live-deps.js +11 -11
  18. package/dist/bench/s1/run-firm.js +3 -0
  19. package/dist/bench/s1/runner-ctx.d.ts +4 -0
  20. package/dist/bench/s1/runner-ctx.js +7 -0
  21. package/dist/boot/config-center.js +23 -1
  22. package/dist/boot/coordinators.js +13 -1
  23. package/dist/boot/leader.d.ts +21 -0
  24. package/dist/boot/leader.js +6 -0
  25. package/dist/boot/parked-revive-gate.d.ts +7 -2
  26. package/dist/boot/parked-revive-gate.js +12 -1
  27. package/dist/boot/resolve-spec.d.ts +3 -0
  28. package/dist/boot/resolve-spec.js +3 -1
  29. package/dist/boot/runner-deps.d.ts +12 -0
  30. package/dist/boot/runner-deps.js +28 -3
  31. package/dist/boot/runtime-caps.d.ts +19 -8
  32. package/dist/boot/runtime-caps.js +50 -20
  33. package/dist/boot/session-shell-gate-registry.d.ts +39 -0
  34. package/dist/boot/session-shell-gate-registry.js +21 -0
  35. package/dist/config-catalog.js +14 -5
  36. package/dist/config-center/types.d.ts +2 -1
  37. package/dist/config-provider.js +1 -0
  38. package/dist/config-types.d.ts +41 -10
  39. package/dist/config.d.ts +21 -0
  40. package/dist/config.js +40 -5
  41. package/dist/http/route-ctx.d.ts +90 -3
  42. package/dist/http/routes/a2a-serve.js +5 -3
  43. package/dist/http/routes/approvals-assistant.js +21 -8
  44. package/dist/http/routes/capabilities.js +21 -4
  45. package/dist/http/routes/leader.js +2 -2
  46. package/dist/http/routes/memory-origin.d.ts +2 -2
  47. package/dist/http/routes/rules.js +3 -2
  48. package/dist/http/routes/runs.d.ts +0 -14
  49. package/dist/http/routes/runs.js +20 -8
  50. package/dist/http/routes/tasks.js +34 -8
  51. package/dist/http/routes/workflows.js +20 -5
  52. package/dist/http/server.d.ts +11 -1
  53. package/dist/http/server.js +220 -34
  54. package/dist/http/wire-types.d.ts +9 -4
  55. package/dist/leader/diffout.js +2 -1
  56. package/dist/leader/endpoint.js +49 -15
  57. package/dist/leader/fanout.js +6 -5
  58. package/dist/leader/leader.js +17 -14
  59. package/dist/leader/merge.js +17 -12
  60. package/dist/leader/planner.js +3 -2
  61. package/dist/leader/repair-oracle.js +6 -4
  62. package/dist/leader/repair-wire.js +5 -3
  63. package/dist/leader/wire.d.ts +80 -1
  64. package/dist/leader/wire.js +55 -26
  65. package/dist/main.js +44 -8
  66. package/dist/observability/err-text.d.ts +6 -0
  67. package/dist/observability/err-text.js +10 -0
  68. package/dist/observability/fail-open.d.ts +40 -0
  69. package/dist/observability/fail-open.js +19 -0
  70. package/dist/observability/metrics.js +1 -1
  71. package/dist/observability/run-terminal-log.d.ts +120 -0
  72. package/dist/observability/run-terminal-log.js +360 -0
  73. package/dist/orchestration/workflow-completion-inbox.d.ts +9 -0
  74. package/dist/orchestration/workflow-completion-inbox.js +8 -0
  75. package/dist/permission-rule-vocab.d.ts +40 -0
  76. package/dist/permission-rule-vocab.js +17 -0
  77. package/dist/plugins/checkpoint-store-sql.d.ts +26 -0
  78. package/dist/plugins/checkpoint-store-sql.js +23 -4
  79. package/dist/plugins/file-run-store.d.ts +3 -34
  80. package/dist/plugins/file-run-store.js +21 -0
  81. package/dist/plugins/local-checkpoint-store.d.ts +10 -0
  82. package/dist/plugins/local-checkpoint-store.js +5 -0
  83. package/dist/plugins/local-session-store.d.ts +9 -9
  84. package/dist/plugins/local-session-store.js +5 -3
  85. package/dist/plugins/memory-run-store.d.ts +3 -15
  86. package/dist/plugins/memory-run-store.js +21 -0
  87. package/dist/plugins/permission-rule-store-file.d.ts +22 -4
  88. package/dist/plugins/permission-rule-store-file.js +25 -11
  89. package/dist/plugins/permission-rule-store-sql.d.ts +60 -30
  90. package/dist/plugins/permission-rule-store-sql.js +39 -23
  91. package/dist/plugins/pg-session-storage.js +17 -13
  92. package/dist/plugins/remote-scratchpad.js +3 -2
  93. package/dist/plugins/run-store-sql.d.ts +3 -42
  94. package/dist/plugins/run-store-sql.js +36 -5
  95. package/dist/plugins/store-backend.d.ts +1 -1
  96. package/dist/plugins/store-contracts.d.ts +70 -1
  97. package/dist/plugins/tidb-session-store.js +18 -14
  98. package/dist/plugins/workflow-run-store-sql.d.ts +5 -0
  99. package/dist/plugins/workflow-run-store-sql.js +10 -2
  100. package/dist/rules-consent.d.ts +5 -4
  101. package/dist/rules-consent.js +25 -46
  102. package/dist/run-cancel-context.d.ts +13 -0
  103. package/dist/run-cancel-context.js +15 -0
  104. package/dist/run-local.js +2 -2
  105. package/dist/runs.d.ts +4 -1
  106. package/dist/runs.js +41 -10
  107. package/dist/runtime-caps-resolver.d.ts +133 -17
  108. package/dist/runtime-caps-resolver.js +44 -4
  109. package/dist/security.d.ts +7 -0
  110. package/dist/task-settings.d.ts +57 -3
  111. package/dist/task-settings.js +78 -5
  112. package/dist/task-workflow.d.ts +32 -2
  113. package/dist/task-workflow.js +8 -3
  114. package/dist/tool-approval.d.ts +38 -63
  115. package/dist/tool-approval.js +102 -74
  116. package/dist/trace/core-keyset-guard.d.ts +2 -2
  117. package/dist/trace/ledger-sink.d.ts +13 -4
  118. package/dist/trace/ledger-sink.js +14 -6
  119. package/dist/trace/project.d.ts +13 -0
  120. package/dist/trace/project.js +10 -1
  121. package/dist/trace/redact.d.ts +22 -11
  122. package/dist/trace/redact.js +655 -20
  123. package/dist/trace/sema-provenance.d.ts +26 -0
  124. package/dist/trace/sema-provenance.js +11 -0
  125. package/dist/turn-activity.d.ts +32 -2
  126. package/dist/turn-activity.js +29 -4
  127. package/package.json +3 -3
  128. package/skills/find-skills.md +1 -1
  129. package/skills/loop.md +1 -1
@@ -1,4 +1,6 @@
1
1
  import { uuidv7 } from "@sema-agent/core";
2
+ import { redactErrorMessage, redactHead, redactLeaderResult, redactLeaderResultWithFlag } from "../observability/run-terminal-log.js";
3
+ import { withPrincipal } from "../observability/principal-context.js";
2
4
  import { leaderIdemKey } from "../plugins/leader-run-store-sql.js";
3
5
  function statusForLeaderResult(result) {
4
6
  switch (result.repairTerminal) {
@@ -37,17 +39,17 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
37
39
  }
38
40
  }
39
41
  }
40
- function drive(id, body) {
42
+ function drive(id, body, owner) {
41
43
  void (async () => {
42
44
  let terminal;
43
45
  try {
44
- const result = await runLeader(body);
46
+ const result = redactLeaderResult(await withPrincipal(owner ?? undefined, () => runLeader(body)));
45
47
  const status = statusForLeaderResult(result);
46
48
  terminal = { status: status, result };
47
49
  opts.logger?.info?.("leader_run_done", { id, ok: result.ok, status, ...(result.repairTerminal ? { repairTerminal: result.repairTerminal } : {}), merged: result.merge && result.merge.ok ? result.merge.merged : undefined });
48
50
  }
49
51
  catch (e) {
50
- terminal = { status: "failed", error: e instanceof Error ? e.message : String(e) };
52
+ terminal = { status: "failed", error: redactErrorMessage(e instanceof Error ? e.message : String(e)) ?? "" };
51
53
  opts.logger?.error?.("leader_run_error", { id, err: terminal.error });
52
54
  }
53
55
  if (opts.store) {
@@ -60,7 +62,7 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
60
62
  }
61
63
  catch (e) {
62
64
  if (attempt >= TERMINAL_WRITE_ATTEMPTS) {
63
- opts.logger?.error?.("leader_run_terminal_write_failed", { id, status: terminal.status, attempts: attempt, err: e instanceof Error ? e.message : String(e) });
65
+ opts.logger?.error?.("leader_run_terminal_write_failed", { id, status: terminal.status, attempts: attempt, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
64
66
  break;
65
67
  }
66
68
  await new Promise((r) => setTimeout(r, TERMINAL_WRITE_RETRY_MS));
@@ -78,16 +80,48 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
78
80
  }
79
81
  })();
80
82
  }
83
+ const CLEAN_MEMO_CAP = 4096;
84
+ const cleanIds = new Set();
85
+ const DIRTY_MEMO_CAP = 256;
86
+ const DIRTY_MEMO_MAX_ENTRY_BYTES = 1_048_576;
87
+ const DIRTY_MEMO_MAX_TOTAL_BYTES = 16 * 1_048_576;
88
+ const dirtyMemo = new Map();
89
+ let dirtyMemoBytes = 0;
90
+ function redactedView(run) {
91
+ const terminal = run.status !== "running";
92
+ if (terminal) {
93
+ if (cleanIds.has(run.id))
94
+ return { ...(run.result ? { result: run.result } : {}), ...(run.error ? { error: run.error } : {}) };
95
+ const hit = dirtyMemo.get(run.id);
96
+ if (hit)
97
+ return hit.view;
98
+ }
99
+ const rr = run.result ? redactLeaderResultWithFlag(run.result) : undefined;
100
+ const err = run.error ? (redactErrorMessage(run.error) ?? "") : undefined;
101
+ const changed = (rr?.changed ?? false) || (err !== undefined && err !== run.error);
102
+ const view = { ...(rr ? { result: rr.value } : {}), ...(err !== undefined ? { error: err } : {}) };
103
+ if (!terminal)
104
+ return view;
105
+ if (!changed) {
106
+ if (cleanIds.size >= CLEAN_MEMO_CAP)
107
+ cleanIds.delete(cleanIds.values().next().value);
108
+ cleanIds.add(run.id);
109
+ return view;
110
+ }
111
+ const bytes = Buffer.byteLength(JSON.stringify(view));
112
+ if (bytes <= DIRTY_MEMO_MAX_ENTRY_BYTES) {
113
+ while (dirtyMemo.size > 0 && (dirtyMemo.size >= DIRTY_MEMO_CAP || dirtyMemoBytes + bytes > DIRTY_MEMO_MAX_TOTAL_BYTES)) {
114
+ const oldest = dirtyMemo.keys().next().value;
115
+ dirtyMemoBytes -= dirtyMemo.get(oldest).bytes;
116
+ dirtyMemo.delete(oldest);
117
+ }
118
+ dirtyMemo.set(run.id, { view, bytes });
119
+ dirtyMemoBytes += bytes;
120
+ }
121
+ return view;
122
+ }
81
123
  function getBody(run) {
82
- return {
83
- status: 200,
84
- body: {
85
- id: run.id,
86
- status: run.status,
87
- ...(run.result ? { result: run.result } : {}),
88
- ...(run.error ? { error: run.error } : {}),
89
- },
90
- };
124
+ return { status: 200, body: { id: run.id, status: run.status, ...redactedView(run) } };
91
125
  }
92
126
  const NOT_FOUND = { status: 404, body: { error: "leader run not found", errorCode: "not_found.leader_run" } };
93
127
  async function handle(method, url, body, requester, idempotencyKey) {
@@ -120,7 +154,7 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
120
154
  return { status: 202, body: { leaderRunId: run.id, status: run.status } };
121
155
  }
122
156
  runs.set(run.id, { id: run.id, status: "running", startedAt: run.startedAtMs, owner });
123
- drive(run.id, b);
157
+ drive(run.id, b, owner);
124
158
  return { status: 202, body: { leaderRunId: run.id, status: "running" } };
125
159
  }
126
160
  if (scopedIdem) {
@@ -133,7 +167,7 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
133
167
  runs.set(id, { id, status: "running", startedAt: Date.now(), owner });
134
168
  if (scopedIdem)
135
169
  memIdem.set(scopedIdem, id);
136
- drive(id, b);
170
+ drive(id, b, owner);
137
171
  return { status: 202, body: { leaderRunId: id, status: "running" } };
138
172
  }
139
173
  const m = method === "GET" ? LEADER_ID_RE.exec(url) : null;
@@ -1,3 +1,4 @@
1
+ import { redactErrorMessage, redactWorkerReportError } from "../observability/run-terminal-log.js";
1
2
  class Semaphore {
2
3
  max;
3
4
  active = 0;
@@ -62,10 +63,10 @@ export async function fanOut(subtasks, opts) {
62
63
  return { ...base, status: "timeout", error: "cancelled before start" };
63
64
  if (opts.runWorker) {
64
65
  try {
65
- return maybeCancelOnSuspend(await opts.runWorker(st, sharedAC.signal));
66
+ return maybeCancelOnSuspend(redactWorkerReportError(await opts.runWorker(st, sharedAC.signal)));
66
67
  }
67
68
  catch (e) {
68
- return { ...base, status: cancelled ? "timeout" : "failed", error: e instanceof Error ? e.message : String(e) };
69
+ return { ...base, status: cancelled ? "timeout" : "failed", error: redactErrorMessage(e instanceof Error ? e.message : String(e)) ?? "" };
69
70
  }
70
71
  }
71
72
  let stream;
@@ -73,16 +74,16 @@ export async function fanOut(subtasks, opts) {
73
74
  stream = opts.runner.runTaskStream({ ...st.spec, sessionId: st.sessionId, signal: sharedAC.signal });
74
75
  }
75
76
  catch (e) {
76
- return { ...base, status: "failed", error: e instanceof Error ? e.message : String(e) };
77
+ return { ...base, status: "failed", error: redactErrorMessage(e instanceof Error ? e.message : String(e)) ?? "" };
77
78
  }
78
79
  streams.set(st.workerId, stream);
79
80
  let report;
80
81
  try {
81
82
  const result = await stream.result();
82
- report = { ...base, status: mapStatus(result.status), stats: result.stats, ...(result.errorMessage ? { error: result.errorMessage } : {}) };
83
+ report = { ...base, status: mapStatus(result.status), stats: result.stats, ...(result.errorMessage ? { error: redactErrorMessage(result.errorMessage) ?? "" } : {}) };
83
84
  }
84
85
  catch (e) {
85
- report = { ...base, status: cancelled ? "timeout" : "failed", error: e instanceof Error ? e.message : String(e) };
86
+ report = { ...base, status: cancelled ? "timeout" : "failed", error: redactErrorMessage(e instanceof Error ? e.message : String(e)) ?? "" };
86
87
  }
87
88
  finally {
88
89
  streams.delete(st.workerId);
@@ -1,4 +1,5 @@
1
1
  import { fanOut, isDurablePauseStatus } from "./fanout.js";
2
+ import { redactErrorMessage, redactHead, redactPatchHead } from "../observability/run-terminal-log.js";
2
3
  import { pullDiff } from "./diffout.js";
3
4
  import { mergeBranches } from "./merge.js";
4
5
  import { Runner, runWithVerification, resumeWithVerification, delimitUntrusted } from "@sema-agent/core";
@@ -58,8 +59,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
58
59
  deps.logger?.warn?.("worker_failed", {
59
60
  workerId: w.workerId, status: r.status, turns: r.stats?.turns ?? 0,
60
61
  ...(r.errorCode ? { errorCode: r.errorCode } : {}),
61
- ...(r.errorMessage ? { error: r.errorMessage.slice(0, 200) } : {}),
62
- ...(r.blockedReason ? { blockedReason: r.blockedReason.slice(0, 200) } : {}),
62
+ ...(r.errorMessage ? { error: redactHead(r.errorMessage, 200) } : {}),
63
+ ...(r.blockedReason ? { blockedReason: redactHead(r.blockedReason, 200) } : {}),
63
64
  });
64
65
  }
65
66
  return {
@@ -85,7 +86,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
85
86
  deps.logger?.info?.("worker_resource_resume", { workerId: w.workerId, slice });
86
87
  return resumeWithVerification(w.runner, token, { gate: "resource_limit", decision: "continue" }, { ...w.spec, sessionId: w.sessionId, signal }, w.spec.objective ?? "", cfg);
87
88
  });
88
- deps.logger?.info?.("verify_outcome", { workerId: w.workerId, status: vr.status, verdict: vr.verification.verdict, rounds: vr.verification.rounds, finding: (vr.verification.findings[0] ?? "").slice(0, 220) });
89
+ deps.logger?.info?.("verify_outcome", { workerId: w.workerId, status: vr.status, verdict: vr.verification.verdict, rounds: vr.verification.rounds, finding: redactHead(vr.verification.findings[0] ?? "", 220) });
89
90
  const pass = vr.status === "completed" && vr.verification.verdict === "PASS";
90
91
  const stillSuspended = vr.checkpointToken !== undefined;
91
92
  const reportStatus = pass ? "completed" : stillSuspended ? "suspended" : "failed";
@@ -93,8 +94,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
93
94
  deps.logger?.warn?.("worker_failed", {
94
95
  workerId: w.workerId, status: reportStatus, turns: vr.stats?.turns ?? 0, verdict: vr.verification.verdict,
95
96
  ...(vr.errorCode ? { errorCode: vr.errorCode } : {}),
96
- ...(vr.errorMessage ? { error: vr.errorMessage.slice(0, 200) } : {}),
97
- ...(vr.blockedReason ? { blockedReason: vr.blockedReason.slice(0, 200) } : {}),
97
+ ...(vr.errorMessage ? { error: redactHead(vr.errorMessage, 200) } : {}),
98
+ ...(vr.blockedReason ? { blockedReason: redactHead(vr.blockedReason, 200) } : {}),
98
99
  });
99
100
  }
100
101
  return {
@@ -105,7 +106,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
105
106
  : {
106
107
  error: vr.verification.verdict === "unverified"
107
108
  ? (vr.errorMessage ?? vr.blockedReason ?? `worker ${vr.status}`)
108
- : `verify ${vr.verification.verdict}: ${(vr.verification.findings[0] ?? "").slice(0, 200)}`,
109
+ : `verify ${vr.verification.verdict}: ${redactHead(vr.verification.findings[0] ?? "", 200)}`,
109
110
  }),
110
111
  ...(vr.checkpointGate ? { checkpointGate: vr.checkpointGate } : {}),
111
112
  ...(vr.errorCode ? { errorCode: vr.errorCode } : {}),
@@ -127,7 +128,7 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
127
128
  return { reports: [rr.report], cancelled: false };
128
129
  }
129
130
  catch (e) {
130
- deps.logger?.warn?.("repair_loop_dispatch_failed", { workerId: solo.workerId, err: e instanceof Error ? e.message : String(e) });
131
+ deps.logger?.warn?.("repair_loop_dispatch_failed", { workerId: solo.workerId, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
131
132
  return runWave([toSubtask(solo)]);
132
133
  }
133
134
  };
@@ -178,14 +179,15 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
178
179
  r.repairs = attempt;
179
180
  if (verdict.ok)
180
181
  return;
182
+ const safeVerdictReason = redactErrorMessage(verdict.reason ?? "behaviorally incorrect") ?? "";
181
183
  if (attempt === maxR) {
182
184
  r.status = "failed";
183
- r.error = `module gate: ${(verdict.reason ?? "behaviorally incorrect").slice(0, 200)}`;
185
+ r.error = `module gate: ${redactHead(verdict.reason ?? "behaviorally incorrect", 200)}`;
184
186
  return;
185
187
  }
186
- deps.logger?.info?.("module_repair", { workerId: r.workerId, attempt: attempt + 1, reason: verdict.reason });
188
+ deps.logger?.info?.("module_repair", { workerId: r.workerId, attempt: attempt + 1, reason: redactHead(verdict.reason ?? "", 500) });
187
189
  try {
188
- const rr = await w.runner.runTaskStream({ ...sub.spec, objective: deps.moduleGate.repairObjective(sub, verdict.reason ?? ""), sessionId: `${w.sessionId}-r${attempt + 1}` }).result();
190
+ const rr = await w.runner.runTaskStream({ ...sub.spec, objective: deps.moduleGate.repairObjective(sub, safeVerdictReason), sessionId: `${w.sessionId}-r${attempt + 1}` }).result();
189
191
  if (r.stats && rr?.stats)
190
192
  r.stats = { ...r.stats, tokens: (r.stats.tokens ?? 0) + (rr.stats.tokens ?? 0), costMicroUsd: (r.stats.costMicroUsd ?? 0) + (rr.stats.costMicroUsd ?? 0) };
191
193
  }
@@ -226,8 +228,9 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
226
228
  };
227
229
  };
228
230
  const collapseToSingle = async (trigger, reason, priorMerge) => {
229
- deps.logger?.warn?.("replan_collapse_to_single", { trigger, reason: reason.slice(0, 300) });
230
- const replan = { trigger, collapsed: reason.slice(0, 300), ...(redispatched ? { redispatched } : {}) };
231
+ const safeReason = redactHead(reason, 300);
232
+ deps.logger?.warn?.("replan_collapse_to_single", { trigger, reason: safeReason });
233
+ const replan = { trigger, collapsed: safeReason, ...(redispatched ? { redispatched } : {}) };
231
234
  const priorDiffs = [];
232
235
  await Promise.all(reports.map(async (r) => {
233
236
  const w = byId.get(r.workerId);
@@ -249,8 +252,8 @@ export async function runLeaderTask(task, durableBaseSha, deps) {
249
252
  task,
250
253
  ``,
251
254
  `--- PRIOR PARALLEL WORK — REUSE IT, do not re-derive from scratch ---`,
252
- `${priorDiffs.length} worker(s) attempted this task in parallel; their changes could NOT be integrated cleanly (${reason.slice(0, 150)}). Each worker's diff over the base is below. COPY the correct, non-overlapping parts verbatim and produce ONE coherent change for the WHOLE task, resolving only the overlaps. Treat these as a strong starting point — fix anything wrong, but do not throw the work away.`,
253
- ...priorDiffs.map((d) => delimitUntrusted(`PRIOR WORKER '${d.workerId}' (${d.status}) DIFF (untrusted)`, d.patch.slice(0, 8000))),
255
+ `${priorDiffs.length} worker(s) attempted this task in parallel; their changes could NOT be integrated cleanly (${safeReason.slice(0, 150)}). Each worker's diff over the base is below. COPY the correct, non-overlapping parts verbatim and produce ONE coherent change for the WHOLE task, resolving only the overlaps. Treat these as a strong starting point — fix anything wrong, but do not throw the work away.`,
256
+ ...priorDiffs.map((d) => delimitUntrusted(`PRIOR WORKER '${d.workerId}' (${d.status}) DIFF (untrusted)`, redactPatchHead(d.patch, 8000))),
254
257
  ].join("\n");
255
258
  const soloSub = { workerId: "collapse-solo", branch: "worker/collapse-solo", files: ["."], spec: { objective: soloObjective } };
256
259
  let solo;
@@ -1,4 +1,5 @@
1
1
  import { delimitUntrusted } from "@sema-agent/core";
2
+ import { OVERSIZED_DIAG_PLACEHOLDER, redactErrorMessage, redactHead, redactTail } from "../observability/run-terminal-log.js";
2
3
  import { applyAndGrade, changedFilesOutOfAllowlist } from "./grader-env-factory.js";
3
4
  import { shellQuote as Q } from "../plugins/remote-shell.js";
4
5
  export async function mergeBranches(reports, baseSha, deps) {
@@ -40,7 +41,7 @@ export async function mergeBranches(reports, baseSha, deps) {
40
41
  conflictResolverCostUsd += cr.costUsd;
41
42
  if (!cr.resolved) {
42
43
  deps.logger?.warn?.("merge_apply_failed", { workerId: p.workerId, exitCode: ap.exitCode });
43
- return { ok: false, phase: "apply", reason: `git apply --3way failed for ${p.workerId} (conflict)`, quarantine: [p.workerId], rej: rej.stdout, details: ap.stderr.slice(0, 800), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
44
+ return { ok: false, phase: "apply", reason: `git apply --3way failed for ${p.workerId} (conflict)`, quarantine: [p.workerId], rej: rej.stdout, details: redactHead(ap.stderr, 800), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
44
45
  }
45
46
  conflictsResolved.push(p.workerId);
46
47
  }
@@ -51,14 +52,18 @@ export async function mergeBranches(reports, baseSha, deps) {
51
52
  let cost = 0;
52
53
  let round = 0;
53
54
  for (; round < integ.repair.maxRounds && test.exitCode !== 0; round++) {
54
- const errorText = (test.stdout + "\n" + test.stderr).slice(-4000);
55
+ const errorText = redactTail(test.stdout + "\n" + test.stderr, 4000);
56
+ if (errorText.startsWith(OVERSIZED_DIAG_PLACEHOLDER)) {
57
+ deps.logger?.warn?.("integration_repair_skipped_oversized_output", { round: round + 1, outputChars: test.stdout.length + test.stderr.length });
58
+ break;
59
+ }
55
60
  deps.logger?.warn?.("integration_repair_round", { round: round + 1, maxRounds: integ.repair.maxRounds });
56
61
  try {
57
62
  const r = await integ.repair.run({ round: round + 1, errorText, testCmd: deps.testCmd, repoDir: repo });
58
63
  cost += r.costUsd ?? 0;
59
64
  }
60
65
  catch (e) {
61
- deps.logger?.warn?.("integration_repair_error", { round: round + 1, err: e instanceof Error ? e.message : String(e) });
66
+ deps.logger?.warn?.("integration_repair_error", { round: round + 1, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
62
67
  break;
63
68
  }
64
69
  repairMeta = { rounds: round + 1, fixed: false, costUsd: cost };
@@ -69,11 +74,11 @@ export async function mergeBranches(reports, baseSha, deps) {
69
74
  deps.logger?.info?.("integration_repair_done", { ...repairMeta });
70
75
  }
71
76
  if (test.exitCode !== 0) {
72
- return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: (test.stdout + "\n" + test.stderr).slice(-2000), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
77
+ return { ok: false, phase: "test", reason: `integration test failed (exit ${test.exitCode})`, details: redactTail(test.stdout + "\n" + test.stderr, 2000), ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
73
78
  }
74
79
  if (deps.measureCmd) {
75
80
  const mr = await env.exec(deps.measureCmd, { cwd: repo });
76
- measure = mr.exitCode === 0 ? { pass: true } : { pass: false, reason: (mr.stdout + " " + mr.stderr).slice(-200) };
81
+ measure = mr.exitCode === 0 ? { pass: true } : { pass: false, reason: redactTail(mr.stdout + " " + mr.stderr, 200) };
77
82
  }
78
83
  phase = "push";
79
84
  if (repairMeta)
@@ -81,11 +86,11 @@ export async function mergeBranches(reports, baseSha, deps) {
81
86
  const costTail = { ...(repairMeta ? { repair: repairMeta } : {}), ...(conflictResolverCostUsd > 0 ? { conflictResolverCostUsd } : {}) };
82
87
  const commit = await env.exec(`git add -A && git -c user.email=leader@local -c user.name=leader commit -m ${Q(`merge: integrate ${mergeable.map((r) => r.workerId).join("+")}`)} --allow-empty`, { cwd: repo });
83
88
  if (commit.exitCode !== 0) {
84
- return { ok: false, phase: "push", reason: `failed to create integration commit (exit ${commit.exitCode})`, details: (commit.stdout + "\n" + commit.stderr).slice(0, 800), ...costTail };
89
+ return { ok: false, phase: "push", reason: `failed to create integration commit (exit ${commit.exitCode})`, details: redactHead(commit.stdout + "\n" + commit.stderr, 800), ...costTail };
85
90
  }
86
91
  const integrated = await env.exec(`git format-patch --stdout ${Q(integBase)}..HEAD`, { cwd: repo });
87
92
  if (integrated.exitCode !== 0) {
88
- return { ok: false, phase: "push", reason: `failed to produce integrated patch (exit ${integrated.exitCode})`, details: integrated.stderr.slice(0, 800), ...costTail };
93
+ return { ok: false, phase: "push", reason: `failed to produce integrated patch (exit ${integrated.exitCode})`, details: redactHead(integrated.stderr, 800), ...costTail };
89
94
  }
90
95
  if (!integrated.stdout.trim()) {
91
96
  return { ok: false, phase: "push", reason: "integrated patch is empty (nothing applied over base) — refusing to push", quarantine: mergeable.map((r) => r.workerId), ...costTail };
@@ -148,11 +153,11 @@ async function evaluatePushHold(mergeable, observeMeasure, integratedPatch, deps
148
153
  return { hold: { reason: `integrated patch touches out-of-allowlist / build-config paths (${offending.slice(0, 5).join(", ")}) — needs_human_oracle (§10.4(ii))`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
149
154
  }
150
155
  const grade = await applyAndGrade(mdr.grader, integratedPatch, mdr.measureSteps);
151
- measure = grade.passed ? { pass: true } : { pass: false, reason: (grade.trace ?? "measure failed in out-of-process grader").slice(-200) };
156
+ measure = grade.passed ? { pass: true } : { pass: false, reason: redactTail(grade.trace ?? "measure failed in out-of-process grader", 200) };
152
157
  deps.logger?.info?.("merge_measure_grader", { passed: grade.passed, applied: grade.applied });
153
158
  }
154
159
  catch (e) {
155
- deps.logger?.warn?.("merge_measure_grader_error", { err: e instanceof Error ? e.message : String(e) });
160
+ deps.logger?.warn?.("merge_measure_grader_error", { err: redactErrorMessage(e instanceof Error ? e.message : String(e)) ?? "" });
156
161
  return { hold: { reason: `measure grader errored — needs_human_oracle (§10.4)`, terminal: "needs_human_oracle", candidatePatch: heldCandidate() }, measure };
157
162
  }
158
163
  }
@@ -160,14 +165,14 @@ async function evaluatePushHold(mergeable, observeMeasure, integratedPatch, deps
160
165
  const strongOracleUnconfirmed = deps.strongOracleSeeded === true;
161
166
  if (measureFailing || strongOracleUnconfirmed) {
162
167
  const why = measureFailing ? "measure did not pass" : "hidden held-out oracle seeded but only the compile gate cleared";
163
- deps.logger?.warn?.("merge_push_held_signal", { why, measureReason: (measure?.reason ?? "").slice(0, 200) });
168
+ deps.logger?.warn?.("merge_push_held_signal", { why, measureReason: redactHead(measure?.reason ?? "", 200) });
164
169
  return { hold: { reason: `strong oracle unconfirmed: ${why} — candidate_only, push held (§10.3)`, terminal: "candidate_only", candidatePatch: heldCandidate() }, measure };
165
170
  }
166
171
  return { measure };
167
172
  }
168
173
  async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver, deps) {
169
174
  let costUsd = 0;
170
- const conflictText = `git apply --3way could not cleanly apply worker '${p.workerId}'.\n--- apply stderr ---\n${applyStderr.slice(0, 1500)}\n--- reject hunks (*.rej) ---\n${rejText.slice(0, 3000)}`;
175
+ const conflictText = `git apply --3way could not cleanly apply worker '${p.workerId}'.\n--- apply stderr ---\n${redactHead(applyStderr, 1500)}\n--- reject hunks (*.rej) ---\n${redactHead(rejText, 3000)}`;
171
176
  for (let round = 0; round < resolver.maxRounds; round++) {
172
177
  deps.logger?.warn?.("merge_conflict_repair_round", { workerId: p.workerId, round: round + 1, maxRounds: resolver.maxRounds });
173
178
  try {
@@ -175,7 +180,7 @@ async function resolveApplyConflict(env, repo, p, rejText, applyStderr, resolver
175
180
  costUsd += r.costUsd ?? 0;
176
181
  }
177
182
  catch (e) {
178
- deps.logger?.warn?.("merge_conflict_repair_error", { workerId: p.workerId, err: e instanceof Error ? e.message : String(e) });
183
+ deps.logger?.warn?.("merge_conflict_repair_error", { workerId: p.workerId, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
179
184
  return { resolved: false, costUsd };
180
185
  }
181
186
  const check = await env
@@ -1,3 +1,4 @@
1
+ import { redactErrorMessage, redactHead, redactTail } from "../observability/run-terminal-log.js";
1
2
  export function economicGuard(objective, opts = {}) {
2
3
  if (opts.fanoutEnabled === false)
3
4
  return { single: true, reason: "fan-out disabled by deployment (A6)" };
@@ -124,13 +125,13 @@ export async function routePlanWithFallback(objective, runRoute, opts = {}) {
124
125
  catch (e) {
125
126
  firstErr ??= e;
126
127
  if (!firstRaw)
127
- firstRaw = raw.length > 600 ? `${raw.slice(0, 300)}…[${raw.length}ch]…${raw.slice(-150)}` : raw;
128
+ firstRaw = raw.length > 600 ? `${redactHead(raw, 300)}…[${raw.length}ch]…${redactTail(raw, 150)}` : (redactErrorMessage(raw) ?? "");
128
129
  }
129
130
  }
130
131
  const reason = firstErr instanceof Error ? firstErr.message : String(firstErr);
131
132
  return {
132
133
  subs: singleRoute(objective),
133
- collapsed: `plan rejected twice → collapse to single: ${reason.slice(0, 200)}`,
134
+ collapsed: `plan rejected twice → collapse to single: ${redactHead(reason, 200)}`,
134
135
  rawSample: firstRaw,
135
136
  };
136
137
  }
@@ -1,6 +1,7 @@
1
1
  import { runExecGate, } from "@sema-agent/core";
2
2
  import { createHash } from "node:crypto";
3
3
  import { shellQuote as sq } from "../plugins/remote-shell.js";
4
+ import { redactHead, redactTail } from "../observability/run-terminal-log.js";
4
5
  const sha = (s) => createHash("sha256").update(s).digest("hex").slice(0, 16);
5
6
  function changedFilesOf(patch) {
6
7
  const out = new Set();
@@ -28,8 +29,9 @@ export function mkRepairOracle(cfg) {
28
29
  return async (graderEnv, _evidence) => {
29
30
  const diff = await cfg.pullWorkerDiff();
30
31
  if (!diff.ok) {
31
- cfg.log?.("repair_oracle_pull_failed", { error: diff.error });
32
- return { tier: cfg.passTier, passed: false, flaky: false, retries: 0, trace: `pull worker diff failed: ${diff.error}` };
32
+ const safeErr = redactHead(diff.error ?? "", 2000);
33
+ cfg.log?.("repair_oracle_pull_failed", { error: safeErr });
34
+ return { tier: cfg.passTier, passed: false, flaky: false, retries: 0, trace: `pull worker diff failed: ${safeErr}` };
33
35
  }
34
36
  const diffHash = sha(diff.patch);
35
37
  const tampered = cfg.oraclePaths && cfg.oraclePaths.length > 0 ? touchesOraclePath(changedFilesOf(diff.patch), cfg.oraclePaths) : undefined;
@@ -51,7 +53,7 @@ export function mkRepairOracle(cfg) {
51
53
  { command: `git -C ${sq(repo)} apply --whitespace=nowarn ${sq(patchPath)}`, label: "grader-apply" },
52
54
  ]);
53
55
  if (!prep.passed) {
54
- const trace = prep.steps.map((s) => `${s.label}: ${s.stderr}`).join("\n").slice(-2000);
56
+ const trace = redactTail(prep.steps.map((s) => `${s.label}: ${s.stderr}`).join("\n"), 2000);
55
57
  cfg.log?.("repair_oracle_prep_failed", { trace });
56
58
  cfg.onGraded?.({ diff: diff.patch, hash: diffHash, passed: false });
57
59
  return { tier: cfg.passTier, passed: false, flaky: false, retries: 0, trace: `grader prep failed:\n${trace}` };
@@ -64,7 +66,7 @@ export function mkRepairOracle(cfg) {
64
66
  const gate = await runExecGate(graderEnv, cfg.oracleSteps);
65
67
  verdicts.push(gate.passed);
66
68
  if (!gate.passed) {
67
- lastTrace = gate.steps.map((s) => `${s.label}\n${s.stdout}\n${s.stderr}`).join("\n").slice(-4000);
69
+ lastTrace = redactTail(gate.steps.map((s) => `${s.label}\n${s.stdout}\n${s.stderr}`).join("\n"), 4000);
68
70
  break;
69
71
  }
70
72
  }
@@ -1,5 +1,6 @@
1
1
  import { runRepairLoop, delimitUntrusted, } from "@sema-agent/core";
2
2
  import { mkRepairOracle } from "./repair-oracle.js";
3
+ import { redactHead } from "../observability/run-terminal-log.js";
3
4
  function statusForTerminal(t) {
4
5
  return t === "fixed" || t === "candidate_only" ? "completed" : "failed";
5
6
  }
@@ -41,12 +42,13 @@ async function runRepairLoopForLeaderInner(solo, implSpec, cfg) {
41
42
  ...(cfg.onAttempt
42
43
  ? { onAttempt: (i) => cfg.onAttempt({ attempt: i.attempt, terminal: i.terminal, oracleTier: i.oracle.tier, passed: i.oracle.passed, flaky: i.oracle.flaky }) }
43
44
  : {}),
44
- onWarn: (e) => cfg.log?.("repair_loop_warn", { warning: e.message }),
45
+ onWarn: (e) => cfg.log?.("repair_loop_warn", { warning: redactHead(e.message, 2000) }),
45
46
  });
46
47
  }
47
48
  catch (e) {
48
- cfg.log?.("repair_loop_threw", { error: e instanceof Error ? e.message : String(e) });
49
- return { report: { ...base, status: "failed", error: e instanceof Error ? e.message : String(e) }, result: undefined };
49
+ const safe = redactHead(e instanceof Error ? e.message : String(e), 2000);
50
+ cfg.log?.("repair_loop_threw", { error: safe });
51
+ return { report: { ...base, status: "failed", error: safe }, result: undefined };
50
52
  }
51
53
  const terminal = result.terminal;
52
54
  const report = {
@@ -1,4 +1,4 @@
1
- import { type Brain, type Model, type ModelRoles, type ModelPricing, type TaskSpec, type ExecutionEnvFactory, type RemoteExecutionEnv, type ToolResultStore, type SessionStore, type RunnerDeps } from "@sema-agent/core";
1
+ import { type Brain, type Model, type ModelRoles, type ModelPricing, type TaskSpec, type ExecutionEnvFactory, type RemoteExecutionEnv, type ToolResultStore, type SessionStore, type ExecutionEnv, type RunnerDeps } from "@sema-agent/core";
2
2
  import type { CheckpointStoreFull } from "../plugins/store-backend.js";
3
3
  import { type LeaderDeps, type LeaderResult } from "./leader.js";
4
4
  import { type DeploymentPostureSeats } from "../boot/runner-deps.js";
@@ -48,6 +48,56 @@ export interface LeaderWireConfig {
48
48
  * leader 编排的 planner / worker / repair / conflict 四类任务**静默不生效**(core 内建 deny 表仍在)。
49
49
  */
50
50
  deploymentPosture?: DeploymentPostureSeats;
51
+ /**
52
+ * [ref]([ref])—— **计费/遥测追踪席**,原样递给本车道的每一只 Runner。唯一属主 =
53
+ * `boot/budget-tracing.ts` 的 `createTracer(...)`(主 runner / subRunner / hook runner 经共享基座吃的
54
+ * 就是它),`boot/leader.ts` 搬的是**同一只**。
55
+ *
56
+ * 🔴 为什么这一席是承重的:`src/budget.ts` 的 `createTracer` 是 `brain.call` **唯一**的记账点 ——
57
+ * `costQuota.add` / `fleetUsage.record` / `fleetLease.recordSpend` / `model_cost_micro_usd_total` 四个写口
58
+ * 全在那里(`createBrain` 自己不记账)。而 `POST /v1/leader` 的准入处**读**的正是 costQuota / fleetLease
59
+ * 这两个桶。缺席 ⇒ 门查的桶与花钱的手不是同一只:`MAX_PRINCIPAL_COST_USD` 与 fleet 租约对这条腿结构性
60
+ * 失效,而一条 leader run 扇出 ≤6 个带手 worker + planner + repair/conflict 环,全是实钱。
61
+ * 归因的另一半在 `leader/endpoint.ts` 的 `drive()`(ALS `withPrincipal`)—— 记账臂按 `currentPrincipal()`
62
+ * 分租户,只补席不包裹 = 归因恒 undefined = 假绿。缺席 ⇒ 展开空对象(与接线前逐字相同)。
63
+ */
64
+ tracer?: RunnerDeps["tracer"];
65
+ /**
66
+ * [ref]([ref],[ref]-T1)—— **部署级 token 治理窗**两键,**成对**递给本车道的每一只 Runner。
67
+ * 属主 = `config.usageWindows`(声明)+ `boot/stores.ts` 造的账本店(两者同真同假,与
68
+ * `boot/runner-deps.ts` 的共享基座同规);leader 不自己读 config。
69
+ *
70
+ * 成对的理由是 core 的消费形:`prepare-task.js` 的 `buildUsageGovernance(usageWindows, deps, …)` 要求
71
+ * 声明与 `deps.usageWindowStore` **同时**在场才铸 `usageGovernance`,缺一即 undefined(既不 check 也不
72
+ * commit)⇒ 半席只是死键。缺席 ⇒ 展开空对象。
73
+ * 提交面那一半(`POST /v1/leader` 的 `usageWindowDenied` 门)在 `src/http/routes/leader.ts`。
74
+ */
75
+ usageWindows?: RunnerDeps["usageWindows"];
76
+ usageWindowStore?: RunnerDeps["usageWindowStore"];
77
+ /**
78
+ * [ref]([ref])—— **center 发布的 prompt catalog** 席,原样递给本车道的每一只 Runner。唯一属主 =
79
+ * `boot/config-center.ts` 的 `configCenter.promptSource`(主 runner / subRunner 同一只;run-local 有自己
80
+ * 的等价腿)。缺席 ⇒ leader 的 planner/worker/repair/conflict 四类**顶层**任务跑引擎内置提示词,而同一
81
+ * 部署的 `/v1/tasks`、`/v1/runs` 跑运维发布的那一份 —— 且 core 顶层新任务走的是**静默**臂(pinned /
82
+ * parentCenterArtifactDigest 两支才 `prompt.snapshot_unavailable` fail-loud),运维面零信号。
83
+ * 缺席 ⇒ 展开空对象(与接线前逐字相同)。
84
+ */
85
+ promptSource?: RunnerDeps["promptSource"];
86
+ /**
87
+ * [ref] 的**披露半件**([ref],codex 对抗复审 r2-[high],亲读装树 core 后修)—— 部署级错误汇。
88
+ *
89
+ * 🔴 为什么它与治理窗席**同生共死**:core 对「治理窗最终扣账失败」的唯一出口就是这一席 ——
90
+ * `dist/core/runner/runtask.js` 收尾段 `catch (flushErr) { this.deps.onError?.(flushErr, { phase: "config", … }) }`
91
+ * (以及 10s 慢盘披露的同一只回调),抛错之后任务**按原结果收尾**。挂了窗席却不挂本席 = 一次窗账
92
+ * 永久丢失时这条腿上零日志零计数,后续提交继续读偏低的窗被放行 —— CLAUDE.md [ref]「治理/门轴禁静默
93
+ * fail-open」正是说这个。它顺带也把 leader 车道其余 phase(degraded / rewind / memory / mcp / a2a /
94
+ * prompt-cache …)的既有观测面补齐,与 `onNotice` 席当年([ref]②)同一个理由。
95
+ *
96
+ * 属主唯一 = `boot/runner-deps.ts` `createRunnerDeps` 铸的那一只;`boot/leader.ts` 直引
97
+ * `runnerDeps.onError`(**同一实例**,[ref]§三族A 的最强同源形,与 main.ts subRunner 的先例逐字同姿势),
98
+ * leader 绝不另写一份臂表(两份必漂)。缺席 ⇒ 展开空对象(与接线前逐字相同)。
99
+ */
100
+ onError?: RunnerDeps["onError"];
51
101
  /** Worker model brain + catalog (the same the service runs tasks on). */
52
102
  brain: Brain;
53
103
  models: Record<string, Model>;
@@ -96,6 +146,35 @@ export interface LeaderWireConfig {
96
146
  }
97
147
  /** The env surface wire needs (both the E2B and k8s adapters implement core's RemoteExecutionEnv). */
98
148
  type WireEnv = Pick<RemoteExecutionEnv, "exec" | "writeFile">;
149
+ /**
150
+ * [ref] —— leader 自铸沙盒的**回收闸**(集成沙盒 / repair-loop grader 各一只)。
151
+ *
152
+ * core 契约:`ExecutionEnv` 基面**没有** `destroy`;只有实现了 `RemoteExecutionEnv` 的适配器才有
153
+ * (`core/remote-env.d.ts:426`),而「这只 env 到底有没有」是**运行期结构事实**,core 自己也只用结构探测
154
+ * `hasDestroy()`(:487,runtask/prepare-task 全部 destroy 站点都先过它)。修前 leader 三处把它写成编译期
155
+ * 断言(`as … & { destroy(): Promise<unknown> }`),于是 TS 对「手里其实是个 **Promise**」完全无感——
156
+ * `env.destroy()` 抛 `env.destroy is not a function`,还把**原始错误盖掉**([ref] 现场)。
157
+ *
158
+ * 🔴 缺席臂 = **fail-closed,当场响亮拒**([ref]:执行车道禁静默兜底;codex r2-[medium] 采纳)。理由:
159
+ * · core 之所以容忍「工厂产物没有 destroy」,是因为工厂**可以**交回一只**共享静态** env(`withWorktreeIsolation`
160
+ * 的非隔离臂就是这一形)——那种 env 本来就不该被谁拆掉。但 leader 这两只是**专用、临时、只属于它自己**
161
+ * 的沙盒:唯一的拆机口就是本闭包(`mergeBranches` / `runRepairLoopForLeader` 在 `finally` 里调它)。
162
+ * 收不动 = 一只计费中的沙盒必然留到后端自己的 timeout,零上行收益 ⇒ 没有「合法兜底」可言。
163
+ * · 静默 no-op 反而比**修前**更松:修前同步工厂路径上,一只没有 destroy 的 env 会当场抛 TypeError。
164
+ * 本闸只是把那个抛**提前到 provision 时刻**,并换成一句点名沙盒与成因的话——不是新增严格度。
165
+ * · 当前三条已接线的 leader 车道交回的 env 都实现了 destroy(e2b `RemoteContainerExecutionEnv`、
166
+ * k8s、host `RemoteHostExecutionEnv`——`hostExecutionEnvFactory` 恒 `new RemoteHostExecutionEnv`),
167
+ * 所以这条臂是**防御性**的:它真响的那天,意思是有人接了一条新工厂车道而 leader 收不动它的沙盒。
168
+ */
169
+ /** 冲突解决模型的目标文本——纯函数(第十三轮重扫 [medium]:wire 内联时无法钉「补丁先脱」;抽出来直接钉)。
170
+ * 补丁走补丁头窗(头 256KB 扫再切 6000;解决者仍有工作树里的冲突标记),conflictText 由 merge.ts 先脱后切。 */
171
+ export declare function buildConflictResolverObjective(args: {
172
+ workerId: string;
173
+ repoDir: string;
174
+ patch: string;
175
+ conflictText: string;
176
+ }): string;
177
+ export declare function createEnvReaper(env: ExecutionEnv, what: string): () => Promise<void>;
99
178
  /**
100
179
  * Resume-safe worker staging (the [ref] F2(a) factory-env path). Runs adapter-side before the agent's
101
180
  * first workspace-touching call: seed the base repo at the worker branch + plant the upload script (so the