@sema-agent/server 7.151.0 → 7.152.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 (286) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +15 -3
  3. package/README.zh-CN.md +10 -2
  4. package/USAGE.md +93 -64
  5. package/deploy/sema-up/README.md +5 -4
  6. package/deploy/sema-up/cluster-up.sh +6 -6
  7. package/deploy/sema-up/kube-up.sh +5 -5
  8. package/deploy/sema-up/lib/flagshape.sh +36 -8
  9. package/deploy/sema-up/machines.example.yaml +3 -1
  10. package/deploy/sema-up/sema-up.sh +22 -16
  11. package/dist/approval-ask-audit-store.d.ts +1 -1
  12. package/dist/approval-ask-machine.d.ts +4 -10
  13. package/dist/approval-ask-retention.d.ts +1 -1
  14. package/dist/approval-card.d.ts +10 -12
  15. package/dist/approval-gate-currency.d.ts +8 -8
  16. package/dist/approval-gate-currency.js +3 -4
  17. package/dist/approval-hmac.d.ts +17 -1
  18. package/dist/approval-hmac.js +7 -1
  19. package/dist/approval-queue-kind.d.ts +1 -1
  20. package/dist/approval.d.ts +2 -2
  21. package/dist/approval.js +2 -2
  22. package/dist/bind-host.d.ts +1 -3
  23. package/dist/bind-host.js +2 -3
  24. package/dist/boot/coordinators.d.ts +1 -1
  25. package/dist/boot/coordinators.js +9 -10
  26. package/dist/boot/device-lane.js +2 -0
  27. package/dist/boot/execution-env.d.ts +1 -0
  28. package/dist/boot/execution-env.js +31 -5
  29. package/dist/boot/leader.d.ts +31 -30
  30. package/dist/boot/leader.js +38 -28
  31. package/dist/boot/leg-path-adjudication.d.ts +40 -0
  32. package/dist/boot/leg-path-adjudication.js +18 -0
  33. package/dist/boot/parked-revive-gate.js +3 -15
  34. package/dist/boot/reapers.d.ts +1 -1
  35. package/dist/boot/reapers.js +1 -1
  36. package/dist/boot/resolve-spec.js +12 -26
  37. package/dist/boot/runner-deps.d.ts +39 -35
  38. package/dist/boot/runner-deps.js +92 -89
  39. package/dist/boot/shutdown.d.ts +6 -0
  40. package/dist/boot/shutdown.js +10 -4
  41. package/dist/boot/stage-02-web-admin-pull.js +4 -5
  42. package/dist/boot/stage-05-execution-env.js +2 -1
  43. package/dist/boot/stage-06-runners.d.ts +2 -0
  44. package/dist/boot/stage-06-runners.js +10 -4
  45. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  46. package/dist/boot/stage-07-capability-layer.js +1 -1
  47. package/dist/boot/stage-08-reapers.d.ts +1 -0
  48. package/dist/boot/stage-09-leader.d.ts +1 -0
  49. package/dist/boot/stage-09-leader.js +2 -2
  50. package/dist/boot/stage-10-http-server.d.ts +8 -0
  51. package/dist/boot/stage-10-http-server.js +11 -15
  52. package/dist/boot/stores.js +7 -4
  53. package/dist/boot/web-admin.js +2 -7
  54. package/dist/capabilities/approvals-push.d.ts +3 -3
  55. package/dist/capabilities/scenarios.d.ts +28 -13
  56. package/dist/capabilities/scenarios.js +2 -2
  57. package/dist/capability-facts.d.ts +22 -20
  58. package/dist/capability-facts.js +18 -34
  59. package/dist/card-rule-offers.d.ts +32 -0
  60. package/dist/card-rule-offers.js +130 -0
  61. package/dist/config-catalog.js +12 -11
  62. package/dist/config-dir-trust.js +4 -2
  63. package/dist/config-invariants.d.ts +18 -7
  64. package/dist/config-invariants.js +51 -8
  65. package/dist/config-lkg.d.ts +5 -19
  66. package/dist/config-lkg.js +8 -62
  67. package/dist/config-provider.d.ts +1 -1
  68. package/dist/config-root-migration.d.ts +2 -2
  69. package/dist/config-types.d.ts +28 -38
  70. package/dist/config.d.ts +7 -5
  71. package/dist/config.js +55 -16
  72. package/dist/control-socket.d.ts +51 -0
  73. package/dist/control-socket.js +115 -0
  74. package/dist/deployment-governance.d.ts +2 -1
  75. package/dist/device-ws-hub.d.ts +7 -0
  76. package/dist/device-ws-hub.js +7 -1
  77. package/dist/elicitation.js +2 -2
  78. package/dist/entry-problems.d.ts +40 -3
  79. package/dist/entry-problems.js +3 -1
  80. package/dist/execution-env-refusal.d.ts +8 -2
  81. package/dist/execution-env-refusal.js +3 -0
  82. package/dist/fleet/engine-signal.d.ts +12 -29
  83. package/dist/fleet/engine-signal.js +1 -7
  84. package/dist/fleet-client.js +2 -5
  85. package/dist/gate-settlement.d.ts +1 -8
  86. package/dist/gate-settlement.js +0 -4
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +2 -3
  89. package/dist/hitl-delivery.js +0 -2
  90. package/dist/hooks/hook-llm.js +4 -2
  91. package/dist/hooks/hook-runner.d.ts +3 -2
  92. package/dist/hooks/hook-runner.js +16 -1
  93. package/dist/host-decision.d.ts +11 -11
  94. package/dist/http/active-run-conflict.d.ts +35 -2
  95. package/dist/http/active-run-conflict.js +48 -19
  96. package/dist/http/approval-sweeps.d.ts +1 -1
  97. package/dist/http/approval-sweeps.js +5 -2
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +6 -2
  101. package/dist/http/dispatch.js +25 -5
  102. package/dist/http/durable-run-leg.js +0 -1
  103. package/dist/http/health-reader.d.ts +16 -9
  104. package/dist/http/health-reader.js +2 -1
  105. package/dist/http/health-self-report.js +1 -1
  106. package/dist/http/owner-access.d.ts +2 -3
  107. package/dist/http/owner-access.js +3 -4
  108. package/dist/http/principal-gate.d.ts +49 -21
  109. package/dist/http/principal-gate.js +61 -11
  110. package/dist/http/request-channel.d.ts +23 -0
  111. package/dist/http/request-channel.js +9 -0
  112. package/dist/http/request-guards.js +17 -2
  113. package/dist/http/resume-ctx-read.js +20 -3
  114. package/dist/http/resume-legs.d.ts +2 -2
  115. package/dist/http/resume-legs.js +111 -41
  116. package/dist/http/route-ctx.d.ts +73 -7
  117. package/dist/http/route-ctx.js +17 -0
  118. package/dist/http/route-table.d.ts +4 -0
  119. package/dist/http/route-table.js +4 -0
  120. package/dist/http/routes/admin-config-refresh.js +3 -3
  121. package/dist/http/routes/admin-drain.d.ts +3 -0
  122. package/dist/http/routes/admin-drain.js +7 -7
  123. package/dist/http/routes/admin-memory-optout.js +3 -3
  124. package/dist/http/routes/adoption.js +3 -5
  125. package/dist/http/routes/approvals-assistant.d.ts +2 -1
  126. package/dist/http/routes/approvals-assistant.js +95 -48
  127. package/dist/http/routes/capabilities.d.ts +4 -0
  128. package/dist/http/routes/capabilities.js +30 -5
  129. package/dist/http/routes/config-catalog.js +2 -2
  130. package/dist/http/routes/devices.js +1 -1
  131. package/dist/http/routes/diagnostics.d.ts +3 -3
  132. package/dist/http/routes/diagnostics.js +19 -7
  133. package/dist/http/routes/images.js +10 -10
  134. package/dist/http/routes/memory-bundle.js +3 -3
  135. package/dist/http/routes/memory-compliance.js +3 -3
  136. package/dist/http/routes/memory-consolidation.js +2 -2
  137. package/dist/http/routes/memory-origin.js +3 -3
  138. package/dist/http/routes/memory-policy.js +2 -2
  139. package/dist/http/routes/observability.js +3 -3
  140. package/dist/http/routes/retention-ops.js +3 -3
  141. package/dist/http/routes/rules.js +5 -5
  142. package/dist/http/routes/run-permission-mode.d.ts +2 -1
  143. package/dist/http/routes/run-permission-mode.js +2 -2
  144. package/dist/http/routes/runs.d.ts +4 -9
  145. package/dist/http/routes/runs.js +73 -33
  146. package/dist/http/routes/session-sync.d.ts +18 -6
  147. package/dist/http/routes/session-sync.js +38 -36
  148. package/dist/http/routes/sessions.js +20 -9
  149. package/dist/http/routes/tasks.js +28 -37
  150. package/dist/http/routes/workflows.js +1 -1
  151. package/dist/http/server.d.ts +9 -3
  152. package/dist/http/server.js +15 -3
  153. package/dist/http/submit-ctx.d.ts +1 -1
  154. package/dist/http/submit-ctx.js +2 -0
  155. package/dist/human-facing-args.d.ts +33 -0
  156. package/dist/human-facing-args.js +33 -0
  157. package/dist/index.d.ts +1 -1
  158. package/dist/k8s-api-credentials.d.ts +29 -0
  159. package/dist/k8s-api-credentials.js +25 -0
  160. package/dist/lane-home-probe.d.ts +1 -1
  161. package/dist/lane-home-probe.js +2 -1
  162. package/dist/leader/diffup.d.ts +19 -0
  163. package/dist/leader/diffup.js +7 -0
  164. package/dist/leader/leader.d.ts +114 -10
  165. package/dist/leader/leader.js +69 -5
  166. package/dist/leader/repair-wire.js +2 -2
  167. package/dist/leader/run-entry.d.ts +2 -1
  168. package/dist/leader/run-entry.js +1 -1
  169. package/dist/leader/wire.d.ts +42 -132
  170. package/dist/leader/wire.js +81 -67
  171. package/dist/memory-scope.d.ts +20 -9
  172. package/dist/memory-scope.js +15 -3
  173. package/dist/model-entry-headers.d.ts +9 -10
  174. package/dist/model-entry-headers.js +0 -4
  175. package/dist/model-entry-refusal.d.ts +7 -4
  176. package/dist/model-entry-refusal.js +11 -5
  177. package/dist/model-plane-face.d.ts +1 -1
  178. package/dist/model-plane-face.js +3 -1
  179. package/dist/model-route-endpoint.d.ts +13 -7
  180. package/dist/model-route-endpoint.js +14 -11
  181. package/dist/model-select.d.ts +22 -0
  182. package/dist/model-select.js +9 -0
  183. package/dist/model-unreachable-notice.d.ts +11 -0
  184. package/dist/model-unreachable-notice.js +4 -4
  185. package/dist/object-store-buckets.d.ts +640 -6
  186. package/dist/object-store-buckets.js +166 -0
  187. package/dist/observability/fail-open.d.ts +20 -16
  188. package/dist/observability/fail-open.js +20 -16
  189. package/dist/observability/logger.js +3 -1
  190. package/dist/operator-roster.d.ts +68 -0
  191. package/dist/operator-roster.js +32 -0
  192. package/dist/orchestration/workflow-park-decision.d.ts +1 -1
  193. package/dist/outbound-dispatcher.d.ts +71 -8
  194. package/dist/outbound-dispatcher.js +48 -7
  195. package/dist/owner-faces.d.ts +1 -1
  196. package/dist/parked-decide.d.ts +2 -2
  197. package/dist/permission-mode-admission.d.ts +59 -26
  198. package/dist/permission-mode-admission.js +37 -10
  199. package/dist/plugins/approval-ask-store-file.d.ts +4 -0
  200. package/dist/plugins/approval-ask-store-file.js +3 -0
  201. package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
  202. package/dist/plugins/approval-ask-store-memory.js +7 -1
  203. package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
  204. package/dist/plugins/approval-ask-store-sql.js +12 -1
  205. package/dist/plugins/blob-backend.d.ts +83 -7
  206. package/dist/plugins/blob-backend.js +57 -6
  207. package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
  208. package/dist/plugins/checkpoint-index-columns.js +3 -6
  209. package/dist/plugins/checkpoint-store-face.d.ts +119 -61
  210. package/dist/plugins/checkpoint-store-face.js +134 -55
  211. package/dist/plugins/landed-attendants.d.ts +2 -2
  212. package/dist/plugins/local-checkpoint-store.d.ts +13 -3
  213. package/dist/plugins/local-checkpoint-store.js +38 -49
  214. package/dist/plugins/one-time-migrations.d.ts +51 -21
  215. package/dist/plugins/one-time-migrations.js +67 -45
  216. package/dist/plugins/pg-pool.js +11 -9
  217. package/dist/plugins/remote-env-host.d.ts +29 -10
  218. package/dist/plugins/remote-env-host.js +99 -19
  219. package/dist/plugins/remote-env-k8s.d.ts +68 -2
  220. package/dist/plugins/remote-env-k8s.js +107 -24
  221. package/dist/plugins/replica-registration.d.ts +1 -1
  222. package/dist/plugins/replica-registration.js +1 -1
  223. package/dist/plugins/s3-presign.d.ts +23 -0
  224. package/dist/plugins/s3-presign.js +37 -3
  225. package/dist/plugins/scheduler-support.d.ts +2 -2
  226. package/dist/plugins/scheduler-support.js +0 -3
  227. package/dist/plugins/schema-indexes.js +2 -0
  228. package/dist/plugins/send-file-ledger.d.ts +2 -2
  229. package/dist/plugins/send-file-ledger.js +2 -2
  230. package/dist/plugins/send-user-file.d.ts +3 -4
  231. package/dist/plugins/send-user-file.js +56 -52
  232. package/dist/plugins/session-blob-claim-token.d.ts +15 -0
  233. package/dist/plugins/session-blob-claim-token.js +20 -0
  234. package/dist/plugins/session-blob-namespace.d.ts +46 -0
  235. package/dist/plugins/session-blob-namespace.js +116 -0
  236. package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
  237. package/dist/plugins/snapshot-bucket-presence.js +8 -2
  238. package/dist/plugins/sql-json-column.d.ts +1 -1
  239. package/dist/plugins/store-backend.d.ts +44 -2
  240. package/dist/plugins/store-backend.js +75 -53
  241. package/dist/plugins/task-attachment-store.d.ts +2 -1
  242. package/dist/plugins/task-attachment-store.js +5 -1
  243. package/dist/plugins/tidb-pool.js +19 -24
  244. package/dist/plugins/workspace-custody.d.ts +4 -3
  245. package/dist/question.d.ts +2 -1
  246. package/dist/question.js +5 -13
  247. package/dist/request-form.d.ts +6 -1
  248. package/dist/request-form.js +4 -1
  249. package/dist/rule-issue-reading.d.ts +1 -1
  250. package/dist/rules-consent.d.ts +1 -1
  251. package/dist/run-local.js +8 -9
  252. package/dist/runs.d.ts +17 -8
  253. package/dist/runs.js +15 -2
  254. package/dist/runtime-governance.d.ts +2 -2
  255. package/dist/sema-config-dir.d.ts +2 -1
  256. package/dist/session-sync.d.ts +27 -11
  257. package/dist/session-sync.js +15 -7
  258. package/dist/stores-sql/blob-index-sql.d.ts +30 -9
  259. package/dist/stores-sql/blob-index-sql.js +40 -21
  260. package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
  261. package/dist/stores-sql/checkpoint-store-sql.js +70 -146
  262. package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
  263. package/dist/stores-sql/file-history-store-sql.js +100 -28
  264. package/dist/stores-sql/retention-store-sql.js +3 -9
  265. package/dist/task-settings.d.ts +6 -5
  266. package/dist/task-settings.js +3 -3
  267. package/dist/tool-approval.d.ts +342 -518
  268. package/dist/tool-approval.js +498 -1207
  269. package/dist/trace/core-keyset-guard.d.ts +1 -1
  270. package/dist/trace/ledger-events.d.ts +3 -3
  271. package/dist/trace/project.d.ts +2 -3
  272. package/dist/trace/redact.js +3 -2
  273. package/dist/trace/self-minted-wire.d.ts +287 -157
  274. package/dist/trace/self-minted-wire.js +93 -48
  275. package/dist/web-admin-client/apply-effective.js +2 -2
  276. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  277. package/dist/web-admin-client/read-warnings.js +1 -1
  278. package/dist/web-admin-client/types.d.ts +1 -1
  279. package/dist/workspace-space.d.ts +10 -4
  280. package/dist/workspace-space.js +4 -1
  281. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
  282. package/package.json +6 -2
  283. package/scripts/embed-minimal.mjs +3 -3
  284. package/wire-changes/server@7.152.0.json +900 -0
  285. package/dist/config-d-migration.d.ts +0 -37
  286. package/dist/config-d-migration.js +0 -60
