@sema-agent/server 7.145.0 → 7.146.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 (275) hide show
  1. package/MIGRATION.md +492 -0
  2. package/USAGE.md +218 -47
  3. package/deploy/facts/CHANGES.json +25 -0
  4. package/deploy/facts/README.md +57 -0
  5. package/deploy/facts/lanes.json +54 -0
  6. package/deploy/facts/lanes.schema.json +57 -0
  7. package/deploy/facts/sandbox-rbac.json +113 -0
  8. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  9. package/deploy/facts/sealed-key.json +9 -0
  10. package/deploy/facts/sealed-key.schema.json +52 -0
  11. package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
  12. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  13. package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
  14. package/deploy/sema-up/chart/templates/server.yaml +4 -0
  15. package/deploy/sema-up/chart/values.yaml +7 -0
  16. package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
  17. package/dist/adoption/import-limits.d.ts +6 -0
  18. package/dist/adoption/import-limits.js +7 -0
  19. package/dist/adoption/import.d.ts +62 -0
  20. package/dist/adoption/import.js +107 -0
  21. package/dist/adoption/wire.d.ts +52 -0
  22. package/dist/adoption/wire.js +9 -0
  23. package/dist/approval-ask-machine.d.ts +2 -2
  24. package/dist/approval-card.d.ts +3 -12
  25. package/dist/approval-card.js +0 -4
  26. package/dist/approval-gate-currency.d.ts +1 -1
  27. package/dist/approval-gate-currency.js +1 -1
  28. package/dist/approval-reconciler.d.ts +1 -1
  29. package/dist/boot/deployment-mods.d.ts +11 -1
  30. package/dist/boot/deployment-mods.js +15 -2
  31. package/dist/boot/device-lane.js +2 -0
  32. package/dist/boot/engine-lease.d.ts +193 -21
  33. package/dist/boot/engine-lease.js +146 -20
  34. package/dist/boot/execution-env.d.ts +4 -0
  35. package/dist/boot/execution-env.js +12 -0
  36. package/dist/boot/exit-boundary.d.ts +50 -0
  37. package/dist/boot/exit-boundary.js +32 -0
  38. package/dist/boot/exit-last.d.ts +20 -4
  39. package/dist/boot/exit-last.js +21 -4
  40. package/dist/boot/governance-seams.js +1 -0
  41. package/dist/boot/memory-consolidation.d.ts +29 -17
  42. package/dist/boot/memory-consolidation.js +26 -15
  43. package/dist/boot/reapers.d.ts +12 -4
  44. package/dist/boot/reapers.js +39 -15
  45. package/dist/boot/resolve-spec.d.ts +2 -0
  46. package/dist/boot/resolve-spec.js +38 -15
  47. package/dist/boot/runner-deps.js +1 -1
  48. package/dist/boot/session-faces.js +3 -1
  49. package/dist/boot/shutdown.js +27 -9
  50. package/dist/boot/stage-01-config.js +1 -1
  51. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  52. package/dist/boot/stage-03-store-backend.js +2 -2
  53. package/dist/boot/stage-04-stores.d.ts +1 -0
  54. package/dist/boot/stage-05-execution-env.d.ts +3 -0
  55. package/dist/boot/stage-05-execution-env.js +3 -3
  56. package/dist/boot/stage-06-runners.d.ts +3 -0
  57. package/dist/boot/stage-06-runners.js +11 -10
  58. package/dist/boot/stage-07-capability-layer.d.ts +3 -0
  59. package/dist/boot/stage-07-capability-layer.js +7 -2
  60. package/dist/boot/stage-08-reapers.d.ts +3 -0
  61. package/dist/boot/stage-08-reapers.js +3 -3
  62. package/dist/boot/stage-09-leader.d.ts +3 -0
  63. package/dist/boot/stage-10-http-server.d.ts +3 -0
  64. package/dist/boot/stage-10-http-server.js +3 -2
  65. package/dist/boot/stores.d.ts +1 -0
  66. package/dist/boot/stores.js +35 -19
  67. package/dist/boot/workflow-orchestration.d.ts +1 -1
  68. package/dist/boot/workflow-orchestration.js +2 -1
  69. package/dist/capabilities/composite-members.d.ts +3 -1
  70. package/dist/capabilities/composite-members.js +3 -1
  71. package/dist/capabilities/scenarios.d.ts +2 -2
  72. package/dist/capability-facts.d.ts +18 -1
  73. package/dist/capability-facts.js +25 -7
  74. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  75. package/dist/config-catalog.js +13 -4
  76. package/dist/config-provider.d.ts +2 -2
  77. package/dist/config-types.d.ts +43 -4
  78. package/dist/config.d.ts +6 -1
  79. package/dist/config.js +67 -11
  80. package/dist/deleted-session-ids.d.ts +19 -0
  81. package/dist/deleted-session-ids.js +26 -0
  82. package/dist/deploy-facts.d.ts +155 -0
  83. package/dist/deploy-facts.js +94 -0
  84. package/dist/device-ws-hub.d.ts +48 -3
  85. package/dist/device-ws-hub.js +48 -2
  86. package/dist/error-code-family.d.ts +35 -0
  87. package/dist/error-code-family.js +26 -0
  88. package/dist/fleet/drain-entry.d.ts +39 -3
  89. package/dist/fleet/drain-entry.js +6 -0
  90. package/dist/fleet/fleet-bus.d.ts +2 -1
  91. package/dist/hooks/branch-transcript.d.ts +1 -1
  92. package/dist/hooks/branch-transcript.js +1 -0
  93. package/dist/hooks/classic-stations.d.ts +25 -8
  94. package/dist/hooks/classic-stations.js +29 -3
  95. package/dist/hooks/hook-runner.d.ts +33 -16
  96. package/dist/hooks/hook-runner.js +166 -73
  97. package/dist/hooks/hook-source.d.ts +14 -2
  98. package/dist/hooks/hook-source.js +8 -0
  99. package/dist/http/active-run-conflict.d.ts +1 -1
  100. package/dist/http/active-run-conflict.js +1 -1
  101. package/dist/http/admission.js +11 -1
  102. package/dist/http/checkpoint-ctx.d.ts +59 -71
  103. package/dist/http/checkpoint-ctx.js +34 -40
  104. package/dist/http/dispatch.js +18 -2
  105. package/dist/http/durable-run-leg.d.ts +5 -0
  106. package/dist/http/durable-run-leg.js +1 -0
  107. package/dist/http/execution-context.d.ts +4 -0
  108. package/dist/http/execution-context.js +9 -0
  109. package/dist/http/health-self-report.d.ts +6 -2
  110. package/dist/http/health-self-report.js +3 -2
  111. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  112. package/dist/http/memory-compliance-failure-reply.js +2 -15
  113. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  114. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  115. package/dist/http/principal-gate.d.ts +35 -12
  116. package/dist/http/principal-gate.js +16 -12
  117. package/dist/http/request-guards.d.ts +1 -1
  118. package/dist/http/request-guards.js +5 -5
  119. package/dist/http/resume-legs.d.ts +22 -0
  120. package/dist/http/resume-legs.js +26 -17
  121. package/dist/http/route-ctx.d.ts +11 -10
  122. package/dist/http/route-table.js +2 -0
  123. package/dist/http/routes/adoption.d.ts +11 -0
  124. package/dist/http/routes/adoption.js +80 -6
  125. package/dist/http/routes/agents-roster.js +1 -0
  126. package/dist/http/routes/approvals-assistant.js +2 -1
  127. package/dist/http/routes/background.js +1 -0
  128. package/dist/http/routes/capabilities.js +4 -1
  129. package/dist/http/routes/diagnostics.d.ts +9 -14
  130. package/dist/http/routes/diagnostics.js +10 -20
  131. package/dist/http/routes/engine.d.ts +31 -0
  132. package/dist/http/routes/engine.js +81 -0
  133. package/dist/http/routes/memory-bundle.js +3 -0
  134. package/dist/http/routes/memory-consolidation.js +3 -5
  135. package/dist/http/routes/memory-origin.d.ts +1 -1
  136. package/dist/http/routes/memory-origin.js +2 -4
  137. package/dist/http/routes/memory-policy.js +5 -1
  138. package/dist/http/routes/observability.js +3 -6
  139. package/dist/http/routes/runs.js +7 -3
  140. package/dist/http/routes/session-sync.js +12 -25
  141. package/dist/http/routes/sessions-list.d.ts +1 -1
  142. package/dist/http/routes/sessions.js +4 -0
  143. package/dist/http/routes/tasks.js +23 -5
  144. package/dist/http/routes/trace-usage.js +1 -1
  145. package/dist/http/run-concurrency-cap.d.ts +86 -0
  146. package/dist/http/run-concurrency-cap.js +54 -0
  147. package/dist/http/server.d.ts +15 -12
  148. package/dist/http/server.js +18 -3
  149. package/dist/http/stored-body-replay.d.ts +9 -6
  150. package/dist/http/stored-body-replay.js +19 -5
  151. package/dist/http/wire-types.d.ts +8 -3
  152. package/dist/json-body-limit.d.ts +8 -0
  153. package/dist/json-body-limit.js +2 -0
  154. package/dist/leader/diffout.d.ts +1 -1
  155. package/dist/local-data-root.d.ts +2 -2
  156. package/dist/memory-operator-faces.d.ts +9 -4
  157. package/dist/memory-scope-stores.d.ts +9 -7
  158. package/dist/memory-scope-stores.js +3 -14
  159. package/dist/memory-scope.d.ts +36 -0
  160. package/dist/memory-scope.js +32 -1
  161. package/dist/net-address.d.ts +53 -0
  162. package/dist/net-address.js +111 -0
  163. package/dist/observability/fail-open.d.ts +14 -10
  164. package/dist/observability/fail-open.js +14 -10
  165. package/dist/orchestration/workflow-notify-journal.js +2 -3
  166. package/dist/org-delivery-gate.js +1 -1
  167. package/dist/peer-directory.d.ts +6 -12
  168. package/dist/peer-directory.js +5 -1
  169. package/dist/plugins/caching-session-store.d.ts +3 -1
  170. package/dist/plugins/caching-session-store.js +3 -0
  171. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  172. package/dist/plugins/checkpoint-store-face.js +152 -0
  173. package/dist/plugins/file-run-store.d.ts +0 -6
  174. package/dist/plugins/file-run-store.js +1 -12
  175. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  176. package/dist/plugins/fork-routing-session-store.js +2 -2
  177. package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
  178. package/dist/plugins/local-checkpoint-store.d.ts +2 -2
  179. package/dist/plugins/local-checkpoint-store.js +2 -1
  180. package/dist/plugins/local-session-store.d.ts +28 -5
  181. package/dist/plugins/local-session-store.js +32 -8
  182. package/dist/plugins/memory-engine-pg.d.ts +29 -6
  183. package/dist/plugins/memory-engine-pg.js +96 -214
  184. package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
  185. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  186. package/dist/plugins/memory-engine-tidb.d.ts +35 -9
  187. package/dist/plugins/memory-engine-tidb.js +97 -227
  188. package/dist/plugins/memory-key-guards.d.ts +1 -1
  189. package/dist/plugins/memory-origin-law.d.ts +3 -3
  190. package/dist/plugins/one-time-migrations.d.ts +32 -56
  191. package/dist/plugins/one-time-migrations.js +25 -117
  192. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  193. package/dist/plugins/permission-rule-store-file.js +8 -1
  194. package/dist/plugins/pg-pool.js +1 -1
  195. package/dist/plugins/pg-session-storage.d.ts +17 -4
  196. package/dist/plugins/pg-session-storage.js +8 -4
  197. package/dist/plugins/run-store-sql.js +2 -2
  198. package/dist/plugins/schema-ensure-legs.js +2 -2
  199. package/dist/plugins/session-fork.d.ts +31 -9
  200. package/dist/plugins/session-fork.js +3 -2
  201. package/dist/plugins/session-placement.d.ts +4 -2
  202. package/dist/plugins/sql-json-column.d.ts +5 -1
  203. package/dist/plugins/store-backend.d.ts +11 -8
  204. package/dist/plugins/store-backend.js +8 -7
  205. package/dist/plugins/store-contracts.d.ts +1 -11
  206. package/dist/plugins/store-contracts.js +0 -9
  207. package/dist/plugins/store-sweep.d.ts +63 -0
  208. package/dist/plugins/store-sweep.js +48 -0
  209. package/dist/plugins/tidb-pool.js +1 -1
  210. package/dist/plugins/tidb-session-store.d.ts +16 -3
  211. package/dist/plugins/tidb-session-store.js +8 -4
  212. package/dist/prompt-screen.d.ts +6 -9
  213. package/dist/prompt-screen.js +2 -5
  214. package/dist/rules-consent.d.ts +20 -27
  215. package/dist/rules-consent.js +82 -6
  216. package/dist/run-cancel-context.d.ts +4 -1
  217. package/dist/run-cancel-context.js +2 -1
  218. package/dist/runs.js +1 -1
  219. package/dist/sandbox-egress-policy.js +6 -11
  220. package/dist/sandbox-rbac-rules.d.ts +88 -0
  221. package/dist/sandbox-rbac-rules.js +80 -0
  222. package/dist/sealed-key-layout.d.ts +17 -0
  223. package/dist/sealed-key-layout.js +4 -0
  224. package/dist/sealed-key.js +5 -5
  225. package/dist/security.d.ts +38 -6
  226. package/dist/security.js +9 -2
  227. package/dist/session-notify-door.d.ts +5 -3
  228. package/dist/session-resident-background.d.ts +57 -0
  229. package/dist/session-resident-background.js +20 -0
  230. package/dist/session-sync-kernel.d.ts +53 -14
  231. package/dist/session-sync-kernel.js +70 -1
  232. package/dist/session-sync.d.ts +5 -3
  233. package/dist/session-workspace.d.ts +1 -0
  234. package/dist/session-workspace.js +1 -0
  235. package/dist/sighup-idle.d.ts +9 -7
  236. package/dist/sighup-idle.js +4 -5
  237. package/dist/spec-fields.d.ts +16 -0
  238. package/dist/spec-fields.js +7 -0
  239. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  240. package/dist/stored-request-env-credentials-m38.js +8 -0
  241. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  242. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +13 -427
  243. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +33 -194
  244. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  245. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  246. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  247. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  248. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
  249. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  250. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  251. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  252. package/dist/task-settings.d.ts +59 -4
  253. package/dist/task-settings.js +50 -4
  254. package/dist/tool-approval.d.ts +2 -2
  255. package/dist/trace/core-keyset-guard.d.ts +8 -7
  256. package/dist/trace/engine-notice-wire.d.ts +1 -1
  257. package/dist/trace/engine-notice-wire.js +2 -0
  258. package/dist/trace/exit-seat.d.ts +6 -0
  259. package/dist/trace/exit-seat.js +1 -0
  260. package/dist/trace/project.js +9 -0
  261. package/dist/trace/redact.d.ts +13 -2
  262. package/dist/trace/redact.js +305 -24
  263. package/dist/trace/self-minted-wire.d.ts +326 -8
  264. package/dist/trace/self-minted-wire.js +125 -7
  265. package/dist/trace/wire-projection-faces.d.ts +1 -1
  266. package/dist/trace/wire-projection-faces.js +1 -1
  267. package/dist/usage-analytics.d.ts +4 -0
  268. package/package.json +12 -4
  269. package/scripts/drain-check-local.mjs +8 -29
  270. package/scripts/drain-check.mysql.sql +3 -24
  271. package/scripts/drain-check.pg.sql +3 -24
  272. package/skills/update-config.md +1 -1
  273. package/wire-changes/server@7.146.0.json +3221 -0
  274. package/dist/approval-batch-census.d.ts +0 -27
  275. package/dist/approval-batch-census.js +0 -27
