@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,32 +1,8 @@
1
- /**
2
- * design/68 §8 F2(a) — worker SELF-UPLOAD diff-out (the factory-env counterpart of diffout.ts's pullDiff).
3
- *
4
- * With a factory env the runner owns the container lifecycle (create at task start, destroy at finish — and
5
- * durable suspend only produces a complete ownedEnv on this path), so the leader can no longer pull the diff
6
- * from a still-alive env after the task. Instead (the gate#2-proven MinIO presigned pattern):
7
- * 1. the env factory STAGES each fresh container (seed repo + write an upload script holding a presigned
8
- * PUT URL) — adapter-side, so the URL/bulk never enters the model's CONTEXT by construction (it does sit
9
- * on the sandbox filesystem, readable if the agent goes looking — the objective forbids inspecting it;
10
- * the URL is single-object, PUT-only, short-TTL, so that residual face is small);
11
- * 2. the worker's objective ends with a MANDATORY final step: run the upload script (`git format-patch` →
12
- * `curl -T <file>` — a real file, so Content-Length is set; MinIO 411-rejects chunked presigned PUTs);
13
- * 3. after fan-out the leader fetches the object over a presigned GET on the CONTROL PLANE (fetchUploadedDiff).
14
- * A worker that skips the upload surfaces as `{ok:false}` → merge quarantines it (honest failure, no hang).
15
- * pullDiff stays as the compat path for static (leader-owned) envs — M2 降兼容.
16
- */
17
- // 字节帽与 `safeRev` 同源取自 pull 腿(理由见 diffout.ts 的 DEFAULT_MAX_DIFF_BYTES 顶注:同值双抄一旦
18
- // 单边改值,上行/pull 两腿对同一个 diff 的过/拒判定就分家,且两边都「按自己的常量」静默正确)。
19
1
  import { safeRev, DEFAULT_MAX_DIFF_BYTES } from "./diffout.js";
20
2
  import { OBJECT_STORE_IO_TIMEOUT_MS } from "../plugins/blob-backend.js";