@@ -1,15 +1,14 @@
1
- import { type Brain, type Model, type ModelRoles, type ModelPricing, type TaskSpec, type ExecutionEnvFactory, type RemoteExecutionEnv, type ToolResultStore, type SessionStore, type ExecutionEnv, type RunnerDeps, type CheckpointToken } from "@sema-agent/core";
1
+ import { type TaskSpec, type ExecutionEnvFactory, type RemoteExecutionEnv, type ToolResultStore, type SessionStore, type ExecutionEnv, type CheckpointToken } from "@sema-agent/core";
2
2
  import type { Autonomy } from "../runtime-governance.js";
3
- import { type CheckpointCtxSealer } from "../http/checkpoint-ctx.js";
4
- import type { CheckpointStoreFull } from "../plugins/store-backend.js";
5
- import { type LeaderDeps, type LeaderResult } from "./leader.js";
6
- import { type DeploymentPostureSeats } from "../boot/runner-deps.js";
3
+ import { type LeaderDiffStore } from "./diffup.js";
4
+ import { type LeaderBirth, type LeaderDurableSeat, type LeaderDeps, type LeaderResult } from "./leader.js";
5
+ import type { DeploymentPlaneDeps } from "../boot/runner-deps.js";
6
+ import type { SandboxPathEnvSlots } from "../boot/deferred-sandbox-path-env.js";
7
7
  import type { LeaderRequestBody } from "./endpoint.js";