@@ -1,5 +1,5 @@
1
- import { ControlPlaneCorruptError } from "@sema-agent/core";
2
1
  import { logStoreFormRefusal, storeFormRefusalOf } from "../memory-store-refusal.js";
2
+ import { sentControlPlaneCorrupt } from "./memory-control-plane-corrupt-reply.js";
3
3
  import { sentMemoryStoreRefusal } from "./memory-store-refusal-reply.js";
4
4
  import { sendError } from "./send.js";
5
5
  import { assertNever } from "./route-ctx.js";
@@ -70,19 +70,6 @@ export function sentMemoryComplianceFailure(res, err, audience, logger) {
70
70
  return assertNever(audience);
71
71
  }
72
72
  }
73
- if (err instanceof ControlPlaneCorruptError) {
74
- switch (audience.kind) {
75
- case "operator":
76
- sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
77
- return true;
78
- case "session_owner":
79
- audience.withheld(err.message);
80
- sendError(res, 500, "internal.memory_control_plane_corrupt", "session memory erasure could not be completed — this deployment's memory control plane could not be read cleanly (depending on where it stopped, either nothing was changed or the deletions were already committed and a replay finishes them); ask the operator to repair it, then resend with the same requestId");
81
- return true;
82
- default:
83
- return assertNever(audience);
84
- }
85
- }
86
- return false;
73
+ return sentControlPlaneCorrupt(res, err, audience);
87
74
  }