21
- /** The staged upload script. `repoDir`/`workDir` are deployment config; `baseSha` is interpolated into a
22
- * shell line, so it is re-validated HERE (fail-closed) — callers should already have applied diffout's
23
- * safeRev rules, but this function is the last line before the shell. The patch is written OUTSIDE the repo
24
- * dir so it never shows up as untracked noise in the worker's `git status`. */
25
3
  export function buildUploadScript(p) {
26
4
  if (!safeRev(p.baseSha))
27
5
  throw new Error(`buildUploadScript: unsafe baseSha ${JSON.stringify(p.baseSha)}`);
28
- // presignS3Url percent-encodes all specials today, so a "'" can't occur — escape anyway (belt-and-suspenders;
29
- // the URL sits inside single quotes in the script below).
30
6
  const putUrl = p.putUrl.replaceAll("'", "'\\''");
31
7
  return [
32
8
  "#!/bin/sh",
@@ -34,35 +10,25 @@ export function buildUploadScript(p) {
34
10
  "set -e",
35
11
  `cd ${p.repoDir}`,
36
12
  `git format-patch --stdout ${p.baseSha}..HEAD > ${p.workDir}/.leader-out.patch`,
37
- // -T <file> (not stdin): sets Content-Length — MinIO returns 411 on chunked presigned PUTs (gate#3 council#3).
38
13
  `curl -fsS -X PUT -T ${p.workDir}/.leader-out.patch '${putUrl}'`,
39
14
  `echo leader-diff-uploaded`,
40
15
  "",
41
16
  ].join("\n");
42
17
  }
43
- /** The objective suffix that makes the upload the worker's last task step (no URL through the model). */
44
18
  export function uploadStepSuffix(workDir) {
45
19
  return `\n\nMANDATORY FINAL STEP: after your final commit, run exactly: sh ${workDir}/.leader-upload.sh — it exports your branch diff for integration. Do not modify, inspect, or skip it; if it fails, fix your git state (commit your work) and run it again.`;
46
20
  }
47
- /**
48
- * Fetch the worker-uploaded patch over a presigned GET (control plane; creds stay adapter-held). Returns the
49
- * same result shape as pullDiff so merge consumes either path. 🔴 The presigned URL is a short-lived bearer
50
- * credential — it must never appear in returned errors (gate#3 council#5 redaction rule).
51
- */
52
21
  export async function fetchUploadedDiff(getUrl, opts = {}) {
53
22
  const maxBytes = opts.maxBytes ?? DEFAULT_MAX_DIFF_BYTES;
54
23
  const f = opts.fetchImpl ?? fetch;
55
24
  let res;
56
25
  try {
57
- // C6(2026-08-03):abort 上界(半开 MinIO 此前把 leader merge 阶段挂满 undici ~300s,钉死整个
58
- // fanout)。maxBytes 检查仍在 text() 缓冲之后=已知残留(流式读上界另案),本修只收超时轴。
59
26
  res = await f(getUrl, { signal: AbortSignal.timeout(OBJECT_STORE_IO_TIMEOUT_MS) });
60
27
  }
61
28
  catch (e) {
62
29
  return { ok: false, error: `diff fetch failed: ${e instanceof Error ? e.message.replaceAll(getUrl, "<presigned-url>") : "network error"}` };
63
30
  }
64
31
  if (res.status === 404 || res.status === 403) {
65
- // MinIO answers 403 (signature scope) for a missing key on some configs — both mean "no upload landed".
66
32
  return { ok: false, error: `no uploaded diff (worker skipped the mandatory upload step, or upload failed) [${res.status}]` };
67
33
  }
68
34
  if (!res.ok)
@@ -74,14 +40,7 @@ export async function fetchUploadedDiff(getUrl, opts = {}) {
74
40
  return { ok: false, error: "uploaded diff is empty (worker committed nothing past base)" };
75
41
  return { ok: true, patch, bytes: patch.length };
76
42
  }
77
- /** The minimal surface staging must run before (the agent reaches the workspace through these). */
78
43
  const STAGED_METHODS = new Set(["exec", "execStream", "writeFile", "readFile"]);
79
- /**
80
- * Wrap an ExecutionEnv so a one-shot `stage(env)` (seed repo + write the upload script) runs before the FIRST
81
- * workspace-touching call — the adapters are lazy-connect, so there is no explicit connect() hook to ride; this
82
- * guarantees seed-before-agent on whichever call comes first. A failed staging rejects that call (task fails
83
- * honestly) and RESETS, so a retried call re-attempts staging instead of replaying a cached rejection.
84
- */
85
44
  export function withStaging(env, stage, finalize) {
86
45
  let staged;
87
46
  const ensure = () => {
@@ -98,11 +57,6 @@ export function withStaging(env, stage, finalize) {
98
57
  return v;
99
58
  const fn = v.bind(t);
100
59
  if (prop === "destroy" && finalize) {
101
- // Belt for the MANDATORY upload step (found live: task02b run4 collapse-solo): a worker that did the
102
- // porting but FORGOT `sh .leader-upload.sh` loses its work when the runner destroys the env — diff-out
103
- // 404s and merge quarantines a worker that actually finished. Run `finalize` best-effort before the
104
- // teardown, only if the env was ever staged (never spawn a pod just to upload nothing). Idempotent:
105
- // re-running the upload script after a model-run upload overwrites the same object with the same diff.
106
60
  return async (...args) => {
107
61
  if (staged)
108
62
  await Promise.resolve(staged).then(() => finalize(env)).catch(() => { });
@@ -112,7 +66,6 @@ export function withStaging(env, stage, finalize) {
112
66
  if (!STAGED_METHODS.has(prop))
113
67
  return fn;
114
68
  if (prop === "execStream") {
115
- // async-iterable return — staging must complete before the underlying stream starts.
116
69
  return (...args) => (async function* () {
117
70
  await ensure();
118
71
  yield* fn(...args);
@@ -1,31 +1,5 @@
1
- /**
2
- * v2 MVP M4 — leader HTTP endpoint (V2-MVP-PLAN.md). Exposes `runLeaderTask` over HTTP:
3
- * POST /v1/leader → 202 { leaderRunId, status:"running" }, runs in the background
4
- * GET /v1/leader/:id → { id, status, result?, error? }
5
- * A leader run is long (minutes: provision N E2B workers → real model coding → merge → push), so it is async
6
- * (background + poll), mirroring /v1/runs. `runLeader` is injected (the real wiring lives in wire.ts /
7
- * main.ts) so this handler is pure + mock-tested.
8
- *
9
- * ── 登记表的两条腿(#193 车4 件1,台账 P1-9)────────────────────────────────────────────────────────
10
- * **默认腿 = durable SQL 店**(`opts.store`,`plugins/leader-run-store-sql.ts` 的双方言孪生)。它一次解掉
11
- * 三条同源缺口:跨副本 GET(行在库里)、POST 提交幂等(`idem_key` UNIQUE 的原子赢或观察)、以及
12
- * 「重复 spawn」这件事本身(准入处只放一条进去,worker 层就不需要那道由构造即无效的并发去重面 ——
13
- * 它的删除理由见 `fanout.ts` 头注)。
14
- *
15
- * **降级腿 = 进程内 `Map`**(`opts.store` 缺席时)。它是**显式的单副本限定**形,不是「以后再补」:
16
- * · 跨副本 GET 404、进程重启即失忆、幂等作用域只到本进程;
17
- * · 生产部署不会落到这条腿上 —— config 层已把「`LEADER_ENABLED=true` 且无外部 SQL 店」整个组合拒启
18
- * (#193 车4 件2 门②),所以这条腿的真实用户只有单元测试与手工试跑。
19
- * 两腿的**可观察行为**除上述三条外逐位一致(同一套 4xx 码、同一套属主门、同一份 202/200 体)。
20
- */
21
1
  import { uuidv7 } from "@sema-agent/core";
22
2
  import { leaderIdemKey } from "../plugins/leader-run-store-sql.js";
23
- /**
24
- * LEADER-REPAIRLOOP-INTEGRATION §5/§10.6 — map a finished `LeaderResult` to the run status. The repair terminal
25
- * (set only when LEADER_REPAIR_LOOP/LEADER_MEASURE_GATES drove a single-agent path or the merge push chokepoint
26
- * held) refines the legacy `ok ? completed : failed` into a THIRD `needs_human` for an ABSTAIN (a held candidate
27
- * awaiting a human), keeping `oracle.unprotected`/`gave_up` as `failed` (carry the terminal as the WHY —
28
- * no-silent-failures). Undefined `repairTerminal` ⇒ the exact legacy mapping (byte-identical default path). */
29
3
  function statusForLeaderResult(result) {
30
4
  switch (result.repairTerminal) {
31
5
  case "candidate_only":
@@ -36,20 +10,13 @@ function statusForLeaderResult(result) {
36
10
  case "gave_up":
37
11
  return "failed";
38
12
  default:
39
- // `fixed` (not reachable SAFE-tier) or undefined → the legacy ok-based mapping.
40
13
  return result.ok ? "completed" : "failed";
41
14
  }
42
15
  }
43
16
  const LEADER_ID_RE = /^\/v1\/leader\/([^/]+)$/;
44
- /** How long a FINISHED leader run stays pollable before it's evicted from the in-memory registry.
45
- * Bounds PROCESS MEMORY on the degraded arm — it is not a retention policy (the durable table keeps its rows;
46
- * see the store's header for why it has no reaper leg). */
47
17
  const RUN_RETENTION_MS = 60 * 60_000;
48
- /** 终局写库的有界重试(codex R1-high③):一次瞬断不该永久丢掉一条真 run 的终局。 */
49
18
  const TERMINAL_WRITE_ATTEMPTS = 3;
50
19
  const TERMINAL_WRITE_RETRY_MS = 200;
51
- /** `Idempotency-Key` 的形:1–255 且无首尾空白(与 `/v1/runs` 的 zod `.max(255)` + 店门口
52
- * `assertIdempotencyKeyShape` 同规)。坏形 ⇒ 400,不静默忽略 —— 忽略一个去重键 = 悄悄放行第二次真 push。 */
53
20
  function idemShapeError(raw) {
54
21
  if (raw.length === 0 || raw.length > 255 || raw.trim() !== raw) {
55
22
  return `invalid Idempotency-Key: must be 1-255 chars with no leading/trailing whitespace (got length ${raw.length})`;
@@ -58,10 +25,7 @@ function idemShapeError(raw) {
58
25
  }
59
26
  export function createLeaderEndpoint(runLeader, opts = {}) {
60
27
  const runs = new Map();
61
- /** 降级臂的进程内幂等索引(scope 过的键 → run id)。store 在场时不使用(库上的 UNIQUE 才是执法者)。 */
62
28
  const memIdem = new Map();
63
- // Bound the in-memory registry: a finished leader run stays pollable for RUN_RETENTION_MS, then is evicted.
64
- // Swept on each POST (amortized, no timer) so the map can't grow without limit over a long-lived process.
65
29
  function reap() {
66
30
  const cutoff = Date.now() - RUN_RETENTION_MS;
67
31
  for (const [id, run] of runs) {
@@ -73,13 +37,12 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
73
37
  }
74
38
  }
75
39
  }
76
- /** 后台驱动一条已准入的 run。两腿共用:终局既写本地 Map(drain 门 + 降级臂的读面),又写库(若在场)。 */
77
40
  function drive(id, body) {
78
41
  void (async () => {
79
42
  let terminal;
80
43
  try {
81
44
  const result = await runLeader(body);
82
- const status = statusForLeaderResult(result); // §10.6: ok→completed/failed PLUS needs_human for a held candidate
45
+ const status = statusForLeaderResult(result);
83
46
  terminal = { status: status, result };
84
47
  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 });
85
48
  }
@@ -87,13 +50,7 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
87
50
  terminal = { status: "failed", error: e instanceof Error ? e.message : String(e) };
88
51
  opts.logger?.error?.("leader_run_error", { id, err: terminal.error });
89
52
  }
90
- // 🔴 codex R1-high③(验真后修):**先落库,后改本地记录**。`inflight()` 数的是本地 `running`,
91
- // 而 drain 门读的正是它 —— 反过来写(先改本地再 await 落库)会让 SIGTERM 在终局 SQL 还在飞时看到 0,
92
- // hardShutdown 关掉连接池 ⇒ 终局写丢 ⇒ 别的副本永远读到 `running`,而代码可能已经真 push 过了。
93
- // 顺序反过来之后,这条 run 直到库写**尘埃落定**(成功或彻底失败)才退出 inflight 集。
94
53
  if (opts.store) {
95
- // 有界重试:一次瞬断(连接被回收/主从切换)不该让一条真 run 的终局永久丢失。重试是**有界**的
96
- // ——停机窗里无限重试只会把 drain 拖死,而那时该做的是响亮记一笔让运维手工对账。
97
54
  for (let attempt = 1;; attempt += 1) {
98
55
  try {
99
56
  const moved = await opts.store.finishRun(id, terminal);
@@ -121,7 +78,6 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
121
78
  }
122
79
  })();
123
80
  }
124
- /** GET 的公共出口形(两腿同一份体)。 */
125
81
  function getBody(run) {
126
82
  return {
127
83
  status: 200,
@@ -152,19 +108,12 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
152
108
  const bad = idemShapeError(idempotencyKey);
153
109
  if (bad)
154
110
  return { status: 400, body: { error: bad, errorCode: "request.body_shape" } };
155
- // 🔴 按提交者 scope:两个 principal 用了同一个字面键,不该撞进同一条 run —— 那既是一次别人的
156
- // run id 泄漏(存在性预言机),也会让 B 的提交被 A 的 run 静默吞掉。
157
111
  scopedIdem = leaderIdemKey(idempotencyKey, owner);
158
112
  }
159
113
  reap();
160
114
  if (opts.store) {
161
115
  const { created, run } = await opts.store.createRun({ objective: b.objective, owner, idemKey: scopedIdem });
162
- // 幂等重投:回既有行,**不**起第二条后台腿(第二次 = 第二次真 git push)。
163
116
  if (!created) {
164
- // 🔴 codex R1-high④(验真后修):重放**必须校属主**。`idem_key` 编码的是提交时的 owner,而
165
- // `owner` 列会被身份收编(design/183)重绑 —— 于是旧身份拿旧键重投,可以命中一条**现属他人**的行。
166
- // 直接回显 = 把别人的 leaderRunId 与状态泄漏出去。姿势照 `/v1/runs` 的 clientTaskId 重放先例:
167
- // 属主不符 ⇒ 409 冲突,不回显任何标识(不给跨租户预言机)。
168
117
  if (run.owner != null && run.owner !== owner) {
169
118
  return { status: 409, body: { error: "idempotency key already used by another principal", errorCode: "conflict.leader_run_exists" } };
170
119
  }
@@ -174,7 +123,6 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
174
123
  drive(run.id, b);
175
124
  return { status: 202, body: { leaderRunId: run.id, status: "running" } };
176
125
  }
177
- // 降级臂(无 store):作用域=本进程的同一套语义。
178
126
  if (scopedIdem) {
179
127
  const prior = memIdem.get(scopedIdem);
180
128
  const priorRun = prior ? runs.get(prior) : undefined;
@@ -182,7 +130,7 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
182
130
  return { status: 202, body: { leaderRunId: priorRun.id, status: priorRun.status } };
183
131
  }
184
132
  const id = uuidv7();
185
- runs.set(id, { id, status: "running", startedAt: Date.now(), owner }); // BL-4: record owner
133
+ runs.set(id, { id, status: "running", startedAt: Date.now(), owner });
186
134
  if (scopedIdem)
187
135
  memIdem.set(scopedIdem, id);
188
136
  drive(id, b);
@@ -191,9 +139,6 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
191
139
  const m = method === "GET" ? LEADER_ID_RE.exec(url) : null;
192
140
  if (m) {
193
141
  const id = m[1];
194
- // BL-4 owner check: an owned run is visible ONLY to its owner — 404 (not 403) to anyone else, so a
195
- // non-owner can't even confirm the id exists. owner null/undefined = anonymous/single-tenant run,
196
- // visible to any authenticated caller (back-compat; mirrors GET /v1/runs/:id's runOwnerOk).
197
142
  const ownerOk = (owner) => owner == null || owner === (requester ?? null);
198
143
  if (opts.store) {
199
144
  const run = await opts.store.getRun(id);
@@ -1,4 +1,3 @@
1
- /** A tiny counting semaphore (FIFO-ish) bounding concurrent worker execution. */
2
1
  class Semaphore {
3
2
  max;
4
3
  active = 0;
@@ -19,23 +18,6 @@ class Semaphore {
19
18
  }
20
19
  }
21
20
  }
22
- /**
23
- * 「这条 worker 的 TaskResult 停在门上(durable park)了吗」——`TaskStatus` 的穷举判据。
24
- *
25
- * 🔴 park 是**两个词**(扫描P2,status/kind 同名词混淆病族):`suspended`(工具审批 / human /
26
- * resource_limit 门)与 `needs_review`(`plan_review` 与 dry-run 拦截门)。真源 = core
27
- * `agents/suspend-guard.js` 的 `isDurablePause`(逐字 `status === "suspended" || status === "needs_review"`),
28
- * `mapNestedSuspend` / `verify.js` 的 `unverifiedReason` 两处都按这张表分词。
29
- *
30
- * 只认 `suspended` 的后果不是「少一个标签」:一条 plan_review 停下的 worker 会被报成 `failed` ⇒
31
- * fanOut 的 C4 抢占({@link FanOutOptions.cancelOnSuspend})不触发(其余 worker 继续烧钱)、leader 的
32
- * replan「suspended 波」看不见它、`suspendedSessions` 的豁免名单漏掉它 ⇒ 它那张还能被决议的 checkpoint
33
- * 与暂停中的环境会被当失败清掉。带验证器的那条腿早已改用「还带着 checkpointToken 就是 park」的判据
34
- * (leader.ts round-3 review BLOCKER),这里是它当时漏掉的另外半场。
35
- *
36
- * `never` 收敛 = core `TaskStatus` 增删成员的**编译期**执行点(同 `store-contracts.ts` 两条判据的姿势);
37
- * 运行时未知词保守判**非 park**(与既有 `else → failed` 同行为,不静默扩大 park 面)。
38
- */
39
21
  export function isDurablePauseStatus(s) {
40
22
  switch (s) {
41
23
  case "suspended":
@@ -55,30 +37,17 @@ export function isDurablePauseStatus(s) {
55
37
  function mapStatus(s) {
56
38
  return s === "completed" ? "completed" : isDurablePauseStatus(s) ? "suspended" : "failed";
57
39
  }
58
- /**
59
- * Fan out `subtasks` in parallel. Never throws — every worker resolves to a WorkerReport (failures captured).
60
- * The caller (Coordinator) merges only `status==="completed"` reports.
61
- */
62
40
  export async function fanOut(subtasks, opts) {
63
41
  const sharedAC = new AbortController();
64
42
  const streams = new Map();
65
43
  let cancelled = false;
66
44
  const cancelAll = async () => {
67
45
  cancelled = true;
68
- // Abort FIRST (council round-2): one signal both aborts RUNNING workers (their shared spec.signal) AND
69
- // stops NOT-YET-STARTED ones — runOne's `sharedAC.signal.aborted` guard returns before provisioning a VM.
70
- // Closes the race where a worker registering AFTER the snapshot would miss it. Then destroy() each registered
71
- // stream (core 1.71): abort + await settle + CAS-expire the checkpoint (fences any concurrent resume) + reap
72
- // a SUSPENDED worker's paused container — so a cancel that hits a suspended worker leaks neither a paused
73
- // container nor an orphan checkpoint. The leader (leader.ts) still destroys each worker's static env in its
74
- // own finally; destroy() here reaps the (durable) checkpoint — the two are disjoint (core [R47]).
75
46
  sharedAC.abort();
76
47
  await Promise.allSettled([...streams.values()].map((s) => s.destroy()));
77
48
  };
78
49
  const sem = new Semaphore(opts.maxConcurrency);
79
50
  let pendingCancel;
80
- // C4: a SUSPENDED settle preempts the remaining fan-out (the settled worker is already out of `streams`,
81
- // so cancelAll's destroy sweep is exempt of it by construction — its checkpoint/paused env survive).
82
51
  const maybeCancelOnSuspend = (r) => {
83
52
  if (opts.cancelOnSuspend && r.status === "suspended" && !cancelled) {
84
53
  opts.logger?.info?.("fanout_suspend_cancel", { suspendedWorker: r.workerId });
@@ -91,8 +60,6 @@ export async function fanOut(subtasks, opts) {
91
60
  return sem.run(async () => {
92
61
  if (sharedAC.signal.aborted)
93
62
  return { ...base, status: "timeout", error: "cancelled before start" };
94
- // Per-worker run override (verify path): reuse the semaphore + shared abort signal; map throws like the
95
- // default path so runOne still never rejects (Promise.all stays safe).
96
63
  if (opts.runWorker) {
97
64
  try {
98
65
  return maybeCancelOnSuspend(await opts.runWorker(st, sharedAC.signal));
@@ -115,32 +82,24 @@ export async function fanOut(subtasks, opts) {
115
82
  report = { ...base, status: mapStatus(result.status), stats: result.stats, ...(result.errorMessage ? { error: result.errorMessage } : {}) };
116
83
  }
117
84
  catch (e) {
118
- // interrupted/aborted workers may reject here — report, don't propagate.
119
85
  report = { ...base, status: cancelled ? "timeout" : "failed", error: e instanceof Error ? e.message : String(e) };
120
86
  }
121
87
  finally {
122
88
  streams.delete(st.workerId);
123
89
  }
124
- // AFTER deregistration — so a suspend-triggered cancelAll can never destroy THIS worker's own stream
125
- // (destroying a suspended stream = CAS-expiring its checkpoint, the exact thing C4 forbids).
126
90
  return maybeCancelOnSuspend(report);
127
91
  });
128
92
  };
129
- // Record each worker's report as it settles so the overall deadline can HARD-bound the wait: if a worker's
130
- // stream.result() ignores the abort, `Promise.all(work)` would never resolve and `await work` below would hang
131
- // PAST the deadline (the overall timeout would be only "soft"). On the grace expiry we return whatever settled
132
- // + a synthesized `timeout` for the rest, so fanOut always returns within timeoutMs + the cancel grace.
133
93
  const results = new Map();
134
94
  const work = Promise.all(subtasks.map((st) => runOne(st).then((r) => { results.set(st.workerId, r); return r; })));
135
95
  const timer = new Promise((resolve) => {
136
96
  const id = setTimeout(() => resolve("timeout"), opts.timeoutMs);
137
- void work.then(() => clearTimeout(id)); // don't leak the timer when work finishes first
97
+ void work.then(() => clearTimeout(id));
138
98
  });
139
99
  const raced = await Promise.race([work.then(() => "done"), timer]);
140
100
  if (raced === "timeout") {
141
101
  opts.logger?.warn?.("fanout_timeout", { timeoutMs: opts.timeoutMs, workers: subtasks.length });
142
102
  await cancelAll();
143
- // Hard bound: give cancelAll a grace window to settle the running workers, then stop waiting regardless.
144
103
  const graceMs = opts.cancelGraceMs ?? CANCEL_GRACE_MS;
145
104
  const grace = new Promise((resolve) => {
146
105
  const id = setTimeout(() => resolve("grace"), graceMs);
@@ -154,11 +113,10 @@ export async function fanOut(subtasks, opts) {
154
113
  };
155
114
  }
156
115
  }
157
- const reports = await work; // all settled (normal path, or cancelled workers settled within grace)
116
+ const reports = await work;
158
117
  if (pendingCancel)
159
- await pendingCancel.catch(() => { }); // suspend-triggered sweep fully settled before return
118
+ await pendingCancel.catch(() => { });
160
119
  return { reports, cancelled };
161
120
  }
162
- /** Grace window after `cancelAll` before fanOut returns regardless — bounds a worker that ignores the abort. */
163
121
  const CANCEL_GRACE_MS = 30_000;
164
122
  //# sourceMappingURL=fanout.js.map
@@ -1,48 +1,9 @@
1
- /**
2
- * The OUT-OF-PROCESS grader plumbing for the leader × `runRepairLoop` integration
3
- * (`sema-internal server/docs/LEADER-REPAIRLOOP-INTEGRATION.md` §3.2 + the authoritative §10 layer). Two distinct consumers:
4
- *
5
- * - **R1 (single-agent oracle, §10.1)** — the `RepairOracle` closure (`repair-oracle.ts`) reuses the EXISTING
6
- * integration sandbox as its grader at ZERO extra sandbox cost. It needs only a `GraderHandle` (a writeFile +
7
- * exec bound to that sandbox via the wire's `asIntegrationEnv`/`sh` pattern) and the worker-diff source — it
8
- * grades through `runExecGate(graderEnv, steps)` itself, so it does NOT use `applyAndGrade` (the closure owns
9
- * its hermetic reset+apply inline; `repair-oracle.ts:89`). This file gives the merge path the SAME apply-and-
10
- * grade primitive so the two stay byte-consistent.
11
- *
12
- * - **R2 (measure-drives-repair, §10.4)** — the merge path (`merge.ts`, Stage-2) RE-SEEDS a single minted
13
- * grader per measure round (NOT a new microVM): `git reset --hard <integBase> && git clean -fdx`, re-apply the
14
- * INTEGRATED patch, then run the trusted `measureCmd` there. The repair agent edits the merged sandbox, so the
15
- * measure must NOT run in that (possibly poisoned) sandbox — `applyAndGrade` runs it in a fresh-seeded grader.
16
- * Before re-measure, the caller mechanically checks the patch's changed-file list against an allowlist
17
- * (`changedFilesWithinAllowlist`, §10.4(ii)) — an out-of-allowlist / build-config touch denies the candidate.
18
- *
19
- * A standalone, mint-a-distinct-sandbox grader belongs to the AUTO-ACCEPT slice, which has NOT been designed or
20
- * built. v1 reuses the integration sandbox (R1) + re-seeds it (R2) instead. **There is no config key that turns
21
- * such a factory on** — an earlier revision of this header named one and shipped a `throw`-only stub beside it,
22
- * but no such key ever existed in `config.ts`, so an operator following that text set an env var that did
23
- * nothing. Both the stub and the promise were removed (#193 车4 件3 / staleness ledger P2-9); the auto-accept
24
- * slice, when it happens, is a new design件 that lands its factory, its wiring and its knob in one batch.
25
- *
26
- * Everything here is additive + OFF by default: nothing in this file runs unless the Stage-2 seams call it behind
27
- * `LEADER_REPAIR_LOOP` / `LEADER_MEASURE_GATES`.
28
- */
29
1
  import { runExecGate } from "@sema-agent/core";
30
2
  import { shellQuote as sq } from "../plugins/remote-shell.js";
31
- /**
32
- * §10.1/§10.4 — HERMETIC apply-and-grade. Reset the grader to its clean `integBase` (`git reset --hard` +
33
- * `git clean -fdx`), apply the FULL `base→latest` `patch` (never cumulative onto an already-applied tree), then
34
- * run the TRUSTED, spec-derived `steps` (`testCmd` / `measureCmd`) there. `steps` MUST NOT be worker-authored
35
- * (the `runExecGate` security contract — exec-gate.ts header). Used by the R2 measure grader; the R1 oracle
36
- * closure performs the equivalent inline (`repair-oracle.ts`) so the single-agent and merge paths grade
37
- * identically. Never throws — an apply conflict or a step failure is a non-passing `GradeOutcome`.
38
- */
39
3
  export async function applyAndGrade(handle, patch, steps) {
40
4
  const repo = handle.repoDir;
41
5
  const patchPath = `/tmp/grade-${Date.now()}.patch`;
42
6
  await handle.writeFile(patchPath, patch);
43
- // Hermetic prep: clean base, then apply the full base→latest diff. `--whitespace=nowarn` matches `merge.ts:144`
44
- // and `repair-oracle.ts:93`. An apply failure is a non-flaky, non-applied failure (a candidate that no longer
45
- // applies onto the clean base — e.g. it raced the integration head).
46
7
  const prep = await runExecGate(handle.env, [
47
8
  { command: `git -C ${sq(repo)} reset --hard ${sq(handle.integBase)} && git -C ${sq(repo)} clean -fdx`, label: "grader-reset" },
48
9
  { command: `git -C ${sq(repo)} apply --whitespace=nowarn ${sq(patchPath)}`, label: "grader-apply" },
@@ -57,21 +18,6 @@ export async function applyAndGrade(handle, patch, steps) {
57
18
  const trace = gate.steps.map((s) => `${s.label}\n${s.stdout}\n${s.stderr}`).join("\n").slice(-4000);
58
19
  return { passed: false, applied: true, trace };
59
20
  }
60
- /**
61
- * §10.4(ii) — the mechanical CHANGED-FILES ALLOWLIST guard. A "fresh sandbox" is necessary-NOT-sufficient: poison
62
- * can ride INSIDE the integrated patch (a malicious `build.gradle` / git hook / test shim). Before re-measuring,
63
- * diff the patch's changed-file list against the allowlist = the workers' planned `files` ∪ trusted spec dirs.
64
- * Any out-of-allowlist path, or a build-config touch (gradle / settings / dependency manifests / git hooks), is a
65
- * DENY — the caller downgrades the candidate to `needs_human_oracle` (the prompt-only "do NOT modify build config"
66
- * `wire.ts:269` is unreliable; this makes it mechanical). Returns the offending paths (empty ⇒ within allowlist).
67
- *
68
- * `changedFiles` is parsed by the caller from the patch (`git apply --numstat` / `git diff --name-only`); this is
69
- * the PURE policy over that list so it is unit-testable without a sandbox. Matching is path-prefix on normalized
70
- * POSIX paths: an allowlist entry `"."` or `""` allows everything UNDER it (a worker that legitimately owns the
71
- * whole tree); a file allow-entry matches exactly; a dir allow-entry (trailing `/` or a real dir) matches its
72
- * subtree. Build-config patterns are denied even when nominally within the allowlist (a worker must not be able
73
- * to launder a `build.gradle` edit through an over-broad `files:["."]`).
74
- */
75
21
  const BUILD_CONFIG_DENY = [
76
22
  /(^|\/)build\.gradle(\.kts)?$/,
77
23
  /(^|\/)settings\.gradle(\.kts)?$/,
@@ -96,12 +42,11 @@ const norm = (p) => p.replace(/^\.\/+/, "").replace(/\/+$/, "");
96
42
  function withinAllowEntry(file, entry) {
97
43
  const e = norm(entry);
98
44
  if (e === "" || e === ".")
99
- return true; // owns the whole tree
45
+ return true;
100
46
  if (file === e)
101
- return true; // exact file match
102
- return file.startsWith(`${e}/`); // subtree match
47
+ return true;
48
+ return file.startsWith(`${e}/`);
103
49
  }
104
- /** The §10.4(ii) decision: the subset of `changedFiles` that is OUT of the allowlist or hits a build-config deny. */
105
50
  export function changedFilesOutOfAllowlist(changedFiles, allowlist) {
106
51
  const files = changedFiles.map(norm).filter((f) => f !== "");
107
52
  const offending = [];
@@ -115,21 +60,7 @@ export function changedFilesOutOfAllowlist(changedFiles, allowlist) {
115
60
  }
116
61
  return offending;
117
62
  }
118
- /** §10.4(ii): true iff EVERY changed file is within the allowlist and none is a denied build-config path. */
119
63
  export function changedFilesWithinAllowlist(changedFiles, allowlist) {
120
64
  return changedFilesOutOfAllowlist(changedFiles, allowlist).length === 0;
121
65
  }
122
- /*
123
- * §3.2 的「standalone 铸新沙箱 grader 工厂」曾以 `GraderEnvFactoryCfg` / `MintedGrader` / `createGraderEnvFactory`
124
- * 三件的形式停在这里:一个固定好的签名 + 一个必抛的 not-implemented 实现,理由写的是「签名先定好,将来
125
- * 自动接受切片直接接线,不改契约」。#193 车4(台账 P2-9)裁定删除,理由三条:
126
- * ① 它零调用零测试,而它承诺的开关(见文件头注)在 `config.ts` 里从来不存在 —— 「签名已定」这句话
127
- * 本身在骗读者:没有任何消费方按它写过一行码,所谓的契约稳定性没有被任何东西验证过;
128
- * ② 一个 `throw` 工厂在 grep 结果里长得像「这条路已经有了」,恰恰是本仓 doc-rot 战役反复清理的那一类
129
- * 「过期的在哪里比缺失的在哪里更危险」;
130
- * ③ 真需求出现时,铸沙箱 + 播种 + oracle 隔离本来就要跟着 `assertOracleIsolation` 一起重新设计
131
- * (§10.2 明说 v1 OMITS immutableOraclePaths),旧签名大概率也不会被原样采用。
132
- * 复原坐标(需要时按此重建):sema-internal `server/docs/LEADER-REPAIRLOOP-INTEGRATION.md` §3.2/§7,
133
- * 以及本文件的 git 历史(#193 车4 之前的任一版本)。
134
- */
135
66
  //# sourceMappingURL=grader-env-factory.js.map