8
8
  /**
9
- * leader worker 停驻行的两只 scope —— **唯一属主**(审批停驻 / 资源分片停驻各一只;保留标识见 `principal-shape.ts` 的 `RESERVED_PRINCIPALS` /
10
- * `RESERVED_PRINCIPAL_PREFIXES`)。worker 的 durable spec 与取消时的残留过期({@link createLeaderParkExpirer})读这同一对。
9
+ * leader worker 资源分片停驻行的 scope —— **唯一属主**(保留前缀见 `principal-shape.ts` 的 `RESERVED_PRINCIPAL_PREFIXES`;审批停驻那一只
10
+ * `LEADER_APPROVAL_SCOPE` 的属主是出生口所在的 `leader.ts`)。worker 的资源分片 spec 与取消时的残留过期({@link createLeaderParkExpirer})读这同一对。
11
11
  */
12
- export declare const LEADER_APPROVAL_SCOPE = "_leader";
13
12
  export declare function leaderResourceScope(workerId: string): string;
14
13
  /** 过期残留停驻要的那两口(SQL 两方言与 local 店都有;结构型,单测用假件)。 */
15
14
  export interface LeaderParkStore {
@@ -30,18 +29,6 @@ export declare function createLeaderParkExpirer(store: LeaderParkStore): (worker
30
29
  readonly workerId: string;
31
30
  readonly sessionId: string;
32
31
  }>) => Promise<LeaderParkExpiry>;