88
75
  //# sourceMappingURL=memory-compliance-failure-reply.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * core 的 `ControlPlaneCorruptError` ⇒ typed 500 `internal.memory_control_plane_corrupt` 的 **HTTP 映射** —— 唯一一处码字面量、唯一一处 `instanceof` 判别。
3
+ *
4
+ * ## 为什么是一只
5
+ * 这是 core 的 fail-closed 信号:记忆引擎的控制面(账本 / 事务日志 / 清标账 / 治理账)说不清,它宁可不答,也不在一个说不清的账上拼一个答案。
6
+ * 凡是会碰控制面的 operator 记忆口,对它的答法必须是同一句话:消费端按 `errorCode` 分支,运维有具体动作(修 / 重建账本后重试)。
7
+ * 此前这条映射写在四个地方(导出 / 导入口、外源标记口、整理口、合规口),码字面量与判别各一份 = 第五个口加错码或漏一支的口子。
8
+ * 同族的店形拒早已是一只(`sentMemoryStoreRefusal`,在 `memory-store-refusal-reply.ts`);本文件是第二只。挂在每一个**已分类收尾**上:
9
+ * `routes/memory-bundle.ts`(导出 / 导入)· `routes/memory-origin.ts`(外源列表 / 清标 / 清标账列表 —— 清标账列表是纯控制面读,店形拒不挂在它上面,它的损坏信号走这一只)·
10
+ * `routes/memory-consolidation.ts`(整理)· `memory-compliance-failure-reply.ts`(出处 / 抹除,两受众)。
11
+ *
12
+ * ## 文案按受众分(码与状态不分;与 `memory-compliance-failure-reply.ts` 同一条规则)
13
+ * · operator ⇒ 引擎原句上 wire(点名**哪一张**控制面账本坏了,运维下一步动作取决于它;原句带宿主路径,过了 operator 门的受众拿它不是泄露);
14
+ * · 会话所有者(自助抹除口,只有这一个口有这个受众)⇒ 一句固定指路,引擎原句整句交 `withheld` 落运维行 —— 不逐句判断哪句安全。
15
+ * 固定句**一码两义都真**:操作前账本读不出 ⇒ 什么都没发生;落定后残留枚举失败 ⇒ 抹除已提交、重放收敛(core 原话)。两句都不许诺零删除,
16
+ * 都指路「运维修好后用同一个 requestId 重发」。本层**不**嗅 core 文案去分辨哪一义(拿上游自由文本当判别式是第二真源)。
17
+ *
18
+ * 两处 `sendError` 的码都写**字面量**:错误码目录门(`test/error-code-catalog-live.test.ts`)与文案冻结门(`test/api-error-text-freeze.test.ts`,
19
+ * 固定句是它冻的那一句)扫的是源码字面量,变量态的码 / 句对它们隐形。
20
+ */
21
+ import type { ServerResponse } from "node:http";
22
+ import type { MemoryComplianceAudience } from "./memory-compliance-failure-reply.js";
23
+ /** `err` 是 core 的控制面损坏 ⇒ 500 `internal.memory_control_plane_corrupt`(文案按受众),返回 true(已应答);否则 false,一个字节都不写。 */
24
+ export declare function sentControlPlaneCorrupt(res: ServerResponse, err: unknown, audience: MemoryComplianceAudience): boolean;
25
+ //# sourceMappingURL=memory-control-plane-corrupt-reply.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { ControlPlaneCorruptError } from "@sema-agent/core";
2
+ import { assertNever } from "./route-ctx.js";
3
+ import { sendError } from "./send.js";
4
+ export function sentControlPlaneCorrupt(res, err, audience) {
5
+ if (!(err instanceof ControlPlaneCorruptError))
6
+ return false;
7
+ switch (audience.kind) {
8
+ case "operator":
9
+ sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
10
+ return true;
11
+ case "session_owner":
12
+ audience.withheld(err.message);
13
+ sendError(res, 500, "internal.memory_control_plane_corrupt", "session memory erasure could not be completed — this deployment's memory control plane could not be read cleanly (depending on where it stopped, either nothing was changed or the deletions were already committed and a replay finishes them); ask the operator to repair it, then resend with the same requestId");
14
+ return true;
15
+ default:
16
+ return assertNever(audience);
17
+ }
18
+ }
19
+ //# sourceMappingURL=memory-control-plane-corrupt-reply.js.map
@@ -72,8 +72,10 @@ export declare function sendPrincipalRefusal(res: ServerResponse, req: IncomingM
72
72
  * 每面一句模块级常量(冻结门按常量解引用逐字钉)。三句都**不含任何值**(令牌 / 头名 / 路径实参一概不回显),且**不区分**
73
73
  * 「没带」与「带错」—— 同一道门上两者逐字同体,区分就是探测面。
74
74
  * · `credential` —— 全局凭据门(`dispatch.ts`):部署配了 service token,请求没带或带的不认(registry 登录令牌验不过同属此面);
75
- * · `operator` —— 只给运维凭据开的口(`/metrics*` 与 `GET /v1/tasks/source-summary`):不带主体头的 service token 开得了(带主体头的是那位主体,
76
- * {@link actsForDeployment}),指标口另认只读的 `METRICS_TOKEN`、`source-summary` 另认只读的 `TRACE_TOKEN`(都在全局门之前判);
75
+ * · `operator` —— 只给运维凭据开的口(`/metrics*` 与 `GET /v1/tasks/source-summary`):不带主体头的 service token 开得了 —— 在部署让它代自己说话时
76
+ * ({@link actsForDeployment};带主体头的是那位主体;声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false` 的部署上它不代任何人),指标口另认只读的 `METRICS_TOKEN`、
77
+ * `source-summary` 另认只读的 `TRACE_TOKEN`(都在全局门之前判)。句子只有一句、两形都真:service token 那条出路带条件「在这台部署让它代自己说话时」,
78
+ * 并指 `/health` 的自报位 `auth.serviceTokenActsForDeployment` 去读这台部署的答案 —— 铸点不读配置、签名不变(一面一只常量);
77
79
  * · `identity` —— 过了凭据门(或这台部署没配 service token)、但这一口要一个**验过的调用方身份**:属主寻址的读 / 写面、规则面、
78
80
  * 记忆面。在配了 service token 的部署上,「带 service token 却没身份」只会落在只认身份的口(规则 / 记忆 / 会话策略)上 ——
79
81
  * 这时再叫调用方「去带 service token」是假出路,所以这一面的句子只讲身份。
@@ -81,7 +83,7 @@ export declare function sendPrincipalRefusal(res: ServerResponse, req: IncomingM
81
83
  */
82
84
  export type UnauthorizedFace = "credential" | "operator" | "identity";
83
85
  export declare const UNAUTHORIZED_CREDENTIAL = "unauthorized \u2014 the request carries no credential this deployment accepts: send the deployment's service token as a Bearer token in the Authorization header (its operator set it as SERVICE_AUTH_TOKEN or SERVICE_AUTH_TOKENS; a sema-up deployment keeps SERVICE_AUTH_TOKEN in ~/.sema-up/<project>/.env on the machine that ran sema-up)";
84
- export declare const UNAUTHORIZED_OPERATOR = "unauthorized \u2014 this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header without a principal header (a service-token request that names a principal acts for that principal, not for the deployment), or a read-only token this endpoint accepts (METRICS_TOKEN for the metrics endpoints, TRACE_TOKEN for the task source summary)";
86
+ export declare const UNAUTHORIZED_OPERATOR = "unauthorized \u2014 this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header without a principal header where this deployment lets its service token act for the deployment itself (GET /health reports it as auth.serviceTokenActsForDeployment; a service-token request that names a principal acts for that principal, not for the deployment), or a read-only token this endpoint accepts (METRICS_TOKEN for the metrics endpoints, TRACE_TOKEN for the task source summary)";
85
87
  export declare const UNAUTHORIZED_IDENTITY = "unauthorized \u2014 this endpoint acts for a named caller and the request carries no caller identity this deployment verifies: identify the caller the way this deployment does (a registry sign-in token as the Bearer token, a signed principal token on a direct-door deployment, or the principal header set by a trusted front end)";
86
88
  export declare function sendUnauthorized(res: ServerResponse, face: UnauthorizedFace): void;
87
89
  /**
@@ -128,15 +130,24 @@ export declare function wiredIdentitySources(w: IdentityWiring): readonly Identi
128
130
  */
129
131
  export declare function devOpenDeployment(w: IdentityWiring): boolean;
130
132
  /**
131
- * service 凭据**代部署本身**说话:出示了这台部署的 service 凭据({@link systemFor}),且不代任何验过的主体。带主体头的 service 凭据请求是前端(BFF)
132
- * 代那位主体发的 —— 它读那位主体,不读全舰队;部署姿态(单用户 / 多租)不改这一句(单用户形上的主体头同样是「这次请求为谁」)。
133
- * 两处读它:fleet 调用方的凭据臂({@link sessionCallerOf})与 `/metrics` 族的 service 臂(`dispatch.ts`)。先判凭据、后取主体:`principal` 只在出示了
134
- * service 凭据时才求值(调用点那一次 {@link gatedPrincipal}),没带凭据的请求不因主体头的形而改答。
133
+ * 🔴 **律(一句,一个参数)**:出示这台部署的 service 凭据且不报主体的调用方代部署本人说话(fleet),**除非部署声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`**
134
+ * —— 此时它是无身份调用方,按各面既有的「过了凭据门、没有主体」那一臂答。带主体头的 service 凭据请求是前端(BFF)代那位主体发的 —— 它读那位主体,
135
+ * 不读全舰队(两形同读);部署姿态(单用户 / 多租)不改这一句(单用户形上的主体头同样是「这次请求为谁」)。
136
+ * 本函数是这条律的**唯一实施点**:声明位({@link ServiceConfig.serviceTokenActsForDeployment},env 一处)是这里的一个合取项,两个读者一次继承 ——
137
+ * fleet 调用方的凭据臂({@link sessionCallerOf})与 `/metrics` 族的 service 臂(`dispatch.ts`);不加拒码、不加闸点。自报位 `/health.auth.serviceTokenActsForDeployment`
138
+ * 投的是同一只读法({@link serviceTokenDeclaredActsForDeployment})与同一只武装谓词(`http/health-self-report.ts`)。先判声明与凭据、后取主体:`principal` 只在两者都成立时才求值(调用点那一次 {@link gatedPrincipal}),
139
+ * 没带凭据的请求不因主体头的形而改答。
135
140
  */
136
141
  export declare function actsForDeployment(req: IncomingMessage, config: ServiceConfig, principal: () => Principal | undefined): boolean;
142
+ /**
143
+ * 声明位 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT` 的**唯一读法**:门({@link actsForDeployment})、自报位(`http/health-self-report.ts`)、起服行
144
+ * ({@link buildServiceTokenPostureNotice})三处都经它,不各写一份真值判断。只有字面 `true` 是「代部署本人」:`loadConfig` 恒写这一格(缺省 `true`),
145
+ * 绕过类型手搓的配置缺这一格 ⇒ 读作不代(fail-closed:安全轴上的缺席不静默放宽),三处同读 —— 不会出现「门收窄、自报位说缺席、起服行沉默」。
146
+ */
147
+ export declare function serviceTokenDeclaredActsForDeployment(config: Pick<ServiceConfig, "serviceTokenActsForDeployment">): boolean;
137
148
  /**
138
149
  * 🔴 **「这个调用方是不是 fleet 调用方(看得到所有租户)」的唯一铸点**({@link SessionCaller}):验过的主体 + 是不是 fleet —— {@link devOpenDeployment} 上的任何
139
- * 调用方,或 {@link actsForDeployment}(出示 service 凭据、不报主体)。带主体头的是那位主体,不是 fleet;只读 trace token 不是 service 凭据;operator 不在这里
150
+ * 调用方,或 {@link actsForDeployment}(出示 service 凭据、不报主体,除非部署声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`)。带主体头的是那位主体,不是 fleet;只读 trace token 不是 service 凭据;operator 不在这里
140
151
  * (运维口各自读 {@link explicitOperatorOk})。读者:会话行属主门(无主 placed 行的放行臂)与 fleet 面的 {@link fleetViewOf} —— 同一问一只读法。
141
152
  */
142
153
  export declare function sessionCallerOf(req: IncomingMessage, w: IdentityWiring): SessionCaller;
@@ -182,16 +193,28 @@ export declare function explicitOperatorOk(principal: string | undefined, roster
182
193
  /** 「这台部署**有没有**运维」的唯一读法(名单非空 ∨ 单用户声明在场)—— 能力事实 `operatorLane`、整理阀门的可达性告警、起服提示同读这一只。 */
183
194
  export declare function operatorRosterPresent(roster: OperatorRoster): boolean;
184
195
  /**
185
- * 起服提示([ref]):单用户形(`REQUIRE_PRINCIPAL` 未开)+ 没有运维名册 + 这台部署有审批底座 ⇒ 一行 warn。这一形在「空名单 = 人人是」那条读法退役之后
186
- * 丢的东西就是审批队列的运维视图(跨 scope 列表 / `?owner=` / 无主体调用方的整个队列),补回它只要一行配置 —— 不拒启:升级那一拍打掉一台今天跑得好好的
187
- * 单用户机器比一条提示重。多租形不提示(它另有拒启不变量);没有审批底座的形什么都没少。
196
+ * 起服提示([ref]):单用户形(`REQUIRE_PRINCIPAL` 未开)+ 没有运维名册 ⇒ 一行 warn。「空名单 = 人人是」那条读法退役之后这一形没有运维:
197
+ * 运维口(诊断 `GET /v1/diagnostics/wiring` —— 每台 main.ts 装起来的部署都挂着它 —— 、`GET /v1/outcomes`、管理与记忆治理口)对谁都答 501,
198
+ * 审批队列(挂着时)只给每个调用方看自己的;补回它们只要一行配置 —— 不拒启:升级那一拍打掉一台今天跑得好好的单用户机器比一条提示重。
199
+ * 多租形不提示(它另有拒启不变量)。
188
200
  */
189
- export declare function buildOperatorUndeclaredNotice(config: OperatorRoster & Pick<ServiceConfig, "requirePrincipal">, approvalFaceWired: boolean): {
201
+ export declare function buildOperatorUndeclaredNotice(config: OperatorRoster & Pick<ServiceConfig, "requirePrincipal">): {
190
202
  event: "operator_undeclared_single_user";
191
203
  fields: {
192
204
  detail: string;
193
205
  };
194
206
  } | undefined;
207
+ /**
208
+ * 起服一行:部署声明 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false` ⇒ `service_token_posture { actsForDeployment: false }`(info);缺省不打。
209
+ * 声明形让运维面板开位当天成片 401,而「位关了」与「token 错了」在 wire 上同体(反探测律),这一行是起服时唯一一处说清这台部署选了哪一读法的地方。
210
+ * 不加逐请求事件(请求日志有 route / status,无凭据来源)。判据与字段单源在这里,装配处(`boot/stage-07-capability-layer.ts`)只调一次、原样打。
211
+ */
212
+ export declare function buildServiceTokenPostureNotice(config: Pick<ServiceConfig, "serviceTokenActsForDeployment">): {
213
+ event: "service_token_posture";
214
+ fields: {
215
+ actsForDeployment: false;
216
+ };
217
+ } | undefined;
195
218
  /** operator 面族在「名单为空」这一形上的**唯一**拒发口:事实 `operatorLane`(`capability-facts.ts`)为假 ⇒ 这一面对任何
196
219
  * 身份都打不通 = 这台部署上不可用 ⇒ 501,不是鉴权拒(403 只留给「名单非空而你不在名单上」)。能力位读的是同一只事实。 */
197
220
  export declare function sendOperatorPrincipalsRequired(res: ServerResponse): void;
@@ -1,6 +1,6 @@
1
1
  import { timingSafeEqual } from "node:crypto";
2
2
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
3
- import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal, ssoVerifiedPrincipalInc, ssoVerifiedScope, ownerlessRowOpen, mintPrincipal } from "../security.js";
3
+ import { PRINCIPAL_TOKEN_HEADER, sameSessionOwner, verifiedPrincipal, ssoVerifiedPrincipal, ssoVerifiedPrincipalInc, ssoVerifiedScope, ownerlessRowOpen, mintPrincipal } from "../security.js";
4
4
  import { IDENTITY_SOURCES, runIdentityClaimOverflow } from "../identity-source.js";
5
5
  import { sendError } from "./send.js";
6
6
  export function headerStr(v) {
@@ -120,7 +120,7 @@ export function sendPrincipalRefusal(res, req, config) {
120
120
  sendError(res, 401, "auth.principal_required", `missing principal header '${config.principalHeader}'`);
121
121
  }
122
122
  export const UNAUTHORIZED_CREDENTIAL = "unauthorized — the request carries no credential this deployment accepts: send the deployment's service token as a Bearer token in the Authorization header (its operator set it as SERVICE_AUTH_TOKEN or SERVICE_AUTH_TOKENS; a sema-up deployment keeps SERVICE_AUTH_TOKEN in ~/.sema-up/<project>/.env on the machine that ran sema-up)";
123
- export const UNAUTHORIZED_OPERATOR = "unauthorized — this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header without a principal header (a service-token request that names a principal acts for that principal, not for the deployment), or a read-only token this endpoint accepts (METRICS_TOKEN for the metrics endpoints, TRACE_TOKEN for the task source summary)";
123
+ export const UNAUTHORIZED_OPERATOR = "unauthorized — this endpoint is for the deployment's operators and the request carries no operator credential: send the deployment's service token as a Bearer token in the Authorization header without a principal header where this deployment lets its service token act for the deployment itself (GET /health reports it as auth.serviceTokenActsForDeployment; a service-token request that names a principal acts for that principal, not for the deployment), or a read-only token this endpoint accepts (METRICS_TOKEN for the metrics endpoints, TRACE_TOKEN for the task source summary)";
124
124
  export const UNAUTHORIZED_IDENTITY = "unauthorized — this endpoint acts for a named caller and the request carries no caller identity this deployment verifies: identify the caller the way this deployment does (a registry sign-in token as the Bearer token, a signed principal token on a direct-door deployment, or the principal header set by a trusted front end)";
125
125
  export function sendUnauthorized(res, face) {
126
126
  switch (face) {
@@ -139,15 +139,10 @@ export function sendUnauthorized(res, face) {
139
139
  }
140
140
  }
141
141
  }
142
- function sameIdentity(principal, owner) {
143
- return principal === owner;
144
- }
145
142
  export function runOwnerReadable(principal, owner, config) {
146
143
  if (owner === null)
147
144
  return true;
148
- if (principal !== undefined && sameIdentity(principal, owner))
149
- return true;
150
- return !DEPLOYMENT_SHAPE_PREDICATES.principalRequired.holds(config) && owner === "anon:shell-live" && principal === undefined;
145
+ return sameSessionOwner(config, owner, principal ?? null);
151
146
  }
152
147
  export function hasServiceAuth(config) {
153
148
  return Boolean(config.authToken) || Object.keys(config.authTokens ?? {}).length > 0;
@@ -173,7 +168,10 @@ export function devOpenDeployment(w) {
173
168
  return wiredIdentitySources(w).length === 0 && !w.config.traceToken && !DEPLOYMENT_SHAPE_PREDICATES.principalRequired.holds(w.config);
174
169
  }
175
170
  export function actsForDeployment(req, config, principal) {
176
- return systemFor(req, config) !== undefined && principal() === undefined;
171
+ return serviceTokenDeclaredActsForDeployment(config) && systemFor(req, config) !== undefined && principal() === undefined;
172
+ }
173
+ export function serviceTokenDeclaredActsForDeployment(config) {
174
+ return config.serviceTokenActsForDeployment === true;
177
175
  }
178
176
  export function sessionCallerOf(req, w) {
179
177
  const principal = gatedPrincipal(req, w.config);
@@ -211,18 +209,24 @@ export function explicitOperatorOk(principal, roster) {
211
209
  export function operatorRosterPresent(roster) {
212
210
  return roster.operatorSingleUser === true || roster.operatorPrincipals.length > 0;
213
211
  }
214
- export function buildOperatorUndeclaredNotice(config, approvalFaceWired) {
215
- if (config.requirePrincipal === true || operatorRosterPresent(config) || !approvalFaceWired)
212
+ export function buildOperatorUndeclaredNotice(config) {
213
+ if (config.requirePrincipal === true || operatorRosterPresent(config))
216
214
  return undefined;
217
215
  return {
218
216
  event: "operator_undeclared_single_user",
219
217
  fields: {
220
218
  detail: "this single-user deployment declares no operator: OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not set, so no caller is an operator — " +
221
- "GET /v1/approvals, its stream, /v1/assistant/inbox and /v1/assistant/tasks show each caller only its own pending approvals (a caller that sends no principal sees only the anonymous ones - the cards and tasks that were submitted without a principal - never a named principal's) and ignore ?owner=. " +
219
+ "operator-only endpoints such as GET /v1/diagnostics/wiring, GET /v1/outcomes and the admin and memory-governance endpoints answer 501 capability.operator_principals_required to every caller, and " +
220
+ "the approval queues (GET /v1/approvals, its stream, /v1/assistant/inbox and /v1/assistant/tasks, where mounted) show each caller only its own pending approvals (a caller that sends no principal sees only the anonymous ones - the cards and tasks that were submitted without a principal - never a named principal's) and ignore ?owner=. " +
222
221
  "To act as this deployment's operator, add one line: OPERATOR_SINGLE_USER=true",
223
222
  },
224
223
  };
225
224
  }
225
+ export function buildServiceTokenPostureNotice(config) {
226
+ if (serviceTokenDeclaredActsForDeployment(config))
227
+ return undefined;
228
+ return { event: "service_token_posture", fields: { actsForDeployment: false } };
229
+ }
226
230
  export function sendOperatorPrincipalsRequired(res) {
227
231
  sendError(res, 501, "capability.operator_principals_required", `this endpoint is operator-only and this deployment has no operator roster (OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not declared), so no caller can be an operator; ${buildOperatorRosterWayOut()}`);
228
232
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 请求守门族([ref] 切片:`http/server.ts` 4837–5195 + `MAX_BODY` 逐字搬家,正文一字未改)。
2
+ * 请求守门族([ref] 切片:`http/server.ts` 4837–5195 逐字搬家,正文一字未改;JSON 体上限的数住叶子 `json-body-limit.ts`)。
3
3
  * 配额 / 租约 / 用量窗 / 限流 / owner-scope / 体读取 —— 每只守门的判据与它自己的头注原位随行。
4
4
  * ctx 字段 1 只(`deps`):本族对外围的耦合就这一格,它也是这一族的可测性读数。
5
5
  */
@@ -9,14 +9,14 @@ import { runRowReadable } from "./owner-access.js";
9
9
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
10
10
  import { quotaWindowResetsAt } from "../observability/cost-quota.js";
11
11
  import { escapedStatementBytes, packetWallStatementBytes, rowWallPayloadBytes } from "../plugins/sql-driver.js";
12
- const MAX_BODY = 8 * 1024 * 1024;
12
+ import { MAX_JSON_BODY_BYTES } from "../json-body-limit.js";
13
13
  export function refuseSubmitBody(walls, body) {
14
14
  const rowWall = walls.maxRowBytes;
15
15
  const packetWall = walls.maxPacketBytes;
16
16
  const row = rowWall === null ? null : { wallBytes: rowWall, limitBytes: rowWallPayloadBytes(rowWall, "json") };
17
17
  const packet = packetWall === null ? null : { wallBytes: packetWall, limitBytes: packetWallStatementBytes(packetWall) };
18
- const liveRow = row !== null && row.limitBytes < MAX_BODY ? row : null;
19
- const livePacket = packet !== null && packet.limitBytes < 2 * MAX_BODY ? packet : null;
18
+ const liveRow = row !== null && row.limitBytes < MAX_JSON_BODY_BYTES ? row : null;
19
+ const livePacket = packet !== null && packet.limitBytes < 2 * MAX_JSON_BODY_BYTES ? packet : null;
20
20
  if (liveRow === null && livePacket === null)
21
21
  return null;
22
22
  const json = JSON.stringify(body);
@@ -228,8 +228,8 @@ export function createRequestGuards(ctx) {
228
228
  let size = 0;
229
229
  for await (const c of req) {
230
230
  size += c.length;
231
- if (size > MAX_BODY)
232
- throw bodyTooLargeError("request body too large", MAX_BODY);
231
+ if (size > MAX_JSON_BODY_BYTES)
232
+ throw bodyTooLargeError("request body too large", MAX_JSON_BODY_BYTES);
233
233
  chunks.push(c);
234
234
  }
235
235
  if (chunks.length === 0)
@@ -24,6 +24,28 @@ import type { createRequestGuards } from "./request-guards.js";
24
24
  import type { FlatServiceDeps } from "./server.js";
25
25
  import { type ResumeRefusalAnswer } from "./checkpoint-ctx.js";
26
26
  import { type ResumeRestatement } from "./resume-restatement.js";
27
+ /**
28
+ * 🔴 **resume / 决断腿上一次 `CheckpointError` 拒的处置 —— 一张表:core 的码闭集 → 处置词**(唯一读口 {@link resumeRefusalDisposition};
29
+ * 7.113.0 提货车 UP ⑤ 起穷举,此前是 catch 臂里一串 `e.code === … ||` 手列)。
30
+ *
31
+ * doctrine(只写这一次):处置词说的是**这张卡与这条 run 行此刻的处境**,不是「重试会不会成」——
32
+ * · `retriable` —— PRE-CAS 拒:**卡仍 pending ⇒ run 行重新 park**(`suspended` / `needs_review`),wire 体自述 `retriable:true`
33
+ * (契约附录 A.1 注的定义:「卡仍 pending,不是『重试会成功』」)。出路(改那一格 / 重取坐标 / 回到写它的那一版引擎 / 换对 worker / 取消)
34
+ * 由码、`field`、`writerVersion` / `readerVersion` 与报文说,**不由 `retriable` 说**;
35
+ * · `terminal` —— 卡已消费 / 已不在 / 本腿读不懂 ⇒ 驱行终局,wire 体不带 `retriable`;
36
+ * · `cancelled` —— 取消 ⇒ 取消终局。
37
+ * 行的落点与 wire 的 `retriable` 读**同一次**求值(catch 臂一处),所以同一处境只有一个答案。决断口 409 的成因({@link decisionRefusalCauseOf})只决定码名、
38
+ * 不另答处置:决断类三词都出自 `checkpoint.invalid_outcome`,落点读本表那一行。「停驻行的格式比本引擎新」只有 core 一个来源(`checkpoint.unsupported_version`
39
+ * + `version_newer` ⇒ `retriable`);续跑上下文的写者版本只让那一码的 409 体多带 `writerVersion` / `readerVersion` 两件事实,不参与判处置。
40
+ * 同码多铸点、可恢复性不同的码再按它自己的判别位闭集分(`"by_reason"` → {@link WORKER_SWAP_REDEEMABLE})。
41
+ * 表是 `Record<CheckpointError["code"], …>` ⇒ core 加一个码而本表没跟 = **tsc 红**(手列串的病:新码不在串里 ⇒ 静默落终局 —— core 7.39.0 的
42
+ * `checkpoint.config_refused` 提货当拍就是这样把一张 pending 卡孤儿化的,钉 `test/durable-resume-http.test.ts` [ref] 两格)。
43
+ *
44
+ * 驱终局为什么危险(原 catch 臂注,D-1 / core 1.101):PRE-CAS 拒时卡仍 pending,驱终局会释放 task_active 并孤儿化那张卡(下一次 /decide → 404);
45
+ * re-park 让改正后的重试能再认领。驱终局只给「卡已消费或已不在」的码,免得留一条僵尸 `running` 行让 reapStale 误标并攥着会话锁 ~runStaleSec。
46
+ */
47
+ export type CheckpointErrorDispositionWord = "cancelled" | "retriable" | "terminal" | "by_reason";
48
+ export declare const CHECKPOINT_ERROR_DISPOSITION: Readonly<Record<CheckpointError["code"], CheckpointErrorDispositionWord>>;
27
49
  /**
28
50
  * 🔴 **一次 `CheckpointError` 拒的处置 —— 唯一读口**:{@link CHECKPOINT_ERROR_DISPOSITION} 的读法(doctrine 在那张表头)。resume 腿的 catch 臂只调它一次,
29
51
  * 行的落点与 wire 的 `retriable` 都读这一个结果。纯函数,钉直接调。运行期遇到表外的码(装树 core 比本二进制多一个码的版本偏斜 —— 型面上拼不出来)
@@ -1,6 +1,6 @@
1
1
  import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
2
2
  import { createHash } from "node:crypto";
3
- import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY } from "@sema-agent/core";
3
+ import { uuidv7, resumeWithVerification, CheckpointError, judgeCheckpointRow, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY } from "@sema-agent/core";
4
4
  import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
5
5
  import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
6
6
  import { headReadOf } from "../session-incarnation.js";
@@ -22,7 +22,7 @@ import { approverFacingArgs, createApprovalCardEmitter, declaredApprovalFrameFam
22
22
  import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
23
23
  import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
24
24
  import { serviceVersion } from "../service-version.js";
25
- import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
25
+ import { TERMINAL_PARK_GATE_KINDS, UnreadableCheckpointRowError } from "../plugins/checkpoint-store-face.js";
26
26
  import { withPrincipal } from "../observability/principal-context.js";
27
27
  import { withModelReachScope } from "../model-reach-scope.js";
28
28
  import { legFactsOf } from "../leg-facts.js";
@@ -103,7 +103,7 @@ const WORKER_SWAP_REDEEMABLE = {
103
103
  function checkpointRowRedeemableElsewhere(reason) {
104
104
  return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
105
105
  }
106
- const CHECKPOINT_ERROR_DISPOSITION = {
106
+ export const CHECKPOINT_ERROR_DISPOSITION = {
107
107
  "checkpoint.resume_aborted": "cancelled",
108
108
  "checkpoint.invalid_outcome": "retriable",
109
109
  "checkpoint.reopen_revote": "retriable",
@@ -208,6 +208,15 @@ function approvalStaleAnswer(facts) {
208
208
  }),
209
209
  };
210
210
  }
211
+ function rowRefusalMessageOf(verdict, diagnostic) {
212
+ if (verdict.kind === "ok")
213
+ return undefined;
214
+ return diagnostic === undefined ? verdict.sentence : `${verdict.sentence} Diagnostic: ${diagnostic}`;
215
+ }
216
+ function unreadableRowMessageOf(e) {
217
+ const text = e instanceof Error ? e.message : String(e);
218
+ return (e instanceof UnreadableCheckpointRowError ? rowRefusalMessageOf(e.rowVerdict, text) : undefined) ?? text;
219
+ }
211
220
  function acceptableFormFacts(detail) {
212
221
  const rewritten = detail !== null && typeof detail === "object" ? detail.editRewritten : undefined;
213
222
  if (rewritten === null || typeof rewritten !== "object" || !Object.hasOwn(rewritten, "acceptableForm"))
@@ -241,7 +250,7 @@ export function decidedActionOf(binding) {
241
250
  return missingKeys.length === 0 ? { action: { boundCallId: boundCallId, boundInputHash: boundInputHash } } : { missingKeys };
242
251
  }
243
252
  export function createResumeLegs(ctx) {
244
- const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
253
+ const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
245
254
  const ctxOpening = (sessionId) => ({ sessionId, opener: deps.checkpointCtxSealing });
246
255
  const resumeFleetScope = (req, auth) => req ? (gatedPrincipal(req, deps.config) ?? "default") : (auth.principal ?? "default");
247
256
  async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider, restatement) {
@@ -303,7 +312,7 @@ export function createResumeLegs(ctx) {
303
312
  deps.logger?.warn?.("approval_decide_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
304
313
  return noApproval;
305
314
  }
306
- return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
315
+ return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: unreadableRowMessageOf(e) }) };
307
316
  }
308
317
  if (!cp) {
309
318
  if (!(await mayDiscloseRowError(cs, token, decider)))
@@ -319,20 +328,20 @@ export function createResumeLegs(ctx) {
319
328
  }
320
329
  const decideGateKind = cp.gate?.kind;
321
330
  if (!isApprovalGateKind(decideGateKind)) {
322
- const activeForPath = decideGateKind
323
- ? await deps.runStore?.getActiveTaskId(sessionId).catch(() => undefined)
324
- : undefined;
325
- const gnPath = decideGateKind && activeForPath
326
- ? resumeEntryForGate(decideGateKind, { sessionId, taskId: activeForPath })
327
- : null;
331
+ if (typeof decideGateKind !== "string" || decideGateKind === "") {
332
+ const rowSentence = rowRefusalMessageOf(judgeCheckpointRow(cp));
333
+ if (rowSentence !== undefined)
334
+ return { status: 409, body: refusalBody({ errorCode: "gate_not_tool_approval", message: rowSentence }) };
335
+ return { status: 409, body: refusalBody({ errorCode: "gate_not_tool_approval", message: "task is suspended on a gate whose kind this engine cannot read, so it cannot say which endpoint resolves it" }) };
336
+ }
337
+ const activeForPath = await deps.runStore?.getActiveTaskId(sessionId).catch(() => undefined);
338
+ const gnPath = activeForPath ? resumeEntryForGate(decideGateKind, { sessionId, taskId: activeForPath }) : null;
328
339
  return {
329
340
  status: 409,
330
341
  body: refusalBody({
331
342
  errorCode: "gate_not_tool_approval",
332
- message: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
333
- facts: {
334
- ...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
335
- },
343
+ message: `task is suspended on a '${decideGateKind}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
344
+ facts: { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } },
336
345
  }),
337
346
  };
338
347
  }
@@ -747,7 +756,7 @@ export function createResumeLegs(ctx) {
747
756
  let resumeLegLive = false;
748
757
  const subagentHandleEvictions = [];
749
758
  let claimedRow = false;
750
- const cancelCtrl = createRunAbort();
759
+ const cancelCtrl = createRunAbort(principal ?? null);
751
760
  let cancelLedgerAppend;
752
761
  const preemptCtrl = new AbortController();
753
762
  if (taskId && deps.runStore) {
@@ -1662,7 +1671,7 @@ export function createResumeLegs(ctx) {
1662
1671
  const routeCtxBase = {
1663
1672
  deps,
1664
1673
  registry: { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters },
1665
- helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk },
1674
+ helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk },
1666
1675
  local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
1667
1676
  legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview, sessionNotify: sessionNotifyDoor },
1668
1677
  };
@@ -79,6 +79,12 @@ export interface RouteHelpers {
79
79
  ownerLeaseDenied(res: ServerResponse, owner: string | undefined): Promise<boolean>;
80
80
  /** [ref]-T1:治理窗 pre-admission(异步 202 车道的唯一 429 出路;key 公式与 core 单源)。 */
81
81
  usageWindowDenied(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
82
+ /**
83
+ * 在飞 run 并发帽的提交门准入(`RUN_MAX_CONCURRENT*`,缺省关;判据、席位与拒的形全文在 `http/run-concurrency-cap.ts`):放行 ⇒ 席位(登记那一拍与 `finally` 放);
84
+ * 超帽 ⇒ 已应答 429 + `Retry-After`、回 `undefined`。只挂**新提交**门(`createDurableRun` / `POST /v1/tasks` / `POST /v1/tasks/stream`),
85
+ * 排在幂等重放与 `usageWindowDenied` 之后、任何建行 / 认领 / SSE 头之前。
86
+ */
87
+ admitRun(req: IncomingMessage, res: ServerResponse): import("./run-concurrency-cap.js").RunSlot | undefined;
82
88
  safeDecode(seg: string): string | null;
83
89
  /** run 面的属主门(谓词 `runOwnerReadable`;非属主 ⇒ 404 `not_found.run` + 长文,与 run 读口的 unknown 臂同码同串,[ref])。 */
84
90
  runOwnerOk(req: IncomingMessage, res: ServerResponse, owner: string | null): boolean;
@@ -191,17 +197,10 @@ export interface CheckpointCtx {
191
197
  * 传原 ctx / 其展开 ⇒ 标随行。缺席 = 不是无人值守腿(HTTP 提交腿;7.106.0 前落盘的行 —— 那时这些腿不铸模式,checkpoint 里也没有)。
192
198
  */
193
199
  unattendedLeg?: true;
194
- /**
195
- * 体里 `settings.permissions.allow` 的**读法记号**(闭集只有当前读法一词 `"allow_layer"`):在场 = 这一行是当前读法下写下的、体里带非空 `allow`,
196
- * 它按「交给引擎的 allow 层」写、也按它读。**服务端写、不来自请求体**(与 {@link unattendedLeg} 同为 `body` 的兄弟键)。
197
- * 缺席(≤7.114.0,名单时代)或旧词 `"statement"`(7.115.0–7.116.x,表态时代)而体里带非空 `allow` ⇒ 续跑腿响亮拒、行字节不动(规则与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注)。
198
- * 写点只有 `buildCheckpointCtx`(同模块;新的持久化写);续跑腿的写回传原 ctx / 其展开 ⇒ 记号随行。体里没有 `allow` 的行不写它。
199
- */
200
- permissionsAllowReading?: import("./checkpoint-ctx.js").PermissionsAllowReading;
201
200
  /**
202
201
  * 体里**模式词的读法记号**(闭集只有当前一词 `"entry_word"`):在场 = 这一行体的顶层 `permissionMode` 是这条 run 模式词唯一的源(提交入口盖过运维
203
202
  * `AUTONOMY=plan` 的地板;`plan_review` 写回改它),续跑照它读。**服务端写、不来自请求体**。缺席(7.121.x 及以前写下的行 —— 那时运维的 plan 由治理层每段重钳)
204
- * 而本部署此刻有入口地板、体的词被地板钳会变 ⇒ 续跑腿响亮拒、行字节不动(规则与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注第三问)。
203
+ * 而本部署此刻有入口地板、体的词被地板钳会变 ⇒ 续跑腿响亮拒、行字节不动(规则与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注「模式词问」)。
205
204
  * 写点两个(写模式词的人盖记号,同模块):`buildCheckpointCtx`(每一次新的持久化写都写)与 `withPermissionModeRewritten`(`plan_review` 改词的写回);
206
205
  * 不改词的续跑腿写回传原 ctx ⇒ 记号随行。
207
206
  */
@@ -210,8 +209,10 @@ export interface CheckpointCtx {
210
209
  * 🔴 [ref] —— **写这份上下文的服务版本**(`package.json` 的 `version`),**服务端写、不来自请求体**(与 {@link unattendedLeg} 同为 `body` 的兄弟键)。
211
210
  * 写点两个:`buildCheckpointCtx`(每一次新的持久化写)与续跑腿的写回({@link DriveResumeArgs.resumeCtx} 的唯一写点,CAS 赢点盖本进程的版本 —— 接下来写下一张停车行的就是它)。
212
211
  * 读者:`checkpoint.unsupported_version` 的 409 体 —— 原样带出 `writerVersion`,与本引擎的 `readerVersion` 并列(运维据此知道该回哪一版去决);
213
- * 两键唯一铸法 `checkpoint-ctx.ts` `buildCheckpointVersionKeys`,本码每个铸点同调。它是**上下文**,不是拒因:成因与码都不读它。
214
- * 缺席 = 本版之前写下的行(不回填):那一码的体不带两键,如实不猜。不是一次性迁移 —— 这一格是常设的事实位,没有删除条件。
212
+ * 两键唯一铸法 `checkpoint-ctx.ts` `buildCheckpointVersionKeys`,本码每个铸点同调。
213
+ * 另一个读者(唯一的拒因读者):存体重放判据的 allow 读法问(`checkpoint-ctx.ts` 顶注)—— 体里带非空 `settings.permissions.allow` 而本格证不出 ≥ 7.117.0 ⇒ 拒。
214
+ * 所以写点守一条律:盖本格的写只在本进程按当前读法读过这份体之后(新提交;或已过存体重放判据的续跑写回),在判据之前盖 = 把旧行洗成新行。
215
+ * 缺席 = 7.124.0 之前写下的行(不回填):那一码的体不带两键,如实不猜;allow 读法问把它读作「证不出」。不是一次性迁移 —— 这一格是常设的事实位,没有删除条件。
215
216
  */
216
217
  writerVersion?: string;
217
218
  /**
@@ -4,6 +4,7 @@ import { handleDiagnostics, DIAGNOSTICS_ROUTES } from "./routes/diagnostics.js";
4
4
  import { handleAdoption, ADOPTION_ROUTES } from "./routes/adoption.js";
5
5
  import { handleRetention, RETENTION_ROUTES } from "./routes/retention-ops.js";
6
6
  import { handleAdminDrain, ADMIN_DRAIN_ROUTES } from "./routes/admin-drain.js";
7
+ import { handleEngine, ENGINE_ROUTES } from "./routes/engine.js";
7
8
  import { handleDevices, DEVICES_ROUTES } from "./routes/devices.js";
8
9
  import { handleAdminConfigRefresh, ADMIN_CONFIG_REFRESH_ROUTES } from "./routes/admin-config-refresh.js";
9
10
  import { handleConfigCatalog, CONFIG_CATALOG_ROUTES } from "./routes/config-catalog.js";
@@ -65,6 +66,7 @@ export const ROUTES = [
65
66
  row({ handler: handleAdoption, decls: ADOPTION_ROUTES, position: "post-credential", label: "adoption" }),
66
67
  row({ handler: handleRetention, decls: RETENTION_ROUTES, position: "post-credential", label: "retention" }),
67
68
  row({ handler: handleAdminDrain, decls: ADMIN_DRAIN_ROUTES, position: "post-credential", label: "admin-drain" }),
69
+ row({ handler: handleEngine, decls: ENGINE_ROUTES, position: "post-credential", label: "engine" }),
68
70
  row({ handler: handleDevices, decls: DEVICES_ROUTES, position: "post-credential", label: "devices" }),
69
71
  row({ handler: handleAdminConfigRefresh, decls: ADMIN_CONFIG_REFRESH_ROUTES, position: "post-credential", label: "admin-config-refresh" }),
70
72
  row({ handler: handleConfigCatalog, decls: CONFIG_CATALOG_ROUTES, position: "post-credential", label: "config-catalog" }),
@@ -18,6 +18,11 @@
18
18
  * 一次落库,此后原样回放)。并发同参 POST 由 `adoption_log.from_principal` 的 UNIQUE 仲裁:恰一行落库,
19
19
  * 两条连接拿到同一个 adoptionId、同一份回执。
20
20
  *
21
+ * ── 第二条车道:源主体的批量导入 `POST /v1/adoption/:id/sessions` ─────────────────────────────────────────────
22
+ * 不是 operator 面:调用者是那条收编行的**源主体本人**(逐字;operator 不能代),把本地会话导进目的主体名下。门序 = 身份(401)→ 设施(501)→
23
+ * 行(404 `not_found.adoption`)→ 源主体(403 `auth.adoption_source_only`,不降级成 404:无行与不匹配同形是一种查不出根因的二义)→ 收编态
24
+ * (409 `adoption.not_adopted`)→ 体形(400)→ 条数帽(400)→ 逐元素(200 + 结局数组)。判断全在 `adoption/import.ts`,本模块只下发。
25
+ *
21
26
  * 分层:本模块不值 import `server.ts`(那条边闭合运行时装载环),只 `import type`。
22
27
  */
23
28
  import type { IncomingMessage, ServerResponse } from "node:http";
@@ -33,6 +38,12 @@ export declare const ADOPTION_ROUTES: readonly [{
33
38
  readonly pattern: RegExp;
34
39
  readonly label: "/v1/adoption/:id";
35
40
  readonly methods: readonly ["GET"];
41
+ }, {
42
+ readonly id: "adoption-sessions-import";
43
+ readonly pattern: RegExp;
44
+ readonly label: "/v1/adoption/:id/sessions";
45
+ readonly credentialGated: readonly ["POST"];
46
+ readonly methods: readonly ["POST"];
36
47
  }];
37
48
  /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
38
49
  export type AdoptionRouteId = RouteIdsOf<typeof ADOPTION_ROUTES>;