33
- /** leader worker 的 durable 审批座(`LeaderWireConfig.durable` 函数席每次产出的一份)。 */
34
- export interface LeaderDurableSeat {
35
- readonly checkpointStore: CheckpointStoreFull;
36
- /** [ref]:写 worker 续跑上下文的封口(`ServiceDeps.checkpointCtxSealing` 同一只;规则属主 `http/checkpoint-ctx.ts` 末段「凭据位密封」)。
37
- * worker 的续跑体只有入口词 + 子任务 objective、不带凭据位 ⇒ 实际不封任何值,但写 ctx 的每一笔都经同一只封口(没有不封的第二种写法)。 */
38
- readonly ctxSealer: CheckpointCtxSealer | undefined;
39
- readonly requireApproval: string[];
40
- readonly deny?: string[];
41
- readonly ttlMs?: number;
42
- readonly autoBudget?: number;
43
- readonly neverAuto?: string[];
44
- }
45
32
  export interface LeaderWireConfig {
46
33
  /** Static-env compat mode (E2B only): the leader provisions/owns each env. Required when `envFactory` unset. */
47
34
  e2bApiKey?: string;
@@ -49,17 +36,17 @@ export interface LeaderWireConfig {
49
36
  * REMOTE_EXEC (e2b or k8s/Kata) — workers and the integration sandbox run on envs from THIS factory.
50
37
  * Requires `s3` (diff-out is the worker's self-upload; there is no leader-held env to pull from). */
51
38
  envFactory?: ExecutionEnvFactory;
52
- /** Durable suspend/resume for SUB-WORKERS ([ref] C4): when the seat yields a value, that worker task carries
53
- * the durable question/ask policy + checkpoint store, so a gated ask/AskUserQuestion SUSPENDS the worker
54
- * (checkpoint + paused env) instead of failing — the trigger replan-lite C4 arbitrates on. Mirrors main.ts's
55
- * per-task durable wiring; ctx is seeded at provision so the surfaced token is resumable on the single-agent path.
56
- *
57
- * 🔴 **函数席,不是快照**([ref] 波 2 ①;先例 = 下方 `deploymentPosture` 的 [ref] 函数席):审批名单
58
- * (`governance.approvalRequire`)自本批起在进程内会被中心热换。boot 期取一次值会把这条腿钉在起服那一刻的名单上
59
- * —— 起服时名单为空 ⇒ 整块缺席、之后加进来的工具在 leader worker 上**永远**不被门住(fail-OPEN)。
60
- * `provisionWorker` 每只 worker 调一次(同一只 worker 的 toolPolicy / resource 停车 / ctx 保温读同一份);
61
- * 返回 `undefined` = 这只 worker 不接 durable 审批(与修前「键缺席」同义)。 */
62
- durable?: () => LeaderDurableSeat | undefined;
39
+ /** 部署的停驻设施([ref] C4):键在场 ⟺ 部署接了停驻店。worker 首跑的出生点按车道决定递不递给这条腿:工厂车道 = 递(worker 的环境由它自己的 Runner
40
+ * 经工厂铸,停驻续得上);静态车道 = `leader.ts` 的 `staticLaneWorkerPark`(上一发布版的条件:另要任一审批名单在场,见 {@link approvalListInForce})。
41
+ * 递了且续跑上下文真落了 ⇒ 这条腿**耐久**(唯一读者 `leader.ts` 的 `leaderLegDurability`):停驻设施进 spec、ctx 保温、开资源分片停驻。
42
+ * 函数席:`provisionWorker` 每只 worker 调一次,同一只 worker 的停驻设施 / ctx 保温 / 资源分片读同一份。 */
43
+ durable?: () => LeaderDurableSeat;
44
+ /** 这台部署此刻有没有任一审批名单在场(`APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO`,含中心热换的那一张;装配处闭包同一个活 config,与
45
+ * {@link baseline} 同源)。**唯一读者** = 静态车道 worker 的停驻设施条件(`leader.ts` 的 `staticLaneWorkerPark`,上一发布版的条件);引擎提供
46
+ * 「宿主声明本腿审批停驻不可续」的声明位后,那条条件退役,本席随之删。函数席:每只 worker 现读一次。 */
47
+ approvalListInForce: () => boolean;
48
+ /** 审批基线的两臂(`LeaderBirth.baseline`;装配处闭包活 config,leader 不读 config)—— 每一条腿出生时经 {@link withLeaderLeg} 按它的耐久事实取一臂。 */
49
+ baseline: LeaderBirth["baseline"];
63
50
  /** Durable tool-result offload store (TiDB). REQUIRED for `durable` to actually suspend: core's suspend
64
51
  * pre-commit refuses with the default InMemoryToolResultStore (offloaded results would deref to null after
65
52
  * a cross-replica resume) and silently falls back to the synchronous onAsk gate — which the leader does not
@@ -70,109 +57,35 @@ export interface LeaderWireConfig {
70
57
  * the resume dies with SessionError "Entry <leafId> not found" (found live: drill c3 deny path). */
71
58
  sessionStore?: SessionStore;
72
59
  /**
73
- * [ref] 件B/C/D([ref],codex R2-F1)—— 部署治理三座席,**原样递给本车道的每一只 Runner**。
74
- * 主车道的 `boot/governance-seams.ts` 是唯一属主(判据、拒启、解析全在那里),本字段只是把它的产物
75
- * 搬过来:一个进程里「这台部署禁哪些能力、锁了哪些键」只能有一个答案。缺席 = 与接线前逐字相同。
60
+ * **部署平面层**(唯一铸点 `boot/runner-deps.ts` 的 `createDeploymentPlaneDeps`;起服第 6 段交出、`boot/leader.ts` 递进来)—— 本车道的每一只
61
+ * `new Runner` 头一项展开它,零逐席搬运:收紧面(治理三座 / 按主体准入 resolver / MCP 撤销台账 / 部署姿态座席族 / 豁免根)、计量面(tracer /
62
+ * 治理窗两键)、服务表(brain / 模型目录 / 定价 / 提示词目录)、部署座 / 手带、两条披露出口(通告 / 错误汇)。新的部署键进那一层即到达这里。
63
+ * 🔴 **函数席**([ref] 先例):姿态族与模型目录会被中心热换,本车道的 Runner 逐调用现构 ⇒ 每只构造时现铸一份,与主车道经 swap 门换代同代。
76
64
  */
77
- governance?: Pick<RunnerDeps, "compliancePostureResolver" | "lockedConfig" | "retentionPolicy">;
78
- /**
79
- * [ref](黑板 [ref])—— **部署姿态座席族**(readFace 四席 `readFace`/`readDenyPatterns`/`readDenyBuiltinTiers`/
80
- * `readDenyBuiltinExclude` + `memoryDelegationEvidence`/`memoryProvenance`/`memoryCapturePolicy`/
81
- * `delegationEntryCaps`),**原样递给本车道的每一只 Runner**。唯一属主 = `boot/runner-deps.ts` 的
82
- * `buildDeploymentPostureSeats(config)`(主 runner / subRunner / run-local 的共享基座展开的就是它),
83
- * `boot/leader.ts` 把**同一函数、同一 config** 的产物搬过来:一个进程里「这台部署的 READ 面 / 记忆姿态 /
84
- * 委派 caps」只能有一个答案。缺席 = 展开空对象 = 五处字面量键缺席(与接线前逐字相同)。
85
- * 修前(≤7.53)这五只 Runner 一席都没有:运维写的 READ_DENY_PATTERNS / MEMORY_DELEGATION_EVIDENCE 等对
86
- * leader 编排的 planner / worker / repair / conflict 四类任务**静默不生效**(core 内建 deny 表仍在)。
87
- *
88
- * 🔴 **是 provider 不是快照**([ref] 追加,codex 对抗复审 r1 [high] 验真后修):本车道的 Runner 是
89
- * **逐调用现构**的,而 [ref] 起 `config.readFace` 在进程内**会变**(center 下发的 read face 转成了
90
- * `Runner.swapDeps` 热换席)。boot 期取一次值再五处复用,等于让 leader 车道永远停在起服那一刻的 READ
91
- * 姿态:读面(`/v1/capabilities`、`/v1/diagnostics/wiring`)报新档、`restart.reasons` 也不再要求重启,
92
- * 而**新建的 leader Runner 仍在旧档上跑** —— 组织把 `open` 收紧成 `roots` 之后这条腿还在 open,方向是
93
- * fail-OPEN。改成函数席之后每只 Runner 构造时现读,与主车道经 swap 门换代**同代**;已经准备好的腿
94
- * 保留它准备时的那一份(core 的自然快照语义,两条腿同律)。 */
95
- deploymentPosture?: () => DeploymentPostureSeats;
65
+ plane: () => DeploymentPlaneDeps;
96
66
  /**
97
- * 运维 `AUTONOMY`(活 config 的那一位)—— 本车道每条 leader run **出生那一拍**读一次,经 `run-entry.ts` 的 `leaderRunEntry`(入口地板 + 受理门同一只入口函数 `entryPermissionMode`)
98
- * 得出这条 run 的入口体(`LeaderDeps.entry`);本 run 铸的每一条 spec 与 worker 的续跑体都从它取词。入口词是 `plan` 的 run 进不了这条车道
99
- * (`leaderEntryRefusal`:worker 会话没有计划审阅通道)⇒ 出生处即以拒句收场;HTTP 受理口用同一只判据先答 422。
100
- *
101
- * 🔴 **函数席**(与 `deploymentPosture` / `durable` 同一条先例):`config.autonomy` 被 web-admin 原地热改,boot 期取一次值会把这条腿钉在起服那一刻。
102
- * **必填**:没有 AUTONOMY 的装配递 `() => undefined` —— 缺席不是「没有地板」的合法拼法(权限轴上不许静默放宽)。
103
- */
104
- autonomy: () => Autonomy | undefined;
105
- /**
106
- * [ref]([ref])—— **计费/遥测追踪席**,原样递给本车道的每一只 Runner。唯一属主 =
107
- * `boot/budget-tracing.ts` 的 `createTracer(...)`(主 runner / subRunner / hook runner 经共享基座吃的
108
- * 就是它),`boot/leader.ts` 搬的是**同一只**。
109
- *
110
- * 🔴 为什么这一席是承重的:`src/budget.ts` 的 `createTracer` 是 `brain.call` **唯一**的记账点 ——
111
- * `costQuota.add` / `fleetUsage.record` / `fleetLease.recordSpend` / `model_cost_micro_usd_total` 四个写口
112
- * 全在那里(`createBrain` 自己不记账)。而 `POST /v1/leader` 的准入处**读**的正是 costQuota / fleetLease
113
- * 这两个桶。缺席 ⇒ 门查的桶与花钱的手不是同一只:`MAX_PRINCIPAL_COST_USD` 与 fleet 租约对这条腿结构性
114
- * 失效,而一条 leader run 扇出 ≤6 个带手 worker + planner + repair/conflict 环,全是实钱。
115
- * 归因的另一半在 `leader/endpoint.ts` 的 `drive()`(ALS `withSubject`)—— 记账臂按 `currentPerson()`
116
- * 分租户,只补席不包裹 = 归因恒 undefined = 假绿。缺席 ⇒ 展开空对象(与接线前逐字相同)。
67
+ * 部署治理折叠(`LeaderBirth.govern`;装配处闭包活 config,leader 不读 config)—— 每一条腿出生时经 {@link withLeaderLeg} 调一次。
117
68
  */
118
- tracer?: RunnerDeps["tracer"];
69
+ govern: LeaderBirth["govern"];
119
70
  /**
120
- * **部署座**(`RunnerDeps.mods`),原样递给本车道的每一只 Runner。唯一铸点 = 起服第 5 段的装配口 `assembleDeploymentMods`(本服务自铸的注册在前、
121
- * 运维声明的 mod 包在后、整列过引擎准入);主 / 无手 / hook 代理 / 子代经共享基座吃的就是它,`boot/leader.ts` 搬的是**同一份**。
122
- * leader 不自铸、不自己装配(一个进程两份部署注册 = 主车道与 leader 车道各按一套扩展跑)。缺失时整个部署座在这条车道上缺席:运维声明的守卫包
123
- * 管不到 leader 派出的腿,拒绝计量 / 工具追踪也漏这条腿。缺席 ⇒ 展开空对象。
71
+ * 每条腿的 per-model 凭据席(`LeaderBirth.auth`;装配处铸,与任务面同一只活引用)—— 本车道每一只 Runner 跑的 spec 都带它:五个出生点经 {@link withLeaderLeg},
72
+ * planner 路由在它的 Runner 构造那一拍取一只。**必填**:没有 per-model 钥匙的装配照样递(席对每只模型答「没有自己的钥」⇒ 网关钥,与不递逐字同)。
124
73
  */
125
- mods?: RunnerDeps["mods"];
74
+ auth: LeaderBirth["auth"];
126
75
  /**
127
- * **手带**(`RunnerDeps.hands`),原样递给本车道的每一只 Runner。唯一铸点 = `boot/runner-deps.ts` 的 `buildHandsBand`(共享基座调的同一只):
128
- * 前台命令超时转后台读部署旋钮 `AUTO_BACKGROUND_ON_TIMEOUT`,本车道不署名。缺失时本车道的 Runner 没有手带(超时即杀、不署名)。缺席 ⇒ 展开空对象。
76
+ * 腿的路径裁决的槽表(进程内唯一那一只,`boot/deferred-sandbox-path-env.ts`)。本车道**手里握着 env** 的腿 —— 静态车道的 worker、集成修复腿 ——
77
+ * 的 env 不是部署工厂为那条腿铸的,工厂装饰器登记不到;每一次 run 由本车道按跑它的会话号登记、run 收场即注销(守卫在沙盒车道上按会话号取真身)。
129
78
  */
130
- hands?: RunnerDeps["hands"];
79
+ legSlots: Pick<SandboxPathEnvSlots, "bind">;
131
80
  /**
132
- * [ref]([ref],[ref]-T1)—— **部署级 token 治理窗**两键,**成对**递给本车道的每一只 Runner。
133
- * 属主 = `config.usageWindows`(声明)+ `boot/stores.ts` 造的账本店(两者同真同假,与
134
- * `boot/runner-deps.ts` 的共享基座同规);leader 不自己读 config。
135
- *
136
- * 成对的理由是 core 的消费形:`prepare-task.js` 的 `buildUsageGovernance(usageWindows, deps, …)` 要求
137
- * 声明与 `deps.usageWindowStore` **同时**在场才铸 `usageGovernance`,缺一即 undefined(既不 check 也不
138
- * commit)⇒ 半席只是死键。缺席 ⇒ 展开空对象。
139
- * 提交面那一半(`POST /v1/leader` 的 `usageWindowDenied` 门)在 `src/http/routes/leader.ts`。
140
- */
141
- usageWindows?: RunnerDeps["usageWindows"];
142
- usageWindowStore?: RunnerDeps["usageWindowStore"];
143
- /**
144
- * [ref]([ref])—— **center 发布的 prompt catalog** 席,原样递给本车道的每一只 Runner。唯一属主 =
145
- * `boot/web-admin.ts` 的 `webAdmin.promptSource`(主 runner / subRunner 同一只;run-local 有自己
146
- * 的等价腿)。缺席 ⇒ leader 的 planner/worker/repair/conflict 四类**顶层**任务跑引擎内置提示词,而同一
147
- * 部署的 `/v1/tasks`、`/v1/runs` 跑运维发布的那一份 —— 且 core 顶层新任务走的是**静默**臂(pinned /
148
- * parentCenterArtifactDigest 两支才 `prompt.snapshot_unavailable` fail-loud),运维面零信号。
149
- * 缺席 ⇒ 展开空对象(与接线前逐字相同)。
150
- */
151
- promptSource?: RunnerDeps["promptSource"];
152
- /**
153
- * [ref] 的**披露半件**([ref],codex 对抗复审 r2-[high],亲读装树 core 后修)—— 部署级错误汇。
154
- *
155
- * 🔴 为什么它与治理窗席**同生共死**:core 对「治理窗最终扣账失败」的唯一出口就是这一席 ——
156
- * `dist/core/runner/runtask.js` 收尾段 `catch (flushErr) { this.deps.onError?.(flushErr, { phase: "config", … }) }`
157
- * (以及 10s 慢盘披露的同一只回调),抛错之后任务**按原结果收尾**。挂了窗席却不挂本席 = 一次窗账
158
- * 永久丢失时这条腿上零日志零计数,后续提交继续读偏低的窗被放行 —— CLAUDE.md [ref]「治理/门轴禁静默
159
- * fail-open」正是说这个。它顺带也把 leader 车道其余 phase(degraded / rewind / memory / mcp / a2a /
160
- * prompt-cache …)的既有观测面补齐,与 `onNotice` 席当年([ref]②)同一个理由。
81
+ * 运维 `AUTONOMY`(活 config 的那一位)—— 本车道每条 leader run **出生那一拍**读一次,经 `run-entry.ts` 的 `leaderRunEntry`(入口地板 + 受理门同一只入口函数 `entryPermissionMode`)
82
+ * 得出这条 run 的入口体(`LeaderDeps.birth.entry`);本 run 铸的每一条 spec 与 worker 的续跑体都从它取词。入口词是 `plan` 的 run 进不了这条车道
83
+ * (`leaderEntryRefusal`:worker 会话没有计划审阅通道)⇒ 出生处即以拒句收场;HTTP 受理口用同一只判据先答 422。
161
84
  *
162
- * 属主唯一 = `boot/runner-deps.ts` `createRunnerDeps` 铸的那一只;`boot/leader.ts` 直引
163
- * `runnerDeps.onError`(**同一实例**,[ref]§三族A 的最强同源形,与 main.ts subRunner 的先例逐字同姿势),
164
- * leader 绝不另写一份臂表(两份必漂)。缺席 ⇒ 展开空对象(与接线前逐字相同)。
85
+ * 🔴 **函数席**(与 `deploymentPosture` / `durable` 同一条先例):`config.autonomy` 被 web-admin 原地热改,boot 期取一次值会把这条腿钉在起服那一刻。
86
+ * **必填**:没有 AUTONOMY 的装配递 `() => undefined` —— 缺席不是「没有地板」的合法拼法(权限轴上不许静默放宽)。
165
87
  */
166
- onError?: RunnerDeps["onError"];
167
- /** Worker model brain + catalog (the same the service runs tasks on). */
168
- brain: Brain;
169
- models: Record<string, Model>;
170
- roles: ModelRoles;
171
- /** The active tier table of the served catalog (boot hands the same object as `config.tiers`, mutated in place on a hot apply).
172
- * Every leader Runner gets it with `models` / `roles` (one seat, `servedTableSeat`): a role resolves in the tier-expanded view,
173
- * exactly as the main runner resolves it — absent ⇒ no tiers (the env table has none). */
174
- tiers?: NonNullable<RunnerDeps["tiers"]>;
175
- pricing: Record<string, ModelPricing>;
88
+ autonomy: () => Autonomy | undefined;
176
89
  /** Workspace root inside each E2B VM. Default /home/user. */
177
90
  workspace?: string;
178
91
  /** Git identity for the Coordinator's local clone/commit. */
@@ -198,14 +111,7 @@ export interface LeaderWireConfig {
198
111
  * complete path; a suspended worker's paused env is exempt from the leader reap by construction) and diff-out
199
112
  * is the worker's mandatory self-upload to this MinIO/S3 store (presigned per worker; creds adapter-held).
200
113
  * Unset = the original leader-owned static-env mode (pullDiff compat). */
201
- s3?: {
202
- endpoint: string;
203
- bucket: string;
204
- accessKey: string;
205
- secretKey: string;
206
- region?: string;
207
- keyPrefix?: string;
208
- };
114
+ s3?: LeaderDiffStore;
209
115
  logger?: {
210
116
  warn?: (m: string, x?: Record<string, unknown>) => void;
211
117
  info?: (m: string, x?: Record<string, unknown>) => void;
@@ -279,6 +185,10 @@ export declare function stageWorkerEnv(env: WireEnv, opts: {
279
185
  * (URL) silently killed long autonomous builds or expired the diff upload mid-run. core's turn cap is now a
280
186
  * high net (10000), so the leader gives each worker an explicit triple bound; a sub-task's own limits override.
281
187
  */
188
+ /** The leader's diff-out URL lifetime (`presignTtlSec` = `LEADER_TIMEOUT_MS` / 1000 + 600: the URL outlives the worker) over the SigV4 ceiling ⇒ the
189
+ * refusal sentence, naming the knob and its largest value; within it ⇒ `undefined`. The runner refuses to be built with a diff-out store on such a
190
+ * configuration (boot), instead of signing URLs the store would refuse at the end of a worker's run. */
191
+ export declare function leaderDiffUrlTtlRefusal(presignTtlSec: number): string | undefined;
282
192
  export declare function leaderResourceConfig(env?: NodeJS.ProcessEnv): {
283
193
  leaderTimeoutMs: number;
284
194
  workerMaxTurns: number;
@@ -3,30 +3,30 @@ import { parseNumOrFail } from "../config.js";
3
3
  import { execFileSync } from "node:child_process";
4
4
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
+ import { randomUUID } from "node:crypto";
6
7
  import { tmpdir } from "node:os";
7
- import { Runner, combinePolicies, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, delimitUntrusted, hasDestroy, } from "@sema-agent/core";
8
+ import { Runner, delimitUntrusted, hasDestroy, } from "@sema-agent/core";
8
9
  import { leaderEntryRefusal, leaderRunEntry } from "./run-entry.js";
9
10
  import { buildCheckpointCtx } from "../http/checkpoint-ctx.js";
11
+ import { submissionPermissionModeWrite } from "../permission-mode-admission.js";
10
12
  import { serviceVersion } from "../service-version.js";
11
13
  import { RemoteContainerExecutionEnv } from "../plugins/remote-env-e2b.js";
12
14
  import { buildLaneHomeClaim } from "../execution-lane-caps.js";
13
15
  const e2bBaseTemplateHomeSeat = buildLaneHomeClaim({ remoteExec: { provider: "e2b" } });
14
- import { createDurableAskPolicy } from "../approval.js";
15
16
  import { gitSubprocessEnv } from "../server-secret-env.js";
16
- import { presignS3Url } from "../plugins/s3-presign.js";
17
- import { buildUploadScript, buildUploadLoopScript, uploadLoopCommand, uploadLoopScriptPath, uploadStepSuffix, fetchUploadedDiff, withStaging, UPLOAD_LOOP_PERIOD_SEC } from "./diffup.js";
17
+ import { buildLeaderDiffUrls, buildUploadScript, buildUploadLoopScript, uploadLoopCommand, uploadLoopScriptPath, uploadStepSuffix, fetchUploadedDiff, withStaging, UPLOAD_LOOP_PERIOD_SEC } from "./diffup.js";
18
18
  import { pullDiff, safeRev } from "./diffout.js";
19
- import { runLeaderTask, withLeaderLeg } from "./leader.js";
19
+ import { LEADER_APPROVAL_SCOPE, leaderLegDurability, runLeaderTask, staticLaneWorkerPark, withLeaderLeg } from "./leader.js";
20
20
  import { attachRepairLoopDeps } from "./repair-wire.js";
21
21
  import { routePlanWithFallback, validateSubtasks } from "./planner.js";
22
- import { createEngineNoticeSeat } from "../boot/runner-deps.js";
23
22
  import { buildInternalRunCtx } from "../internal-run-env.js";
24
23
  import { currentSubject } from "../observability/principal-context.js";
25
24
  import { partsOf } from "../owner-brands.js";
26
25
  import { taskSpecIdentityOf } from "../engine-identity.js";
27
26
  import { redactHead } from "../observability/run-terminal.js";
27
+ import { seatText } from "../trace/exit-seat.js";
28
+ import { SIGV4_PRESIGN_MAX_SEC } from "../object-store-buckets.js";
28
29
  const REPAIR_LOOP_WIRED = true;
29
- export const LEADER_APPROVAL_SCOPE = "_leader";
30
30
  export function leaderResourceScope(workerId) {
31
31
  return `_leader-resource-${workerId}`;
32
32
  }
@@ -104,6 +104,12 @@ function parseNumOrFailNonNegative(name, raw) {
104
104
  throw new Error(`env ${name}=${n} must not be negative`);
105
105
  return n;
106
106
  }
107
+ export function leaderDiffUrlTtlRefusal(presignTtlSec) {
108
+ if (presignTtlSec <= SIGV4_PRESIGN_MAX_SEC)
109
+ return undefined;
110
+ const maxTimeoutMs = (SIGV4_PRESIGN_MAX_SEC - 600) * 1000;
111
+ return `LEADER_TIMEOUT_MS makes the leader's diff-out links live ${presignTtlSec} s (the worker timeout + 600 s), longer than a presigned URL can (at most ${SIGV4_PRESIGN_MAX_SEC} s (7 days)) — set LEADER_TIMEOUT_MS to at most ${maxTimeoutMs}`;
112
+ }
107
113
  export function leaderResourceConfig(env = process.env) {
108
114
  const leaderTimeoutMs = Math.max(600_000, Math.floor(parseNumOrFailNonNegative("LEADER_TIMEOUT_MS", env.LEADER_TIMEOUT_MS) || 0) || 86_400_000);
109
115
  const workerMaxTurns = Math.floor(parseNumOrFailNonNegative("LEADER_WORKER_MAX_TURNS", env.LEADER_WORKER_MAX_TURNS) || 0) || 10_000;
@@ -199,22 +205,28 @@ export function createLeaderRunner(cfg) {
199
205
  };
200
206
  const ident = cfg.git ?? { name: "leader", email: "leader@local" };
201
207
  const { leaderTimeoutMs, workerLimits, presignTtlSec, resourceSuspend: resourceCfg } = leaderResourceConfig();
208
+ const ttlRefusal = cfg.s3 !== undefined ? leaderDiffUrlTtlRefusal(presignTtlSec) : undefined;
209
+ if (ttlRefusal !== undefined)
210
+ throw new Error(ttlRefusal);
202
211
  const { repairRounds, repairBudgetUsd, repairLoopOn, measureGatesOn, repairLoopAttempts, oracleFlakyK, replanBudgetUsd, } = leaderLoopConfig({ repairRounds: cfg.repairRounds });
203
- const onNoticeSeat = createEngineNoticeSeat(cfg.logger);
204
- const governanceSeat = cfg.governance ?? {};
205
- const deploymentPostureSeat = () => cfg.deploymentPosture?.() ?? {};
206
- const tracerSeat = cfg.tracer ? { tracer: cfg.tracer } : {};
207
- const modsSeat = cfg.mods ? { mods: cfg.mods } : {};
208
- const handsSeat = cfg.hands ? { hands: cfg.hands } : {};
209
- const servedTableSeat = { models: cfg.models, roles: cfg.roles, ...(cfg.tiers ? { tiers: cfg.tiers } : {}) };
210
- const usageWindowSeat = cfg.usageWindows && cfg.usageWindowStore ? { usageWindows: cfg.usageWindows, usageWindowStore: cfg.usageWindowStore } : {};
211
- const promptSourceSeat = cfg.promptSource ? { promptSource: cfg.promptSource } : {};
212
- const onErrorSeat = cfg.onError ? { onError: cfg.onError } : {};
213
- const principalSpec = () => {
214
- const subject = currentSubject();
215
- return taskSpecIdentityOf(subject === undefined ? undefined : partsOf(subject));
212
+ const mintLegSessionId = (stem) => `leader-${stem}-${Date.now()}-${randomUUID()}`;
213
+ const bindLegSlot = (key, env) => {
214
+ const release = cfg.legSlots.bind(key, env);
215
+ if (release === undefined)
216
+ cfg.logger?.warn?.("sandbox_path_env_slot_at_capacity", { sessionId: key, leg: "leader" });
217
+ return release ?? (() => { });
218
+ };
219
+ const withLegSlot = (runner, env, key) => {
220
+ const orig = runner.runTaskStream.bind(runner);
221
+ runner.runTaskStream = (...args) => {
222
+ const release = bindLegSlot(key, env);
223
+ const stream = orig(...args);
224
+ void stream.result().then(release, release);
225
+ return stream;
226
+ };
227
+ return runner;
216
228
  };
217
- const mkRepair = (rawEnv, repoDir, oracleFiles, entry) => {
229
+ const mkRepair = (rawEnv, repoDir, oracleFiles, birth) => {
218
230
  if (repairRounds <= 0)
219
231
  return undefined;
220
232
  return {
@@ -227,7 +239,6 @@ export function createLeaderRunner(cfg) {
227
239
  throw new Error(`oracle still present after remove (${f.path}) — refusing repair (measurement integrity)`);
228
240
  }
229
241
  try {
230
- const runner = new Runner({ brain: cfg.brain, ...servedTableSeat, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, ...handsSeat, executionEnv: rawEnv, rootPath: repoDir });
231
242
  const objective = [
232
243
  `The integrated project at ${repoDir} fails its build/test. Make the MINIMAL change to the working tree so this command exits 0 (cd into the repo and run it yourself to confirm):`,
233
244
  ` ${testCmd}`,
@@ -236,17 +247,20 @@ export function createLeaderRunner(cfg) {
236
247
  ``,
237
248
  delimitUntrusted("FAILING OUTPUT (untrusted)", errorText),
238
249
  ].join("\n");
250
+ const repairSessionId = mintLegSessionId(`repair-${round}`);
251
+ const runner = withLegSlot(new Runner({ ...cfg.plane(), executionEnv: rawEnv, rootPath: repoDir }), rawEnv, repairSessionId);
239
252
  const res = await runner
240
253
  .runTaskStream(withLeaderLeg({
241
254
  objective,
242
- ...principalSpec(),
243
- sessionId: `leader-repair-${Date.now()}-${round}`,
255
+ sessionId: repairSessionId,
244
256
  signal,
245
257
  limits: { maxCostUsd: repairBudgetUsd, maxWalltimeMs: Math.max(300_000, Math.floor(leaderTimeoutMs / 2)) },
246
- }, entry))
258
+ }, birth, { slotKey: repairSessionId, durability: leaderLegDurability({ continuation: "in_run" }), executor: { kind: "fresh" } }))
247
259
  .result();
248
- if (res.terminal.kind !== "completed")
249
- throw new Error(`repair round ${round} did not complete (status=${outwardPlane(res.terminal).status})`);
260
+ if (res.terminal.kind !== "completed") {
261
+ const plane = outwardPlane(res.terminal);
262
+ throw new Error(`repair round ${round} did not complete (status=${plane.status}${plane.errorMessage !== undefined ? `: ${plane.errorMessage}` : ""})`);
263
+ }
250
264
  return { costUsd: res.stats?.costMicroUsd !== undefined ? res.stats.costMicroUsd / 1e6 : undefined, note: res.result?.slice(0, 200) };
251
265
  }
252
266
  finally {
@@ -274,6 +288,8 @@ export function createLeaderRunner(cfg) {
274
288
  const refused = leaderEntryRefusal(entry);
275
289
  if (refused !== undefined)
276
290
  return { ok: false, reports: [], cancelled: false, error: refused };
291
+ const subject = currentSubject();
292
+ const birth = { entry, identity: taskSpecIdentityOf(subject === undefined ? undefined : partsOf(subject)), govern: cfg.govern, baseline: cfg.baseline, auth: cfg.auth };
277
293
  const injectOracles = async (env) => {
278
294
  const ofs = body.oracleFiles ?? [];
279
295
  if (ofs.length === 0)
@@ -295,15 +311,21 @@ export function createLeaderRunner(cfg) {
295
311
  if (Array.isArray(body.subtasks) && body.subtasks.length > 0) {
296
312
  return validateSubtasks(body.subtasks);
297
313
  }
298
- const planRunner = new Runner({ brain: cfg.brain, ...servedTableSeat, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, ...handsSeat });
314
+ const planRunner = new Runner({ ...cfg.plane() });
315
+ const routeAuth = birth.auth(undefined);
299
316
  const runRoute = async (prompt) => {
300
317
  const res = await planRunner.runTaskStream({
301
318
  objective: prompt,
302
- ...principalSpec(),
303
- sessionId: `leader-route-${Date.now()}`,
319
+ ...birth.identity,
320
+ getApiKeyAndHeaders: routeAuth,
321
+ sessionId: mintLegSessionId("route"),
304
322
  signal,
305
323
  ...(cfg.routerModel ? { model: cfg.routerModel } : { modelRole: "classifier" }),
306
324
  }).result();
325
+ if (res.terminal.kind !== "completed") {
326
+ const plane = outwardPlane(res.terminal);
327
+ cfg.logger?.warn?.("plan_route_run_incomplete", { status: plane.status, ...(plane.errorMessage !== undefined ? { err: seatText("operator-log-error", plane.errorMessage) } : {}) });
328
+ }
307
329
  return res.result ?? "";
308
330
  };
309
331
  const { subs, collapsed, rawSample } = await routePlanWithFallback(body.objective, runRoute, {
@@ -315,23 +337,18 @@ export function createLeaderRunner(cfg) {
315
337
  return subs;
316
338
  };
317
339
  const provisionWorker = async (sub) => {
318
- const sessionId = `leader-${sub.workerId}-${Date.now()}`;
319
- const durable = cfg.durable?.();
340
+ const sessionId = mintLegSessionId(sub.workerId);
341
+ const factory = cfg.s3 !== undefined && cfg.envFactory !== undefined ? { s3: cfg.s3, envFactory: cfg.envFactory } : undefined;
342
+ const park = factory !== undefined ? cfg.durable?.() : staticLaneWorkerPark(cfg.durable?.(), cfg.approvalListInForce());
320
343
  const resumeBody = { ...entry, objective: sub.spec.objective };
321
- const storedResumeCtx = durable !== undefined ? buildCheckpointCtx({ body: resumeBody, unattendedLeg: true, writerVersion: serviceVersion() }, durable.ctxSealer, sessionId) : undefined;
322
- let durableSpec = {};
323
- if (durable && storedResumeCtx !== undefined) {
324
- const d = durable;
325
- durableSpec = {
326
- toolPolicy: combinePolicies(createDurableQuestionPolicy(), createDurableAskPolicy({ requireApproval: d.requireApproval, ...(d.deny ? { deny: d.deny } : {}), ...(d.autoBudget ? { autoBudget: d.autoBudget } : {}), ...(d.neverAuto ? { neverAuto: d.neverAuto } : {}) })),
327
- checkpointStore: d.checkpointStore,
328
- durableApproval: { scope: LEADER_APPROVAL_SCOPE, ...(d.ttlMs ? { ttlMs: d.ttlMs } : {}) },
329
- onQuestion: QUESTION_AWAITS_RESUME,
330
- };
331
- await d.checkpointStore.putCtx(sessionId, storedResumeCtx).catch((e) => {
344
+ const storedResumeCtx = park !== undefined ? buildCheckpointCtx({ body: resumeBody, unattendedLeg: true, writerVersion: serviceVersion(), permissionModeWrite: submissionPermissionModeWrite(resumeBody) }, park.ctxSealer, sessionId) : undefined;
345
+ const durability = leaderLegDurability(park === undefined || storedResumeCtx === undefined
346
+ ? { continuation: "replay", resumeCtx: "unwritten" }
347
+ : await park.checkpointStore.putCtx(sessionId, storedResumeCtx).then(() => ({ continuation: "replay", resumeCtx: "landed", park }), (e) => {
332
348
  cfg.logger?.warn?.("leader_putctx_failed", { workerId: sub.workerId, err: redactHead(e instanceof Error ? e.message : String(e), 2000) });
333
- });
334
- }
349
+ return { continuation: "replay", resumeCtx: "lost" };
350
+ }));
351
+ const durable = durability.kind === "durable" ? durability.park : undefined;
335
352
  let resourceSpec = {};
336
353
  const repairLoopActive = repairLoopOn && REPAIR_LOOP_WIRED;
337
354
  if (repairLoopActive && resourceCfg) {
@@ -348,8 +365,8 @@ export function createLeaderRunner(cfg) {
348
365
  else if (resourceCfg && !repairLoopActive) {
349
366
  cfg.logger?.warn?.("leader_resource_suspend_inactive", {
350
367
  workerId: sub.workerId,
351
- reason: !durable
352
- ? "no durable checkpoint store"
368
+ reason: durability.kind === "transient"
369
+ ? durability.why
353
370
  : !cfg.toolResultStore
354
371
  ? "no durable tool-result store"
355
372
  : "no durable session store",
@@ -360,8 +377,8 @@ export function createLeaderRunner(cfg) {
360
377
  if (!d || storedResumeCtx === undefined)
361
378
  return runner;
362
379
  const orig = runner.runTaskStream.bind(runner);
363
- runner.runTaskStream = (spec, resume) => {
364
- const stream = orig(spec, resume);
380
+ runner.runTaskStream = (...args) => {
381
+ const stream = orig(...args);
365
382
  const iv = setInterval(() => {
366
383
  void d.checkpointStore.putCtx(sessionId, storedResumeCtx).catch(() => { });
367
384
  }, 45_000);
@@ -371,16 +388,11 @@ export function createLeaderRunner(cfg) {
371
388
  };
372
389
  return runner;
373
390
  };
374
- if (cfg.s3 && cfg.envFactory) {
375
- const s3 = cfg.s3;
376
- const envFactory = cfg.envFactory;
391
+ if (factory !== undefined) {
392
+ const { s3, envFactory } = factory;
377
393
  const baseSha = body.baseSha;
378
394
  const seedCmd = body.seedCmd;
379
- const key = `${s3.keyPrefix ?? "leader-diff"}/${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}/${sub.workerId}.patch`;
380
- const ttl = presignTtlSec;
381
- const base = { endpoint: s3.endpoint, bucket: s3.bucket, accessKey: s3.accessKey, secretKey: s3.secretKey, ...(s3.region ? { region: s3.region } : {}), key, expiresSec: ttl };
382
- const putUrl = presignS3Url({ ...base, method: "PUT" });
383
- const getUrl = presignS3Url({ ...base, method: "GET" });
395
+ const { putUrl, getUrl } = buildLeaderDiffUrls(s3, sub.workerId, presignTtlSec);
384
396
  const script = buildUploadScript({ repoDir: repo, baseSha, putUrl, workDir: W });
385
397
  const stage = async (env) => {
386
398
  await stageWorkerEnv(env, { seedCmd, repo, branch: sub.branch, uploadScriptPath: `${W}/.leader-upload.sh`, uploadScript: script });
@@ -389,21 +401,22 @@ export function createLeaderRunner(cfg) {
389
401
  return {
390
402
  workerId: sub.workerId, sessionId, branch: sub.branch,
391
403
  baseSha,
392
- runner: keepCtxWarm(new Runner({
393
- brain: cfg.brain, ...servedTableSeat, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, ...handsSeat,
404
+ runner: keepCtxWarm(new Runner({ ...cfg.plane(),
394
405
  ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}),
395
406
  ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}),
396
407
  executionEnvFactory: async (ctx) => {
397
408
  const env = await envFactory(ctx);
398
409
  assertLeaderOwnedEnvReapable(env, "leader worker sandbox");
410
+ const releaseSlot = bindLegSlot(sessionId, env);
399
411
  return withStaging(env, stage, async (e) => {
412
+ releaseSlot();
400
413
  await stopUploadLoop(e, sub.workerId);
401
414
  await sh(e)(`cd ${repo} && git add -A && git -c user.name='leader-belt' -c user.email='belt@leader' commit -qm 'belt: auto-commit uncommitted worker output' || true; sh ${W}/.leader-upload.sh`);
402
415
  });
403
416
  },
404
417
  })),
405
418
  fetchDiff: () => fetchUploadedDiff(getUrl),
406
- spec: withLeaderLeg({ ...workerLimits, ...sub.spec, ...durableSpec, ...resourceSpec, ...principalSpec(), objective: `${sub.spec.objective}${uploadStepSuffix(W)}` }, resumeBody),
419
+ spec: withLeaderLeg({ ...workerLimits, ...sub.spec, ...resourceSpec, objective: `${sub.spec.objective}${uploadStepSuffix(W)}` }, birth, { slotKey: sessionId, durability, executor: { kind: "fresh" } }),
407
420
  };
408
421
  }
409
422
  if (!cfg.e2bApiKey)
@@ -415,15 +428,15 @@ export function createLeaderRunner(cfg) {
415
428
  const baseSha = await sh(env)(`cd ${repo} && git rev-parse HEAD`);
416
429
  return {
417
430
  workerId: sub.workerId, sessionId, branch: sub.branch, baseSha,
418
- runner: keepCtxWarm(new Runner({ brain: cfg.brain, ...servedTableSeat, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, ...handsSeat, ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}), ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}), executionEnv: env })),
431
+ runner: keepCtxWarm(withLegSlot(new Runner({ ...cfg.plane(), ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}), ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}), executionEnv: env }), env, sessionId)),
419
432
  diffEnv: env,
420
433
  destroy: () => env.destroy().then(() => { }),
421
- spec: withLeaderLeg({ ...workerLimits, ...sub.spec, ...durableSpec, ...resourceSpec, ...principalSpec() }, resumeBody),
434
+ spec: withLeaderLeg({ ...workerLimits, ...sub.spec, ...resourceSpec }, birth, { slotKey: sessionId, durability, executor: { kind: "fresh" } }),
422
435
  };
423
436
  };
424
437
  const provisionIntegrationSandbox = async () => {
425
438
  if (cfg.envFactory) {
426
- const integSessionId = `leader-integ-${Date.now()}`;
439
+ const integSessionId = mintLegSessionId("integ");
427
440
  const env = await cfg.envFactory(buildInternalRunCtx(integSessionId));
428
441
  const reap = createEnvReaper(env, "leader integration sandbox");
429
442
  try {
@@ -436,7 +449,7 @@ export function createLeaderRunner(cfg) {
436
449
  }
437
450
  const rawEnv = env;
438
451
  const oracleFiles = body.oracleFiles ?? [];
439
- const repair = mkRepair(rawEnv, repo, oracleFiles, entry);
452
+ const repair = mkRepair(rawEnv, repo, oracleFiles, birth);
440
453
  return { env: asIntegrationEnv(env), destroy: reap, ...(repair ? { repair } : {}) };
441
454
  }
442
455
  if (!cfg.e2bApiKey)
@@ -454,11 +467,12 @@ export function createLeaderRunner(cfg) {
454
467
  await env.destroy().catch(() => { });
455
468
  throw e;
456
469
  }
457
- return { env: asIntegrationEnv(env), destroy: () => env.destroy().then(() => { }), ...(mkRepair(env, repo, body.oracleFiles ?? [], entry) ? { repair: mkRepair(env, repo, body.oracleFiles ?? [], entry) } : {}) };
470
+ const repair = mkRepair(env, repo, body.oracleFiles ?? [], birth);
471
+ return { env: asIntegrationEnv(env), destroy: () => env.destroy().then(() => { }), ...(repair ? { repair } : {}) };
458
472
  };
459
473
  const push = createCoordinatorPush(body.durableRemote, body.targetRef, ident);
460
474
  const deps = {
461
- entry,
475
+ birth,
462
476
  signal,
463
477
  plan,
464
478
  provisionWorker,
@@ -476,7 +490,7 @@ export function createLeaderRunner(cfg) {
476
490
  const resolveRepair = async (solo, _sub) => {
477
491
  let env;
478
492
  if (cfg.envFactory) {
479
- const graderSessionId = `leader-grader-${Date.now()}`;
493
+ const graderSessionId = mintLegSessionId("grader");
480
494
  env = await cfg.envFactory(buildInternalRunCtx(graderSessionId));
481
495
  }
482
496
  else {
@@ -174,20 +174,31 @@ export declare function memoryEngineRootFor(config: Pick<ServiceConfig, "memoryE
174
174
  * 所以它的根必须走这条链而不是 `memoryEngineRootFor` 的分派(codex 修复验证轮 [medium]:分派会把 run-local 的
175
175
  * 记忆搬到 `<localDataRoot>/memory-work`,存量记忆与控制面升级后隐形)。 */
176
176
  export declare function fileMemoryEngineRoot(config: Pick<ServiceConfig, "memoryEngineDir">, fallbackRoot?: string): string;
177
+ /**
178
+ * 一段路径**在不在盘**的唯一读法(三态,{@link canonicalPathOf} 逐段走用它;host 车道托管判在「读不出就不删」那一臂登记根时也读它):
179
+ * · `stat` 得动(跟随符号链接)⇒ `true`(在);
180
+ * · `ENOENT` / `ENOTDIR`(这一处不存在、悬空链接的目标不存在、中间一段不是目录)⇒ `false`(不在);
181
+ * · 其余错误(`EACCES` 没有搜索权限的段、`ELOOP` 链接成环、I/O 错误……)⇒ **原样抛**(读不出)。读不出不是「不在」:把它读成不在,
182
+ * 下游的路径包含判就会把一处它根本看不见的目录判成「不在根里」—— 那正是删除面上的 fail-open。
183
+ */
184
+ export declare function presentOnDisk(p: string): boolean;
177
185
  /**
178
186
  * 一条路径的**规范形**(给 {@link judgeMemoryRootOutsideDataRoot} 的 `canonical` 座用的生产实现;host 车道的工作区托管判也读它):
179
- * · 既存的绝对路径 ⇒ **原样**交给 `realpath`(core `physicalPathOf` = realpath(3):大小写不敏感的文件系统上返回盘上的拼写;符号链接、macOS 的 `/var` → `/private/var` 在这一步展平)。
187
+ * · 在盘的绝对路径 ⇒ **原样**交给 `realpath`(core `physicalPathOf` = realpath(3):大小写不敏感的文件系统上返回盘上的拼写;符号链接、macOS 的 `/var` → `/private/var` 在这一步展平)。
180
188
  * 不先在字串上折 `..`:操作系统在展开符号链接**之后**才折 `..`,`/link/..` 的真身是链接目标的父目录,字串折叠会把它读成 `/`;
181
- * · 其余(尚不存在 / 相对)⇒ 绝对化(**不折叠**)后逐段走:既存的前缀每进一段取一次 `realpath`(符号链接在这一步展开),`..` 折在**已展开**的前缀上 ——
182
- * 与操作系统同序(`/别名/../新目录` 的真身在链接目标的父目录下;先在字串上折会读成别名的父目录,判到另一只挂载上);第一段不存在起,余下是尚不存在的尾巴
189
+ * · 其余(尚不在盘 / 相对)⇒ 绝对化(**不折叠**)后逐段走:在盘的前缀每进一段取一次 `realpath`(符号链接在这一步展开),`..` 折在**已展开**的前缀上 ——
190
+ * 与操作系统同序(`/别名/../新目录` 的真身在链接目标的父目录下;先在字串上折会读成别名的父目录,判到另一只挂载上);第一段不在盘起,余下是尚不在盘的尾巴
183
191
  * (尾巴只做字串规范化,与 `mkdir -p` 建出来的同形)。
184
192
  *
185
- * 为什么按「最深既存祖先」而不是直接 `realpath(p)`:boot 期记忆根常常还没建出来(新数据根),`realpath` 对不存在的路径抛 ENOENT,
186
- * 而数据根那一侧已经存在、会被展平 —— 两侧拼写空间不一样,同一个目录会被读成「不在其下」。祖先用 `existsSync` 找(它对 ENOENT / ENOTDIR / EACCES / ELOOP
187
- * 一律答 `false`,不抛),所以走到的那一级一定 stat 得动;`realpath` 仍可能在「探完到取值之间目录被挪走 / 搜索权限被收」的竞态里抛 ——
188
- * 不在本函数里吞(吞了就是一条无声的降级臂),由 boot 调用点接住并响亮报一行(`memory_engine_dir_check_unavailable`),起服照常。
193
+ * 为什么按「最深在盘祖先」而不是直接 `realpath(p)`:boot 期记忆根常常还没建出来(新数据根),`realpath` 对不存在的路径抛 ENOENT,
194
+ * 而数据根那一侧已经存在、会被展平 —— 两侧拼写空间不一样,同一个目录会被读成「不在其下」。
195
+ *
196
+ * **读不出就抛**:每一段在不在盘读 {@link presentOnDisk}(三态)—— 不在(`ENOENT` / `ENOTDIR`)才接成尾巴;没有搜索权限的段、成环的链接、`realpath` 在
197
+ * 「探完到取值之间目录被挪走 / 搜索权限被收」的竞态里失败,一律**原样抛**,不在本函数里吞(吞了就是一条无声的降级臂:把看不见的一段当成「还不存在」,包含判就答
198
+ * 「不在里面」)。调用方各自接住、按自己的面响亮说「查不了」:起服记忆根判(`memory_engine_dir_check_unavailable`)· 配置根判(`sema_config_root_workspace_check_unavailable`)·
199
+ * 店的持久性取证(`path_unresolvable`)· host 车道托管判(铸时 ⇒ 这条腿不铸;删前 ⇒ 不删、留给退出兜底)。
189
200
  *
190
- * 取舍:尾巴(不存在的那几段)只比规范化字串,**不折大小写**,尾巴里的 `..` 按字串折:两个只差大小写、且都还不存在的拼写在大小写不敏感的盘上会被读成两处 ——
201
+ * 取舍:尾巴(不在盘的那几段)只比规范化字串,**不折大小写**,尾巴里的 `..` 按字串折:两个只差大小写、且都还不在盘的拼写在大小写不敏感的盘上会被读成两处 ——
191
202
  * 方向是多报一条 warn(这条判据的失败方向),不会漏报。
192
203
  */
193
204
  export declare function canonicalPathOf(p: string): string;
@@ -195,7 +206,7 @@ export declare function canonicalPathOf(p: string): string;
195
206
  * **路径包含**(含相等)的判官:`child` 是不是 `root` 本身或它的后代 —— `path.relative(canonical(root), canonical(child))` 不是 `..`、不以 `..` 加分隔符起头、也不是绝对路径(跨盘符)。
196
207
  * `..` 要按**整段**判:名为 `..data` 的子目录在其下;`startsWith("..")` 会把它读成在其外。`canonical` 座缺省 `path.resolve`(字串规范形),boot 递 {@link canonicalPathOf} 取符号链接展平后的形;
197
208
  * 两侧同一只座(先 root、后 child)。读者三处:记忆根「在数据根之外」那一判({@link judgeMemoryRootOutsideDataRoot})、起服时「配置根是不是工作区的祖先」那一判(`sema-config-dir.ts`)
198
- * 与 host 车道工厂「腿的目录落在哪只本服务铸的临时根里」那一判(`plugins/remote-env-host.ts`,座 = {@link canonicalPathOf});
209
+ * 与 host 车道工厂托管的两判(`plugins/remote-env-host.ts`,座 = {@link canonicalPathOf}:铸时「腿的目录落在哪只本服务铸的临时根里」· 删前「此刻谁的工作目录在这只根里」);
199
210
  * 树里别处内联的同形表达式(`boot/deployment-mods.ts` · `plugins/local-data-dir-modes-m39.ts` · `plugins/remote-env-host.ts` 的残余镜像写判 · `plugins/worktree-isolation.ts`)不在本次收口范围。
200
211
  */
201
212
  export declare function pathIsWithin(root: string, child: string, canonical?: (p: string) => string): boolean;