@sema-agent/server 7.132.0 → 7.133.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 (402) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +174 -55
  5. package/deploy/sema-up/README.md +19 -1
  6. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
  7. package/deploy/sema-up/chart/templates/server.yaml +5 -2
  8. package/deploy/sema-up/stack/compose.yaml +3 -0
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  10. package/dist/adoption/plan.d.ts +3 -2
  11. package/dist/adoption/plan.js +7 -5
  12. package/dist/adoption/runner.js +3 -10
  13. package/dist/adoption/sql.d.ts +3 -2
  14. package/dist/adoption/sql.js +3 -5
  15. package/dist/adoption/wire.d.ts +4 -1
  16. package/dist/adoption/wire.js +1 -0
  17. package/dist/approval-ask-audit-store.d.ts +31 -13
  18. package/dist/approval-ask-audit-store.js +272 -74
  19. package/dist/approval-ask-machine.d.ts +15 -0
  20. package/dist/approval-ask-machine.js +6 -0
  21. package/dist/approval-ask-retention.d.ts +136 -0
  22. package/dist/approval-ask-retention.js +102 -0
  23. package/dist/approval-card.d.ts +79 -19
  24. package/dist/approval-card.js +44 -13
  25. package/dist/approval-deny-reasons.d.ts +1 -1
  26. package/dist/approval.d.ts +9 -0
  27. package/dist/approval.js +24 -3
  28. package/dist/audit.d.ts +27 -10
  29. package/dist/audit.js +12 -16
  30. package/dist/bake-runner/main.js +3 -2
  31. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  32. package/dist/boot/execution-env.js +9 -7
  33. package/dist/boot/execution-lane.js +11 -0
  34. package/dist/boot/leader.js +2 -0
  35. package/dist/boot/memory-boundary.js +2 -2
  36. package/dist/boot/memory-consolidation.d.ts +5 -4
  37. package/dist/boot/memory-consolidation.js +7 -6
  38. package/dist/boot/org-delivery.d.ts +28 -0
  39. package/dist/boot/org-delivery.js +20 -0
  40. package/dist/boot/reapers.d.ts +3 -0
  41. package/dist/boot/reapers.js +21 -1
  42. package/dist/boot/resolve-spec.d.ts +3 -0
  43. package/dist/boot/resolve-spec.js +60 -81
  44. package/dist/boot/runner-deps.js +1 -0
  45. package/dist/boot/session-faces.d.ts +1 -0
  46. package/dist/boot/session-faces.js +4 -2
  47. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  48. package/dist/boot/stage-06-runners.d.ts +2 -0
  49. package/dist/boot/stage-06-runners.js +6 -3
  50. package/dist/boot/stage-07-capability-layer.d.ts +2 -0
  51. package/dist/boot/stage-07-capability-layer.js +24 -3
  52. package/dist/boot/stage-08-reapers.d.ts +2 -0
  53. package/dist/boot/stage-08-reapers.js +2 -2
  54. package/dist/boot/stage-09-leader.d.ts +3 -0
  55. package/dist/boot/stage-10-http-server.d.ts +3 -0
  56. package/dist/boot/stage-10-http-server.js +5 -6
  57. package/dist/boot/stores.js +2 -2
  58. package/dist/boot/web-admin.d.ts +4 -0
  59. package/dist/boot/web-admin.js +151 -63
  60. package/dist/boot/workflow-orchestration.d.ts +2 -0
  61. package/dist/boot/workflow-orchestration.js +2 -1
  62. package/dist/brain.js +8 -3
  63. package/dist/breaker-key.d.ts +6 -0
  64. package/dist/breaker-key.js +29 -0
  65. package/dist/caller-text-echo.d.ts +9 -6
  66. package/dist/caller-text-echo.js +8 -11
  67. package/dist/capabilities/center-plugins.d.ts +7 -0
  68. package/dist/capabilities/center-plugins.js +1 -1
  69. package/dist/capabilities/composite-members.d.ts +1 -1
  70. package/dist/capabilities/composite-members.js +1 -1
  71. package/dist/capabilities/execution-lane.js +2 -2
  72. package/dist/capabilities/hands-lane.d.ts +21 -0
  73. package/dist/capabilities/hands-lane.js +10 -0
  74. package/dist/capabilities/mcp-probe.d.ts +36 -4
  75. package/dist/capabilities/mcp-probe.js +27 -4
  76. package/dist/capabilities/scenarios.d.ts +63 -7
  77. package/dist/capabilities/scenarios.js +39 -7
  78. package/dist/capability-facts.d.ts +34 -4
  79. package/dist/capability-facts.js +28 -2
  80. package/dist/center-config-tier.d.ts +69 -0
  81. package/dist/center-config-tier.js +42 -0
  82. package/dist/checkpoint-identity.d.ts +16 -0
  83. package/dist/checkpoint-identity.js +19 -0
  84. package/dist/config-catalog.d.ts +17 -10
  85. package/dist/config-catalog.js +43 -28
  86. package/dist/config-d-migration.d.ts +3 -1
  87. package/dist/config-d-migration.js +2 -2
  88. package/dist/config-dir-trust.d.ts +114 -0
  89. package/dist/config-dir-trust.js +388 -0
  90. package/dist/config-invariants.d.ts +18 -5
  91. package/dist/config-invariants.js +35 -3
  92. package/dist/config-provider.d.ts +6 -1
  93. package/dist/config-provider.js +6 -2
  94. package/dist/config-types.d.ts +71 -14
  95. package/dist/config-types.js +8 -0
  96. package/dist/config.d.ts +13 -0
  97. package/dist/config.js +121 -59
  98. package/dist/delegated-session-owner.d.ts +185 -0
  99. package/dist/delegated-session-owner.js +119 -0
  100. package/dist/device-protocol.d.ts +5 -3
  101. package/dist/device-protocol.js +1 -1
  102. package/dist/device-ws-hub.js +21 -2
  103. package/dist/device-ws-protocol.d.ts +70 -6
  104. package/dist/device-ws-protocol.js +50 -23
  105. package/dist/elicitation.js +3 -2
  106. package/dist/entry-problems.d.ts +34 -0
  107. package/dist/entry-problems.js +14 -0
  108. package/dist/execution-lane-caps.d.ts +49 -10
  109. package/dist/execution-lane-caps.js +43 -4
  110. package/dist/field-conflict.d.ts +43 -0
  111. package/dist/field-conflict.js +20 -0
  112. package/dist/fleet/fleet-bus.d.ts +80 -6
  113. package/dist/fleet/fleet-bus.js +41 -9
  114. package/dist/hooks/classic-stations.d.ts +10 -5
  115. package/dist/hooks/classic-stations.js +4 -4
  116. package/dist/hooks/command-spawn.d.ts +57 -0
  117. package/dist/hooks/command-spawn.js +139 -0
  118. package/dist/hooks/hook-llm.js +2 -2
  119. package/dist/hooks/hook-runner.d.ts +127 -25
  120. package/dist/hooks/hook-runner.js +668 -287
  121. package/dist/hooks/hook-source.d.ts +44 -0
  122. package/dist/hooks/hook-source.js +40 -0
  123. package/dist/http/active-run-conflict.d.ts +5 -13
  124. package/dist/http/active-run-conflict.js +17 -27
  125. package/dist/http/admission.js +103 -60
  126. package/dist/http/approval-sweeps.js +55 -18
  127. package/dist/http/caller-trust.d.ts +77 -0
  128. package/dist/http/caller-trust.js +143 -0
  129. package/dist/http/checkpoint-ctx.d.ts +16 -16
  130. package/dist/http/checkpoint-ctx.js +13 -10
  131. package/dist/http/claimed-run.d.ts +4 -3
  132. package/dist/http/device-placement.d.ts +2 -4
  133. package/dist/http/device-placement.js +2 -1
  134. package/dist/http/dispatch.js +10 -4
  135. package/dist/http/durable-run-leg.d.ts +4 -32
  136. package/dist/http/durable-run-leg.js +34 -10
  137. package/dist/http/owner-access.d.ts +56 -0
  138. package/dist/http/owner-access.js +42 -0
  139. package/dist/http/principal-gate.d.ts +48 -23
  140. package/dist/http/principal-gate.js +39 -12
  141. package/dist/http/readiness.d.ts +11 -5
  142. package/dist/http/readiness.js +10 -3
  143. package/dist/http/request-guards.d.ts +16 -23
  144. package/dist/http/request-guards.js +30 -15
  145. package/dist/http/resume-ctx-read.d.ts +2 -5
  146. package/dist/http/resume-ctx-read.js +7 -4
  147. package/dist/http/resume-legs.d.ts +3 -4
  148. package/dist/http/resume-legs.js +126 -69
  149. package/dist/http/retired-service-deps.d.ts +43 -0
  150. package/dist/http/retired-service-deps.js +18 -0
  151. package/dist/http/route-ctx.d.ts +44 -28
  152. package/dist/http/route-ctx.js +4 -0
  153. package/dist/http/route-table.js +2 -0
  154. package/dist/http/routes/a2a-serve.js +3 -4
  155. package/dist/http/routes/admin-config-refresh.js +1 -1
  156. package/dist/http/routes/admin-drain.js +1 -1
  157. package/dist/http/routes/admin-memory-optout.js +5 -1
  158. package/dist/http/routes/adoption.js +2 -9
  159. package/dist/http/routes/approvals-assistant.js +49 -48
  160. package/dist/http/routes/background.js +3 -2
  161. package/dist/http/routes/capabilities.js +20 -4
  162. package/dist/http/routes/config-catalog.d.ts +3 -3
  163. package/dist/http/routes/config-catalog.js +1 -1
  164. package/dist/http/routes/devices.js +3 -17
  165. package/dist/http/routes/diagnostics.js +5 -1
  166. package/dist/http/routes/images.js +6 -7
  167. package/dist/http/routes/leader.js +1 -8
  168. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  169. package/dist/http/routes/mcp-reconnect.js +3 -2
  170. package/dist/http/routes/memory-bundle.js +1 -1
  171. package/dist/http/routes/memory-compliance.js +1 -1
  172. package/dist/http/routes/memory-consolidation.js +1 -1
  173. package/dist/http/routes/memory-origin.js +1 -1
  174. package/dist/http/routes/memory-policy.d.ts +1 -1
  175. package/dist/http/routes/memory-policy.js +3 -2
  176. package/dist/http/routes/notify-wake.js +8 -21
  177. package/dist/http/routes/observability.js +2 -2
  178. package/dist/http/routes/principal-inbox.d.ts +39 -0
  179. package/dist/http/routes/principal-inbox.js +76 -0
  180. package/dist/http/routes/retention-ops.js +2 -9
  181. package/dist/http/routes/rules.js +5 -18
  182. package/dist/http/routes/runs.d.ts +5 -0
  183. package/dist/http/routes/runs.js +33 -106
  184. package/dist/http/routes/session-sync.js +15 -28
  185. package/dist/http/routes/sessions.js +30 -40
  186. package/dist/http/routes/side-query.js +6 -4
  187. package/dist/http/routes/tasks.js +50 -14
  188. package/dist/http/routes/workflows.d.ts +351 -0
  189. package/dist/http/routes/workflows.js +153 -119
  190. package/dist/http/send.d.ts +24 -3
  191. package/dist/http/send.js +14 -2
  192. package/dist/http/server.d.ts +17 -18
  193. package/dist/http/server.js +15 -13
  194. package/dist/http/stored-body-replay.js +17 -4
  195. package/dist/http/submit-ctx.js +4 -3
  196. package/dist/http/submit-replay.d.ts +4 -3
  197. package/dist/http/submit-replay.js +5 -4
  198. package/dist/http/wire-types.d.ts +10 -7
  199. package/dist/index.d.ts +1 -1
  200. package/dist/index.js +0 -1
  201. package/dist/lane-home-probe.d.ts +78 -9
  202. package/dist/lane-home-probe.js +78 -12
  203. package/dist/leader/endpoint.d.ts +4 -1
  204. package/dist/leader/endpoint.js +11 -10
  205. package/dist/limit-refusal.d.ts +17 -5
  206. package/dist/limit-refusal.js +4 -1
  207. package/dist/mcp-assertion-header.d.ts +31 -10
  208. package/dist/mcp-assertion-header.js +10 -3
  209. package/dist/mcp-tool-faces.js +1 -3
  210. package/dist/memory-project-layer.d.ts +37 -0
  211. package/dist/memory-project-layer.js +46 -0
  212. package/dist/memory-scope.d.ts +23 -16
  213. package/dist/memory-scope.js +24 -8
  214. package/dist/memory-sync-client.d.ts +27 -13
  215. package/dist/memory-sync-client.js +107 -18
  216. package/dist/model-entry-refusal.d.ts +9 -1
  217. package/dist/model-entry-refusal.js +6 -1
  218. package/dist/model-plane-face.d.ts +51 -0
  219. package/dist/model-plane-face.js +24 -0
  220. package/dist/model-provider.d.ts +5 -0
  221. package/dist/model-provider.js +4 -2
  222. package/dist/model-route-endpoint.d.ts +8 -11
  223. package/dist/model-route-endpoint.js +4 -7
  224. package/dist/model-route-env.d.ts +22 -0
  225. package/dist/model-route-env.js +2 -0
  226. package/dist/model-route-gap.d.ts +60 -30
  227. package/dist/model-route-gap.js +21 -15
  228. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  229. package/dist/observability/corrupt-read-seat.js +9 -0
  230. package/dist/observability/err-text.d.ts +6 -0
  231. package/dist/observability/err-text.js +18 -4
  232. package/dist/observability/fail-open.d.ts +35 -9
  233. package/dist/observability/fail-open.js +35 -9
  234. package/dist/observability/metrics.js +1 -0
  235. package/dist/observability/prompt-manifest.d.ts +3 -17
  236. package/dist/observability/prompt-manifest.js +45 -42
  237. package/dist/observability/tool-trace.js +9 -9
  238. package/dist/operator-ask.d.ts +10 -0
  239. package/dist/operator-ask.js +3 -0
  240. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  241. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  242. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  243. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  244. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  245. package/dist/orchestration/workflow-notify-journal.js +27 -34
  246. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  247. package/dist/orchestration/workflow-park-decision.js +118 -0
  248. package/dist/org-delivery-gate.d.ts +81 -0
  249. package/dist/org-delivery-gate.js +59 -0
  250. package/dist/panel-tool-names.d.ts +23 -0
  251. package/dist/panel-tool-names.js +8 -0
  252. package/dist/parked-decide.d.ts +39 -6
  253. package/dist/parked-decide.js +98 -51
  254. package/dist/parked-revive-surface.d.ts +9 -2
  255. package/dist/parked-revive-surface.js +11 -8
  256. package/dist/permission-rule-list.d.ts +1 -1
  257. package/dist/permission-rule-list.js +2 -2
  258. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  259. package/dist/plugins/approval-ask-store-file.js +21 -0
  260. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  261. package/dist/plugins/approval-ask-store-memory.js +34 -2
  262. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  263. package/dist/plugins/approval-ask-store-sql.js +37 -1
  264. package/dist/plugins/blob-backend.d.ts +38 -25
  265. package/dist/plugins/blob-backend.js +29 -19
  266. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  267. package/dist/plugins/breaker-state-sql.js +4 -4
  268. package/dist/plugins/caching-session-store.d.ts +3 -0
  269. package/dist/plugins/caching-session-store.js +3 -0
  270. package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
  271. package/dist/plugins/checkpoint-store-sql.js +20 -2
  272. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  273. package/dist/plugins/file-history-store-sql.js +3 -1
  274. package/dist/plugins/file-run-store.d.ts +5 -13
  275. package/dist/plugins/file-run-store.js +9 -0
  276. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  277. package/dist/plugins/fork-routing-session-store.js +12 -1
  278. package/dist/plugins/host-platform.d.ts +2 -0
  279. package/dist/plugins/host-platform.js +2 -1
  280. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  281. package/dist/plugins/local-checkpoint-store.js +6 -1
  282. package/dist/plugins/local-session-store.d.ts +1 -1
  283. package/dist/plugins/local-session-window.d.ts +39 -0
  284. package/dist/plugins/local-session-window.js +54 -0
  285. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  286. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  287. package/dist/plugins/mailbox-store-sql.js +77 -52
  288. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  289. package/dist/plugins/memory-run-store.d.ts +5 -13
  290. package/dist/plugins/memory-run-store.js +8 -0
  291. package/dist/plugins/one-time-migrations.d.ts +110 -5
  292. package/dist/plugins/one-time-migrations.js +320 -8
  293. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  294. package/dist/plugins/org-delivery-quota-store.js +228 -0
  295. package/dist/plugins/pg-pool.js +39 -7
  296. package/dist/plugins/pg-session-storage.d.ts +17 -4
  297. package/dist/plugins/pg-session-storage.js +29 -6
  298. package/dist/plugins/remote-env-adb.d.ts +6 -12
  299. package/dist/plugins/remote-env-adb.js +12 -2
  300. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  301. package/dist/plugins/remote-env-k8s.js +23 -5
  302. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  303. package/dist/plugins/remote-env-local-docker.js +12 -9
  304. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  305. package/dist/plugins/remote-env-ssh.js +17 -11
  306. package/dist/plugins/run-store-sql.d.ts +16 -14
  307. package/dist/plugins/run-store-sql.js +22 -2
  308. package/dist/plugins/schema-indexes.js +2 -0
  309. package/dist/plugins/send-user-file.js +2 -2
  310. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  311. package/dist/plugins/session-epoch-anchor.js +2 -3
  312. package/dist/plugins/session-store.d.ts +18 -8
  313. package/dist/plugins/session-store.js +15 -4
  314. package/dist/plugins/sql-driver.d.ts +74 -11
  315. package/dist/plugins/sql-driver.js +210 -12
  316. package/dist/plugins/sql-errors.d.ts +23 -0
  317. package/dist/plugins/sql-errors.js +21 -0
  318. package/dist/plugins/store-backend.d.ts +36 -1
  319. package/dist/plugins/store-backend.js +40 -14
  320. package/dist/plugins/store-contracts.d.ts +42 -0
  321. package/dist/plugins/store-contracts.js +1 -0
  322. package/dist/plugins/tidb-pool.d.ts +6 -1
  323. package/dist/plugins/tidb-pool.js +44 -10
  324. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  325. package/dist/plugins/tidb-session-storage.js +2 -2
  326. package/dist/plugins/tidb-session-store.d.ts +24 -4
  327. package/dist/plugins/tidb-session-store.js +28 -4
  328. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  329. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  330. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  331. package/dist/plugins/workflow-run-store-sql.js +50 -9
  332. package/dist/posture-source.d.ts +18 -10
  333. package/dist/posture-source.js +4 -1
  334. package/dist/project-identity.d.ts +45 -6
  335. package/dist/project-identity.js +162 -8
  336. package/dist/project-layer-key.d.ts +28 -0
  337. package/dist/project-layer-key.js +29 -0
  338. package/dist/question.js +3 -2
  339. package/dist/read-deny-entry.d.ts +25 -0
  340. package/dist/read-deny-entry.js +12 -0
  341. package/dist/read-face-posture.d.ts +37 -11
  342. package/dist/read-face-posture.js +32 -3
  343. package/dist/request-key-closure.d.ts +7 -0
  344. package/dist/request-key-closure.js +9 -1
  345. package/dist/request-value-closure.d.ts +5 -0
  346. package/dist/request-value-closure.js +11 -3
  347. package/dist/rule-entry-list.d.ts +26 -2
  348. package/dist/rule-entry-list.js +72 -12
  349. package/dist/rules-consent.js +3 -4
  350. package/dist/run-acceptance.d.ts +19 -3
  351. package/dist/run-acceptance.js +13 -1
  352. package/dist/run-local.js +36 -6
  353. package/dist/runs.d.ts +10 -9
  354. package/dist/runs.js +21 -3
  355. package/dist/runtime-caps-resolver.d.ts +16 -6
  356. package/dist/runtime-caps-resolver.js +18 -5
  357. package/dist/sandbox-pkg-source.d.ts +19 -1
  358. package/dist/sandbox-pkg-source.js +29 -7
  359. package/dist/security.d.ts +87 -9
  360. package/dist/security.js +52 -9
  361. package/dist/session-error-wire.d.ts +19 -2
  362. package/dist/session-error-wire.js +5 -1
  363. package/dist/session-titled-frame.d.ts +33 -0
  364. package/dist/session-titled-frame.js +4 -0
  365. package/dist/session-titler.d.ts +9 -1
  366. package/dist/session-titler.js +15 -4
  367. package/dist/spec-fields.js +21 -2
  368. package/dist/sql-engine-posture.d.ts +3 -1
  369. package/dist/task-cwd.d.ts +1 -1
  370. package/dist/task-mcp.d.ts +3 -3
  371. package/dist/task-mcp.js +7 -66
  372. package/dist/task-settings.d.ts +89 -88
  373. package/dist/task-settings.js +71 -63
  374. package/dist/text-bidi.d.ts +0 -4
  375. package/dist/text-bidi.js +1 -3
  376. package/dist/tighten-only-seats.d.ts +150 -0
  377. package/dist/tighten-only-seats.js +196 -0
  378. package/dist/tool-approval.d.ts +303 -87
  379. package/dist/tool-approval.js +623 -239
  380. package/dist/trace/core-keyset-guard.d.ts +104 -22
  381. package/dist/trace/exit-seat.d.ts +28 -0
  382. package/dist/trace/exit-seat.js +76 -0
  383. package/dist/trace/ledger-events.d.ts +3 -2
  384. package/dist/trace/project.d.ts +80 -12
  385. package/dist/trace/project.js +41 -28
  386. package/dist/trace/projection-table.d.ts +100 -0
  387. package/dist/trace/projection-table.js +130 -0
  388. package/dist/trace/redact.d.ts +54 -0
  389. package/dist/trace/redact.js +778 -152
  390. package/dist/trace/self-minted-wire.d.ts +345 -1
  391. package/dist/trace/self-minted-wire.js +117 -1
  392. package/dist/trace/wire-projection-faces.d.ts +298 -2
  393. package/dist/trace/wire-projection-faces.js +154 -13
  394. package/dist/url-refusal.js +2 -0
  395. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  396. package/dist/web-admin-client/apply-effective.js +56 -5
  397. package/dist/web-admin-client/facade.d.ts +1 -1
  398. package/dist/web-admin-client/facade.js +1 -1
  399. package/dist/web-admin-client/read-face.js +5 -9
  400. package/dist/web-admin-client/skills-mcp.js +4 -5
  401. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  402. package/package.json +7 -3
@@ -4,32 +4,98 @@ const SHOWN_MAX = 256;
4
4
  const shown = (raw) => JSON.stringify(raw.length > SHOWN_MAX ? `${raw.slice(0, SHOWN_MAX)}…` : raw);
5
5
  const comparable = (p) => (p.length > 1 ? p.replace(/\/+$/, "") : p);
6
6
  function markedHome(stdout) {
7
- const lines = stdout.split("\n").filter((l) => l.startsWith(LANE_HOME_PROBE_MARKER));
8
- const last = lines[lines.length - 1];
9
- return last === undefined ? undefined : last.slice(LANE_HOME_PROBE_MARKER.length).replace(/\r$/, "");
7
+ const lines = stdout.split("\n").map((l) => l.replace(/\r$/, ""));
8
+ const marked = lines.flatMap((l, i) => (l.startsWith(LANE_HOME_PROBE_MARKER) ? [i] : []));
9
+ if (marked.length === 0)
10
+ return { why: "the probe printed no home line" };
11
+ const at = marked[0];
12
+ if (marked.length > 1 || lines.slice(at + 1).some((l) => l !== ""))
13
+ return { why: "the probe output holds more than one home line or text after it, so it cannot be read as one path" };
14
+ return { home: lines[at].slice(LANE_HOME_PROBE_MARKER.length) };
15
+ }
16
+ function readLaneHome(probe) {
17
+ if (!probe.ok)
18
+ return { kind: "unread", reason: probe.reason };
19
+ const marked = markedHome(probe.stdout);
20
+ if ("why" in marked)
21
+ return { kind: "unread", reason: marked.why };
22
+ return marked.home.startsWith("/") ? { kind: "home", home: marked.home } : { kind: "not_absolute", value: marked.home };
10
23
  }
11
24
  export function judgeLaneHome(lane, declared, probe) {
12
25
  const head = `REMOTE_EXEC_HOME_DIR=${shown(declared)} is the home "~/" permission rules and the engine's home-anchored read-deny rows are placed under on the ${lane} execution lane`;
13
- const measured = probe.ok ? markedHome(probe.stdout) : undefined;
14
- if (measured === undefined) {
15
- const reason = probe.ok ? "the probe printed no home line" : probe.reason;
26
+ const reading = readLaneHome(probe);
27
+ if (reading.kind === "unread") {
16
28
  return {
17
- refusal: `${head}, but the home of this execution environment could not be read (${reason}) — the declaration cannot be confirmed, so this environment is not started. Fix the lane, or unset REMOTE_EXEC_HOME_DIR (home-relative rules are then refused at submission instead).`,
29
+ refusal: `${head}, but the home of this execution environment could not be read (${reading.reason}) — the declaration cannot be confirmed, so this environment is not started. Fix the lane, or unset REMOTE_EXEC_HOME_DIR (home-relative rules are then refused at submission instead).`,
18
30
  answered: false,
19
31
  };
20
32
  }
21
- if (!measured.startsWith("/")) {
33
+ if (reading.kind === "not_absolute") {
22
34
  return {
23
- refusal: `${head}, but this execution environment reports HOME=${shown(measured)}, not an absolute path — the declaration cannot be confirmed, so this environment is not started. Give the environment's user a home (HOME), or unset REMOTE_EXEC_HOME_DIR.`,
35
+ refusal: `${head}, but this execution environment reports HOME=${shown(reading.value)}, not an absolute path — the declaration cannot be confirmed, so this environment is not started. Give the environment's user a home (HOME), or unset REMOTE_EXEC_HOME_DIR.`,
24
36
  answered: true,
25
37
  };
26
38
  }
27
- if (comparable(measured) === comparable(declared))
39
+ if (comparable(reading.home) === comparable(declared))
28
40
  return undefined;
29
41
  return {
30
- refusal: `${head}, but its commands run with HOME=${shown(measured)} — rules would guard ${shown(declared)} while ${shown(measured)} stays readable and writable, ` +
31
- `so this environment is not started. Set REMOTE_EXEC_HOME_DIR to ${shown(measured)}, or run the environment as the user whose home is ${shown(declared)}.`,
42
+ refusal: `${head}, but its commands run with HOME=${shown(reading.home)} — rules would guard ${shown(declared)} while ${shown(reading.home)} stays readable and writable, ` +
43
+ `so this environment is not started. Set REMOTE_EXEC_HOME_DIR to ${shown(reading.home)}, or run the environment as the user whose home is ${shown(declared)}.`,
32
44
  answered: true,
33
45
  };
34
46
  }
47
+ export const LANE_HOME_UNDECLARED_EVENT = "execution_lane_home_undeclared";
48
+ const HOME_ABSENT_COST = "the engine cannot place its home-anchored read-deny rows, so every contents-disclosing recursive read (grep -r, rg, git grep, tar c, …) asks a person on the full shell — " +
49
+ "no allow rule, mode or bypass releases that question — and is refused on the read-only face, so unattended tasks cannot search code; \"~/\" permission rules are refused at submission.";
50
+ export function buildHomeAbsentNote(lane, cause, remedy) {
51
+ return `the ${lane} lane declares no home for its execution environment (${cause}): ${HOME_ABSENT_COST} ${remedy}`;
52
+ }
53
+ const DECLARE_ARG = "REMOTE_EXEC_HOME_DIR=<the absolute path the environment's $HOME reads>";
54
+ export const DECLARE_HOME_REMEDY = `Declare the home: ${DECLARE_ARG}`;
55
+ export class LaneHomeSeat {
56
+ measured;
57
+ lane;
58
+ declared;
59
+ verifiesDeclared;
60
+ onAbsent;
61
+ constructor(options) {
62
+ this.lane = options.lane;
63
+ this.declared = options.declared;
64
+ this.verifiesDeclared = options.verifiesDeclared;
65
+ this.onAbsent = options.onAbsent;
66
+ }
67
+ get home() {
68
+ return this.declared ?? this.measured;
69
+ }
70
+ get asks() {
71
+ return this.declared === undefined ? this.measured === undefined : this.verifiesDeclared;
72
+ }
73
+ settle(probe) {
74
+ if (this.declared !== undefined)
75
+ return this.verifiesDeclared ? judgeLaneHome(this.lane, this.declared, probe) : undefined;
76
+ const reading = readLaneHome(probe);
77
+ if (reading.kind === "home") {
78
+ this.measured = reading.home;
79
+ return undefined;
80
+ }
81
+ const because = reading.kind === "unread" ? `could not be read (${reading.reason})` : `reads ${shown(reading.value)}, not an absolute path`;
82
+ const remedy = this.verifiesDeclared
83
+ ? "Give the environment's user an absolute HOME (a declared REMOTE_EXEC_HOME_DIR is checked against it, so declaring cannot stand in for it)."
84
+ : `Give the environment's user an absolute HOME, or declare it: ${DECLARE_ARG}.`;
85
+ this.onAbsent?.({ provider: this.lane, note: buildHomeAbsentNote(this.lane, `REMOTE_EXEC_HOME_DIR is unset and the environment's own $HOME ${because}`, remedy) });
86
+ return undefined;
87
+ }
88
+ }
89
+ const ABSENCE_SEEN_MAX = 64;
90
+ export function createLaneHomeAbsenceReporter(warn) {
91
+ const seen = new Set();
92
+ return (absence) => {
93
+ const key = `${absence.provider}\u0000${absence.note}`;
94
+ if (seen.has(key))
95
+ return;
96
+ if (seen.size < ABSENCE_SEEN_MAX)
97
+ seen.add(key);
98
+ warn(LANE_HOME_UNDECLARED_EVENT, { provider: absence.provider, note: absence.note });
99
+ };
100
+ }
35
101
  //# sourceMappingURL=lane-home-probe.js.map
@@ -59,7 +59,7 @@ export interface LeaderEndpoint {
59
59
  * With a store in place this Map holds only the runs THIS replica is driving. Exposed for tests. */
60
60
  runs: Map<string, LeaderRun>;
61
61
  }
62
- export declare function createLeaderEndpoint(runLeader: (body: LeaderRequestBody) => Promise<LeaderResult>, opts?: {
62
+ export declare function createLeaderEndpoint(runLeader: (body: LeaderRequestBody) => Promise<LeaderResult>, opts: {
63
63
  logger?: {
64
64
  info?: (m: string, x?: Record<string, unknown>) => void;
65
65
  error?: (m: string, x?: Record<string, unknown>) => void;
@@ -73,5 +73,8 @@ export declare function createLeaderEndpoint(runLeader: (body: LeaderRequestBody
73
73
  requiredFields?: readonly string[];
74
74
  /** [ref] 车4 件1:durable 登记表。缺席 ⇒ 显式的单副本内存降级臂(见文件头注)。 */
75
75
  store?: LeaderRunStore;
76
+ /** S-487 RH-3:「这个调用方读得到这一行 leader run 吗」—— 属主存取器的判官形(`http/owner-access.ts` 的 `createLeaderRunVisibility`,boot 装一次)。
77
+ * 必填:本模块不自判属主(修前手抄 `owner == null || owner === requester`,缺 [ref] C8 窄互认臂而注称 mirrors runOwnerOk),读面与幂等重放同读它。 */
78
+ ownerVisible: (principal: string | null | undefined, owner: string | null | undefined) => boolean;
76
79
  }): LeaderEndpoint;
77
80
  //# sourceMappingURL=endpoint.d.ts.map
@@ -3,6 +3,7 @@ import { redactErrorMessage, redactHead, redactLeaderResult, redactLeaderResultW
3
3
  import { withPrincipal } from "../observability/principal-context.js";
4
4
  import {} from "../plugins/leader-run-store-sql.js";
5
5
  import { idemKeyOf } from "../http/idempotency.js";
6
+ import { refusalBody } from "../http/refusal-body.js";
6
7
  function statusForLeaderResult(result) {
7
8
  if (result.conflict)
8
9
  return "needs_human";
@@ -28,7 +29,7 @@ function idemShapeError(raw) {
28
29
  }
29
30
  return undefined;
30
31
  }
31
- export function createLeaderEndpoint(runLeader, opts = {}) {
32
+ export function createLeaderEndpoint(runLeader, opts) {
32
33
  const runs = new Map();
33
34
  const memIdem = new Map();
34
35
  function reap() {
@@ -126,19 +127,19 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
126
127
  function getBody(run) {
127
128
  return { status: 200, body: { id: run.id, status: run.status, ...redactedView(run) } };
128
129
  }
129
- const NOT_FOUND = { status: 404, body: { error: "leader run not found", errorCode: "not_found.leader_run" } };
130
+ const NOT_FOUND = { status: 404, body: refusalBody({ errorCode: "not_found.leader_run", message: "leader run not found" }) };
130
131
  async function handle(method, url, body, caller, idempotencyKey) {
131
132
  const requester = caller?.principal ?? null;
132
133
  const idemCaller = caller ?? { principal: null, source: null };
133
134
  if (method === "POST" && url === "/v1/leader") {
134
135
  const b = body;
135
136
  if (!b || typeof b.objective !== "string" || b.objective.trim() === "") {
136
- return { status: 400, body: { error: "missing 'objective' string", errorCode: "request.body_shape" } };
137
+ return { status: 400, body: refusalBody({ errorCode: "request.body_shape", message: "missing 'objective' string" }) };
137
138
  }
138
139
  if (opts.requiredFields) {
139
140
  const missing = opts.requiredFields.filter((f) => typeof b[f] !== "string" || b[f].trim() === "");
140
141
  if (missing.length > 0) {
141
- return { status: 400, body: { error: `missing required field(s): ${missing.join(", ")}`, errorCode: "request.body_shape" } };
142
+ return { status: 400, body: refusalBody({ errorCode: "request.body_shape", message: `missing required field(s): ${missing.join(", ")}` }) };
142
143
  }
143
144
  }
144
145
  const owner = requester;
@@ -146,15 +147,15 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
146
147
  if (idempotencyKey != null) {
147
148
  const bad = idemShapeError(idempotencyKey);
148
149
  if (bad)
149
- return { status: 400, body: { error: bad, errorCode: "request.body_shape" } };
150
+ return { status: 400, body: refusalBody({ errorCode: "request.body_shape", message: bad }) };
150
151
  scopedIdem = idemKeyOf(idempotencyKey, idemCaller, "leader");
151
152
  }
152
153
  reap();
153
154
  if (opts.store) {
154
155
  const { created, run } = await opts.store.createRun({ objective: b.objective, owner, idemKey: scopedIdem });
155
156
  if (!created) {
156
- if (run.owner != null && run.owner !== owner) {
157
- return { status: 409, body: { error: "idempotency key already used by another principal", errorCode: "conflict.leader_run_exists" } };
157
+ if (!opts.ownerVisible(requester, run.owner)) {
158
+ return { status: 409, body: refusalBody({ errorCode: "conflict.leader_run_exists", message: "idempotency key already used by another principal" }) };
158
159
  }
159
160
  return { status: 202, body: { leaderRunId: run.id, status: run.status } };
160
161
  }
@@ -175,13 +176,13 @@ export function createLeaderEndpoint(runLeader, opts = {}) {
175
176
  drive(id, b, owner);
176
177
  return { status: 202, body: { leaderRunId: id, status: "running" } };
177
178
  }
178
- const ownerOk = (owner) => owner == null || owner === requester;
179
+ const ownerOk = (owner) => opts.ownerVisible(requester, owner);
179
180
  if (method === "GET" && url === "/v1/leader") {
180
181
  if (idempotencyKey == null)
181
- return { status: 400, body: { error: "idempotencyKey is required: pass the raw Idempotency-Key the submission was sent with", errorCode: "request.query_invalid" } };
182
+ return { status: 400, body: refusalBody({ errorCode: "request.query_invalid", message: "idempotencyKey is required: pass the raw Idempotency-Key the submission was sent with" }) };
182
183
  const bad = idemShapeError(idempotencyKey);
183
184
  if (bad)
184
- return { status: 400, body: { error: bad, errorCode: "request.query_invalid" } };
185
+ return { status: 400, body: refusalBody({ errorCode: "request.query_invalid", message: bad }) };
185
186
  const key = idemKeyOf(idempotencyKey, idemCaller, "leader");
186
187
  if (opts.store) {
187
188
  const run = await opts.store.getByIdem(key);
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * 律:请求体上任何一处撞了帽,拒体带同一组机读位 ——
5
5
  * · `field` = 撞帽的那一处在请求体里的坐标(`skills[1].allowedTools`、`agents[0].denyTools[3]`、`settings.permissions.allow`);
6
- * · `reason` = 帽的量纲,闭集两词:`over_cap`(条数:数组的条目数)/ `too_long`(字符数:一段文字的长度);
7
- * · `count` = 送来的量(条数或字符数,量纲由 `reason` 定);
6
+ * · `reason` = 帽的量纲,闭集三词:`over_cap`(条数:数组的条目数)/ `too_long`(字符数:一段文字的长度)/ `too_large`(字节数:一份载荷的体积);
7
+ * · `count` = 送来的量(条数、字符数或字节数,量纲由 `reason` 定);
8
8
  * · `cap` = 帽。
9
- * 句子的头一句同一句:`<field> has <count> entries|characters — at most <cap> are accepted`;之后各面可以接自己的出路句(为什么拒而不截、怎么改)。
9
+ * 句子的头一句同一句:`<field> has <count> entries|characters|bytes — at most <cap> are accepted`;之后各面可以接自己的出路句(为什么拒而不截、怎么改)。
10
+ * 唯一的缺量形:读体读到帽就停 / 库只说「一行装不下」时量不出送来多少 ⇒ {@link limitFactsUnmeasured}(`count` 缺席,`cap` 只在帽已知时在;不编数)。
11
+ * 已有句子的面(上限早于本模块定下来的受理口各处)句子逐字不变、只加机读位 —— 用 {@link limitFacts} 铸,不改写句子。
10
12
  * 帽本身(数字)归各面的属主;本模块只管「撞了帽怎么说、怎么机读」。
11
13
  *
12
14
  * 量纲 → `reason` 是一张闭表({@link LIMIT_REASONS}):加一种量纲 ⇒ 这里加一行,`reason` 的词随之进闭集,读者穷举读。
@@ -15,10 +17,11 @@
15
17
  export declare const LIMIT_REASONS: {
16
18
  readonly entries: "over_cap";
17
19
  readonly characters: "too_long";
20
+ readonly bytes: "too_large";
18
21
  };
19
- /** 帽的量纲:条数 / 字符数(句子里就是这个词)。 */
22
+ /** 帽的量纲:条数 / 字符数 / 字节数(句子里就是这个词)。 */
20
23
  export type LimitMeasure = keyof typeof LIMIT_REASONS;
21
- /** 机读 `reason`:`over_cap`(条数)/ `too_long`(字符数)。 */
24
+ /** 机读 `reason`:`over_cap`(条数)/ `too_long`(字符数)/ `too_large`(字节数)。 */
22
25
  export type LimitReason = (typeof LIMIT_REASONS)[LimitMeasure];
23
26
  /** 一处撞帽的机读位(原样进拒体:`refusalBody` 的 facts / `HttpError.extra`;型别名而不是接口 —— 要能直接当 `Record<string, unknown>` 用)。 */
24
27
  export type LimitFacts = {
@@ -27,6 +30,13 @@ export type LimitFacts = {
27
30
  readonly count: number;
28
31
  readonly cap: number;
29
32
  };
33
+ /**
34
+ * 撞了帽、却**量不出**送来多少的机读位:读体读到帽就停(体没读完 —— 不为量一个数把 8 MiB 之外的字节再读进内存),或库只说「一行装不下」、不说多大。
35
+ * 同一组键的缺量形:`field` / `reason`(量纲定)恒在,`cap` 只在帽已知时在,`count` 恒缺席 —— 缺席就是「量不出」,不编数。
36
+ */
37
+ export type UnmeasuredLimitFacts = Pick<LimitFacts, "field" | "reason"> & {
38
+ readonly cap?: number;
39
+ };
30
40
  /** 一处撞帽的拒:句子 + 机读位。 */
31
41
  export interface LimitRefusal {
32
42
  readonly message: string;
@@ -36,6 +46,8 @@ export interface LimitRefusal {
36
46
  export declare function describeLimit(field: string, measure: LimitMeasure, count: number, cap: number): string;
37
47
  /** 机读位:`reason` 由量纲定。 */
38
48
  export declare function limitFacts(field: string, measure: LimitMeasure, count: number, cap: number): LimitFacts;
49
+ /** 缺量形的机读位({@link UnmeasuredLimitFacts}):`reason` 仍由量纲定;`cap` 只在帽已知时给。 */
50
+ export declare function limitFactsUnmeasured(field: string, measure: LimitMeasure, cap?: number): UnmeasuredLimitFacts;
39
51
  /**
40
52
  * 一处撞帽的拒:句子 = {@link describeLimit}(+ `tail`:该面的出路句,接在头一句之后),机读位 = {@link limitFacts}(`field` 同一个坐标)。
41
53
  * `sentenceField` 只在该面的句子沿用另一种点名写法时给(子代定义的句子写作 `agents[0]: agent.name`,机读位仍是体内坐标 `agents[0].name`)。
@@ -1,10 +1,13 @@
1
- export const LIMIT_REASONS = { entries: "over_cap", characters: "too_long" };
1
+ export const LIMIT_REASONS = { entries: "over_cap", characters: "too_long", bytes: "too_large" };
2
2
  export function describeLimit(field, measure, count, cap) {
3
3
  return `${field} has ${count} ${measure} — at most ${cap} are accepted`;
4
4
  }
5
5
  export function limitFacts(field, measure, count, cap) {
6
6
  return { field, reason: LIMIT_REASONS[measure], count, cap };
7
7
  }
8
+ export function limitFactsUnmeasured(field, measure, cap) {
9
+ return cap === undefined ? { field, reason: LIMIT_REASONS[measure] } : { field, reason: LIMIT_REASONS[measure], cap };
10
+ }
8
11
  export function limitRefusal(field, measure, count, cap, opts = {}) {
9
12
  const head = describeLimit(opts.sentenceField ?? field, measure, count, cap);
10
13
  return { message: opts.tail === undefined ? head : `${head}. ${opts.tail}`, facts: limitFacts(field, measure, count, cap) };
@@ -18,13 +18,16 @@
18
18
  * 所以读器(契约包的 `readMcpIdentityHeaders`,`normalizeServer` 的 http 传输读两个身份头用它)的处置是**降级到升级前的读法**:`principalHeader` 胜(照挂,与升级前逐字相同),`assertionHeader` 不生效;
19
19
  * 不静默 —— 真挂上的这类条目由 {@link noteMcpAssertionHeaderIgnored} 说一行 warn(部署声明面没有这一格:配置不是被重放的存量任务,契约包解析期那一刀直接丢这一条)。
20
20
  *
21
- * ## 发送归引擎座(本版只承载,不发送)
22
- * core 的 http 传输只有 `url` / `headers` / `principalHeader`,**没有**断言头座。所以本键承载到「铸 core 规格」为止:
23
- * 请求面在 {@link toCoreMcpServerSpec} 剥掉它,settings 面的 `resolveMcpServers` 不投它;两处都用同一只
24
- * {@link noteMcpAssertionHeaderNotSent} 说出一行 info(声明在、发送未接)—— 披露,不是兜底:server 照常挂载、不带断言头,
25
- * 要求断言的托管连接器会拒这些调用,运维据这一行知道原因在哪。
26
- *
27
- * ## 退役(编译期自动逼,零人工记忆)
21
+ * ## 发送归引擎座(本版只承载,不发送)—— 「发不发」只有一只开关
22
+ core 的 http 传输只有 `url` / `headers` / `principalHeader`,**没有**断言头座。所以本键承载到「铸 core 规格」为止:
23
+ 请求面在 {@link toCoreMcpServerSpec} 剥掉它,settings 面的 `resolveMcpServers` 不投它;两处都用同一只
24
+ {@link noteMcpAssertionHeaderNotSent} 说出一行 info(声明在、发送未接)—— 披露,不是兜底:server 照常挂载、不带断言头,
25
+ 要求断言的托管连接器会拒这些调用,运维据这一行知道原因在哪。
26
+ 「这台 server 发不发」是**一个事实**,只有一只开关 {@link MCP_ASSERTION_HEADER_SENT} 说它;读它的地方共五处,全经 {@link withholdsMcpAssertionHeader} 这一只谓词
27
+ (能力位 `GET /v1/capabilities` 的 `mcpAssertionHeaderSent` 直读开关本人):请求面剥键、settings 面不投、一行 info、探测口整条拒、能力位。所以「位为真」与「还在说发送未接」
28
+ 结构上不可能同时成立,消费方按位分支、位真时不再看到任何披露句;发送座到货的那天翻的就是这一行(与下面的退役门同批)。
29
+
30
+ ## 退役(编译期自动逼,零人工记忆)
28
31
  * 引擎的 http 传输一旦长出同名座,{@link _CoreHasNoAssertionSeat} 当场编译红:届时删本文件里的 {@link RequestMcpServerSpec}
29
32
  * 扩展与 {@link toCoreMcpServerSpec} 的剥键、两处 info,把键直接投进引擎规格(发送由引擎座兑现)。
30
33
  */
@@ -60,9 +63,27 @@ export declare function mcpIdentityHeadersIssue(transport: Record<string, unknow
60
63
  export declare function describeMcpIdentityHeadersConflict(server: string): string;
61
64
  /** `assertionHeader` 值不是字符串的**原因句**(不带条目名与键路径:点名由请求面统一的拒形 `entryFieldRefusal` 负责;值的形状只说种类,不回显值本身:它是一个调用方写的头名位置,可能是任何东西)。 */
62
65
  export declare function describeMcpAssertionHeaderNotAString(value: unknown): string;
63
- /** 这一条请求面条目是否声明了 `assertionHeader`(= 铸 core 规格时会被剥掉、需要说一行的那一类)。 */
64
- export declare function declaresMcpAssertionHeader(spec: RequestMcpServerSpec): boolean;
65
- /** 请求面条目 → 交给引擎的规格:剥掉 `assertionHeader`(引擎无座);其余逐字不动。声明过它的条目由调用方另说一行 info。 */
66
+ /**
67
+ * 引擎的 http 传输有没有断言头发送座 —— **全仓唯一的开关**。今天没有(core 的 http 传输无同名座,见头注与退役门)⇒ `false`:声明在、发送未接。
68
+ * 翻真 = 这一行 + 删退役门 + 把 {@link RequestHttpMcpTransport} 并进引擎型;剥键、披露、探测口拒、settings 面投影、能力位 `mcpAssertionHeaderSent` 随它一起翻
69
+ * (五处都读 {@link withholdsMcpAssertionHeader} / 本常量,没有第六处)。声明成 `boolean` 而不是字面 `false`:谓词里的真臂是活代码(单测传 `sent=true` 钉它),不是被收窄掉的死分支。
70
+ */
71
+ export declare const MCP_ASSERTION_HEADER_SENT: boolean;
72
+ /**
73
+ * 这一份 `assertionHeader` 声明是不是「被扣下」的那一类(需要剥键、需要说一行 info、探测口要拒的那一类)—— 即**声明了,而发送座不在**。
74
+ * 三处消费(剥键 / 披露 / 探测口拒)与 settings 面的投影共读这一只;`sent` 缺省读开关,单测传显式值钉另一臂。
75
+ */
76
+ export declare function withholdsMcpAssertionHeader(assertionHeader: unknown, sent?: boolean): boolean;
77
+ /** {@link withholdsMcpAssertionHeader} 在请求面条目上的读法:只有 http 传输有这个键(stdio 形上它不存在)。 */
78
+ export declare function specWithholdsMcpAssertionHeader(spec: RequestMcpServerSpec): boolean;
79
+ /**
80
+ * settings 面铸 spec 时「这个键投不投进引擎规格」:发送座在(`sent` 真)且声明了字符串 ⇒ 投,否则整键缺席。今天恒为空对象(开关假);
81
+ * 开关翻真的那天 settings 面因此自动跟着投,不会出现「位说发了、部署声明面却没投」。
82
+ */
83
+ export declare function sentMcpAssertionHeaderMember(assertionHeader: unknown, sent?: boolean): {
84
+ assertionHeader?: string;
85
+ };
86
+ /** 请求面条目 → 交给引擎的规格:被扣下的 `assertionHeader` 剥掉(引擎无座);其余逐字不动。被扣下的条目由调用方另说一行 info。 */
66
87
  export declare function toCoreMcpServerSpec(spec: RequestMcpServerSpec): McpServerSpec;
67
88
  /** `noteMcpAssertionHeaderNotSent` 的日志行名(运维 grep 用;请求面与 settings 面同一个名字)。 */
68
89
  export declare const MCP_ASSERTION_HEADER_NOT_SENT = "mcp_assertion_header_not_sent";
@@ -14,11 +14,18 @@ export function describeMcpAssertionHeaderNotAString(value) {
14
14
  const kind = value === null ? "null" : Array.isArray(value) ? "an array" : `a ${typeof value}`;
15
15
  return `must be a string (the HTTP header NAME the connector assertion travels in) — got ${kind}; omit the key to send no assertion header`;
16
16
  }
17
- export function declaresMcpAssertionHeader(spec) {
18
- return spec.transport.kind === "http" && spec.transport.assertionHeader !== undefined;
17
+ export const MCP_ASSERTION_HEADER_SENT = false;
18
+ export function withholdsMcpAssertionHeader(assertionHeader, sent = MCP_ASSERTION_HEADER_SENT) {
19
+ return !sent && assertionHeader !== undefined;
20
+ }
21
+ export function specWithholdsMcpAssertionHeader(spec) {
22
+ return spec.transport.kind === "http" && withholdsMcpAssertionHeader(spec.transport.assertionHeader);
23
+ }
24
+ export function sentMcpAssertionHeaderMember(assertionHeader, sent = MCP_ASSERTION_HEADER_SENT) {
25
+ return sent && typeof assertionHeader === "string" ? { assertionHeader } : {};
19
26
  }
20
27
  export function toCoreMcpServerSpec(spec) {
21
- if (spec.transport.kind !== "http" || spec.transport.assertionHeader === undefined)
28
+ if (spec.transport.kind !== "http" || !withholdsMcpAssertionHeader(spec.transport.assertionHeader))
22
29
  return spec;
23
30
  const { assertionHeader: _declaredNotSent, ...transport } = spec.transport;
24
31
  return { ...spec, transport };
@@ -1,11 +1,9 @@
1
1
  import { TOOL_APPROVAL_CARDS, TOOL_FAMILIES, TOOL_PATH_ABSENCES, TOOL_PATH_ACCESSES, TOOL_PATH_BASES } from "@sema-agent/core";
2
+ import { isStringArray } from "@sema-agent/settings-schema";
2
3
  import { quotedCallerText } from "./caller-text-echo.js";
3
4
  function isRecord(v) {
4
5
  return typeof v === "object" && v !== null && !Array.isArray(v);
5
6
  }
6
- function isStringArray(v) {
7
- return Array.isArray(v) && v.every((x) => typeof x === "string");
8
- }
9
7
  function memberOf(table, v) {
10
8
  return typeof v === "string" && table.includes(v);
11
9
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * S-640(S-512 片二)—— 一个任务的**项目层准备一步**:求键 → 把同一 checkout 的旧层折进当前层 → 交给同步腿跟踪。
3
+ * HTTP 腿(`boot/resolve-spec.ts`)与 run-local 腿共用这一只;键的规则只在 `memory-scope.ts` `memoryProjectLayerFor`(本模块不判让位、不铸键)。
4
+ *
5
+ * ## 折叠(`foldFrom`,由 `project-identity.ts` `projectLayerForTaskRoot` 给出,按序)
6
+ * · 退役 `repo:<sha256(根)[:24]>` 层 —— **一次性迁移**(7.98.0–7.132.x 的项目键;窗、删法见 `plugins/one-time-migrations.ts` 登记表 M25);
7
+ * · 当前档为 `r`(origin 远端)时本 checkout 的 `p` 档 —— **常设律**(先 `git init` 后加远端的项目,加远端那一刻不丢项目记忆)。
8
+ * 折 = core `migrateScope`(id 保留、rev 按新 scope 重算、逐条 CAS;冲突留原地,下一个任务再折)。**不记「已折」**:每个任务先读一眼旧层
9
+ * (`listHeaders`,空就不动),旧层之后又来了条目(仍在跑的旧任务 harvest、同步把别处的改动拉回旧层)下一个任务照折。
10
+ * 🔴 **与同步轮串行**:旧层若是被同步的层(`p` 档),在飞的那一轮的应答(拉回的改动 / 删除)按 id 落盘 —— 折在它前面,应答会把刚折走的条目改回旧层
11
+ * 或在新层上删掉它。所以有东西要折时,折叠排进同步腿的串行链(`MemorySyncRunner.exclusive`):等在飞的那一轮落定再折,折的时候没有轮在飞。
12
+ * 折在 spec 返回之前完成(`await`)⇒ 这一任务挂载时就看得见折进来的条目(代价如实:有东西要折又恰有一轮在飞时,任务起步等那一轮)。
13
+ * 折叠失败不挡任务(响亮 warn,条目原地不动、下次再折):项目层记忆暂时少了旧层那几条,比任务起不来更接近用户要的东西。
14
+ *
15
+ * ## 跟踪
16
+ * 当前层在复制覆盖面里(否则规则已让位、根本走不到这里)且有同步腿 ⇒ `track(scope)`:本地还没有条目也每轮拉它 —— 第二台设备第一次进这个项目
17
+ * 就拉得到第一台写的(拉回来可能晚于本任务挂载:第一个任务看不见,下一个任务看得见,如实)。
18
+ */
19
+ import { type MemoryBackend } from "@sema-agent/core";
20
+ import type { ServiceConfig } from "./config-types.js";
21
+ import type { MemorySyncRunner } from "./memory-sync-client.js";
22
+ import type { MemoryProjectKey } from "./project-identity.js";
23
+ export interface MemoryProjectLayers {
24
+ /** 这一任务的项目层声明(`TaskSpec.memory.projectKey`);`undefined` 的三形同 `memoryProjectKeyFor`。 */
25
+ prepare(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): Promise<MemoryProjectKey | undefined>;
26
+ }
27
+ export declare function createMemoryProjectLayers(deps: {
28
+ /** 本部署的记忆引擎 backend(File 或 SQL 孪生);缺席 ⇒ 没有可折的盘(记忆暗时调用方根本不会问)。 */
29
+ backend: MemoryBackend | undefined;
30
+ /** 同步腿(开同步时在场):跟踪当前层;有东西要折时折叠排进它的串行链。 */
31
+ syncRunner: Pick<MemorySyncRunner, "track" | "exclusive"> | undefined;
32
+ log: {
33
+ info(msg: string, fields?: Record<string, unknown>): void;
34
+ warn(msg: string, fields?: Record<string, unknown>): void;
35
+ };
36
+ }): MemoryProjectLayers;
37
+ //# sourceMappingURL=memory-project-layer.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { migrateScope } from "@sema-agent/core";
2
+ import { memoryProjectLayerFor } from "./memory-scope.js";
3
+ export function createMemoryProjectLayers(deps) {
4
+ const fold = async (backend, from, to) => {
5
+ let report;
6
+ try {
7
+ report = await migrateScope(backend, from, to);
8
+ }
9
+ catch (err) {
10
+ deps.log.warn("memory_project_layer_fold_failed", { from, to, err: String(err), note: "entries stay where they are; the next task in this project retries" });
11
+ return;
12
+ }
13
+ if (report.moved > 0)
14
+ deps.log.info("memory_project_layer_folded", { from, to, moved: report.moved, cursorMoved: report.cursorMoved });
15
+ if (report.conflicts.length > 0) {
16
+ deps.log.warn("memory_project_layer_fold_conflicts", { from, to, conflicts: report.conflicts.length, note: "conflicting entries stay in the old layer; the next task in this project retries" });
17
+ }
18
+ };
19
+ const occupied = async (backend, sources) => {
20
+ const out = [];
21
+ for (const from of sources) {
22
+ if ((await backend.listHeaders([from])).some((h) => h.scope === from))
23
+ out.push(from);
24
+ }
25
+ return out;
26
+ };
27
+ return {
28
+ async prepare(config, scope, taskRoot) {
29
+ const layer = memoryProjectLayerFor(config, scope, taskRoot);
30
+ const current = layer?.key.scope;
31
+ if (layer === undefined || current === undefined)
32
+ return layer?.key;
33
+ const backend = deps.backend;
34
+ if (backend !== undefined && (await occupied(backend, layer.foldFrom)).length > 0) {
35
+ const foldAll = async () => {
36
+ for (const from of await occupied(backend, layer.foldFrom))
37
+ await fold(backend, from, current);
38
+ };
39
+ await (deps.syncRunner !== undefined ? deps.syncRunner.exclusive(foldAll) : foldAll());
40
+ }
41
+ deps.syncRunner?.track(current);
42
+ return layer.key;
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=memory-project-layer.js.map
@@ -1,7 +1,7 @@
1
1
  import { FileMemoryEngineBackend, type TaskSpec } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "./config-types.js";
3
3
  import { type ExecutionLane } from "./execution-lane-caps.js";
4
- import { type MemoryProjectKey } from "./project-identity.js";
4
+ import { type MemoryProjectKey, type ProjectLayerOfTask } from "./project-identity.js";
5
5
  /** 前缀词表本体(供门/诊断读;判定一律走 {@link isV2ScopeKey},别在别处重写 startsWith 链)。 */
6
6
  export declare const V2_SCOPE_PREFIXES: readonly string[];
7
7
  /**
@@ -41,17 +41,19 @@ export declare function memoryScopeFor(config: ServiceConfig, principal?: string
41
41
  * S-512 / [ref] ① —— 这一请求的**项目身份声明**(`TaskSpec.memory.projectKey`)的唯一属主。
42
42
  *
43
43
  * **一条规则,与车道无关**(主会话 09-23 裁 ②):项目根 = spec 解析出的**真任务根**(`boot/resolve-spec.ts`
44
- * 的 `realTaskRootOf`,S-177 属主;run-local 腿 = 它的 `workspaceDir`)。本进程读得到这个根 ⇒ 求 canonical
45
- * 项目根并铸 `repo:` 键(in-process 车道的真任务根是 `process.cwd()`,读得到,所以照常铸 —— 它的语义就是
46
- * 「项目 = 任务根指向的地方」,不需要另一个词);读不到(根在沙箱/设备盘上,或 host 车道会话没登记 cwd、
47
- * spec 期还不知道根)⇒ 调用方传 `undefined` ⇒ `{ absent: "unreadable" }`,core 铸 operator 通知
48
- * `memory.project_scope_absent`,写照常落全局层 —— **响亮**,不是静默落 `local`。
44
+ * 的 `realTaskRootOf`,S-177 属主;run-local 腿 = 它的 `workspaceDir`)。本进程读得到这个根 ⇒ 求项目身份并铸项目层键
45
+ * (in-process 车道的真任务根是 `process.cwd()`,读得到,所以照常铸 —— 它的语义就是「项目 = 任务根指向的地方」,不需要另一个词);
46
+ * 读不到(根在沙箱/设备盘上,或 host 车道会话没登记 cwd、spec 期还不知道根)⇒ 调用方传 `undefined` ⇒ `{ absent: "unreadable" }`,
47
+ * core 铸 operator 通知 `memory.project_scope_absent`,写照常落全局层 —— **响亮**,不是静默落 `local`。
48
+ *
49
+ * 🔴 S-640 键形:项目层 = **全局层的属主 × 项目身份**,`userrepo:<属主段>/<档>.<摘要>`(文法 `project-layer-key.ts`,身份 `project-identity.ts`);
50
+ * 属主 = `projectLayerOwnerOf(scope)`。
49
51
  *
50
52
  * 🔴 **写家只落复制路径覆盖得到的层**(7.98.0 合并复审 F1,主会话裁「统一规则,不做特判」):写家 = 读列表里、本部署
51
- * **每一条已配置的复制路径都覆盖到**的最具体的一层。项目层不在覆盖面里 ⇒ 本任务**不按项目键控**,写家落全局层,
52
- * 读列表与 7.97.0 同形 —— 否则开了 `MEMORY_SYNC_*` 的部署会把新记忆写进一个同步腿从不碰的层(静默不同步)。
53
- * 覆盖面只经 {@link memoryReplicationCoverage} 求(调用点零 `if (memorySync)`);以后项目层同步落地,只改覆盖面的
54
- * 来源,本规则不动。让位由启动通知 `memory_project_scope_yields_to_sync` 说出来(`config.ts` 记忆域,读同一只覆盖面)。
53
+ * **每一条已配置的复制路径都覆盖到**的最具体的一层。项目层不在覆盖面里 ⇒ 本任务**不按项目键控**,写家落全局层 —— 否则开了
54
+ * `MEMORY_SYNC_*` 的部署会把新记忆写进一个同步腿从不碰的层(静默不同步)。覆盖面只经 {@link memoryReplicationCoverage} 求(调用点零
55
+ * `if (memorySync)`);S-640 改的只是覆盖面的**来源**(一条复制路径复制它那一层与同属主的全部项目层),本规则一字不动。让位由启动通知
56
+ * `memory_project_scope_yields_to_sync` 说出来(`config.ts` 记忆域,读同一只覆盖面与 {@link replicatesProjectLayersOf})。
55
57
  * 缺席声明(`{ absent }`)不受覆盖面影响:它本来就写全局层,通知说的是「为什么没有项目层」。
56
58
  *
57
59
  * 返回 `undefined` 只有三形,都不是「忘了」:
@@ -60,20 +62,25 @@ export declare function memoryScopeFor(config: ServiceConfig, principal?: string
60
62
  * · 本任务的项目层不在复制覆盖面里(见上)—— 让位给全局层。
61
63
  */
62
64
  export declare function memoryProjectKeyFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): MemoryProjectKey | undefined;
65
+ /** {@link memoryProjectKeyFor} 的同一条规则,带上本 checkout 要折进当前层的旧层(`memory-project-layer.ts` 读它)。 */
66
+ export declare function memoryProjectLayerFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): ProjectLayerOfTask | undefined;
63
67
  /**
64
68
  * 本部署记忆的**复制覆盖面** —— 唯一属主(7.98.0 合并复审 F1)。「复制路径」= 把某一层记忆搬出本机的腿;今天只有一条:
65
- * TOC 同步客户端腿(`MEMORY_SYNC_*`,`memory-sync-client.ts`),它只同步**一层**(`config.memorySync.scope`,core
66
- * `syncMemoryScope` 按 `scope ===` 取条目)。没有复制路径 ⇒ `unreplicated`(没有哪一层会被复制落下)。
69
+ * TOC 同步客户端腿(`MEMORY_SYNC_*`,`memory-sync-client.ts`)。🔴 S-640:一条复制路径复制**它那一层**(`config.memorySync.scope`)
70
+ * **以及同一属主的全部项目层**(`userrepo:<属主>/…`,属主 = `projectLayerOwnerOf(那一层)`)—— 同步腿按同一判据选层
71
+ * (`memory-sync-client.ts` 读 {@link replicationCovers})。没有复制路径 ⇒ `unreplicated`(没有哪一层会被复制落下)。
67
72
  */
68
73
  export type MemoryReplicationCoverage = {
69
74
  readonly kind: "unreplicated";
70
75
  } | {
71
- readonly kind: "scopes";
72
- readonly scopes: readonly string[];
76
+ readonly kind: "layers";
77
+ readonly layers: readonly string[];
73
78
  };
74
79
  export declare function memoryReplicationCoverage(config: Pick<ServiceConfig, "memorySync">): MemoryReplicationCoverage;
75
- /** 覆盖面是否含这一层(`unreplicated` 含一切)。 */
80
+ /** 覆盖面是否含这一层(`unreplicated` 含一切;复制层本身,或它同属主的项目层)。 */
76
81
  export declare function replicationCovers(coverage: MemoryReplicationCoverage, scope: string): boolean;
82
+ /** 覆盖面复制不复制属主 `owner` 的项目层(有一条复制层与它同属主)。启动通知与 {@link replicationCovers} 读同一只判据。 */
83
+ export declare function replicatesProjectLayersOf(coverage: MemoryReplicationCoverage, owner: string): boolean;
77
84
  /**
78
85
  * N0 (通宵测试 2026-07-09,三重坐实 e2b+kata+代码): the file memory engine (core [ref]) materializes and
79
86
  * harvests via `node:fs` on the WORKER's local fs, while a remote exec lane routes the model's file tools to
@@ -242,7 +249,7 @@ export declare function memoryStoreRootMovedRefusal(e: unknown): (Error & {
242
249
  export declare function memorySpecForRequest(scope: string | undefined,
243
250
  /** S-512 / [ref] ①:本请求的项目身份声明({@link memoryProjectKeyFor} 的产物)。**必填位**(不是可选参数):
244
251
  * 每个铸点都得写出一个值 —— `undefined` 只合法地表示「本任务不按项目键控」(多租户 / 记忆暗 / 项目层不在复制覆盖面里)。
245
- * · `{ scope: repoKey }` ⇒ 读 = `[repoKey, scope, ...extras]`、`writeScope` **显式** = `repoKey`(core 缺省取末层
252
+ * · `{ scope: projectKey }`(S-640 起 `userrepo:…`)⇒ 读 = `[projectKey, scope, ...extras]`、`writeScope` **显式** = 项目层(core 缺省取末层
246
253
  * = 全局层,不钉就是 M-03 本身;core 另有 `config.memory_write_scope_mismatch` 兜这一向);
247
254
  * · `{ absent }` ⇒ 读 = `[scope, ...extras]`、`writeScope` 显式 = `scope`(全局层),core 铸 `memory.project_scope_absent`;
248
255
  * · 两形都把声明原样落 `TaskSpec.memory.projectKey`(core 的生效面回显它)。 */
@@ -2,7 +2,8 @@ import { join } from "node:path";
2
2
  import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope, normalizeMemorySpec } from "@sema-agent/core";
3
3
  import { executionLaneCaps, executionLaneOf } from "./execution-lane-caps.js";
4
4
  import { assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
5
- import { projectKeyForTaskRoot } from "./project-identity.js";
5
+ import { projectLayerForTaskRoot } from "./project-identity.js";
6
+ import { parseProjectLayerKey, projectLayerOwnerOf } from "./project-layer-key.js";
6
7
  const V2_SCOPE_PREFIX_BY_KIND = {
7
8
  user: "user:",
8
9
  org: "org:",
@@ -28,22 +29,37 @@ export function memoryScopeFor(config, principal, projectId) {
28
29
  return config.memoryScope;
29
30
  }
30
31
  export function memoryProjectKeyFor(config, scope, taskRoot) {
32
+ return memoryProjectLayerFor(config, scope, taskRoot)?.key;
33
+ }
34
+ export function memoryProjectLayerFor(config, scope, taskRoot) {
31
35
  if (config.requirePrincipal === true || scope === undefined)
32
36
  return undefined;
33
- const declared = projectKeyForTaskRoot(taskRoot);
34
- if (declared.scope !== undefined && !replicationCovers(memoryReplicationCoverage(config), declared.scope))
37
+ const layer = projectLayerForTaskRoot(taskRoot, projectLayerOwnerOf(scope));
38
+ if (layer.key.scope !== undefined && !replicationCovers(memoryReplicationCoverage(config), layer.key.scope))
35
39
  return undefined;
36
- return declared;
40
+ return layer;
37
41
  }
38
42
  export function memoryReplicationCoverage(config) {
39
- return config.memorySync === undefined ? { kind: "unreplicated" } : { kind: "scopes", scopes: [config.memorySync.scope] };
43
+ return config.memorySync === undefined ? { kind: "unreplicated" } : { kind: "layers", layers: [config.memorySync.scope] };
40
44
  }
41
45
  export function replicationCovers(coverage, scope) {
42
46
  switch (coverage.kind) {
43
47
  case "unreplicated":
44
48
  return true;
45
- case "scopes":
46
- return coverage.scopes.includes(scope);
49
+ case "layers": {
50
+ if (coverage.layers.includes(scope))
51
+ return true;
52
+ const projectLayer = parseProjectLayerKey(scope);
53
+ return projectLayer !== undefined && replicatesProjectLayersOf(coverage, projectLayer.owner);
54
+ }
55
+ }
56
+ }
57
+ export function replicatesProjectLayersOf(coverage, owner) {
58
+ switch (coverage.kind) {
59
+ case "unreplicated":
60
+ return true;
61
+ case "layers":
62
+ return coverage.layers.some((layer) => projectLayerOwnerOf(layer) === owner);
47
63
  }
48
64
  }
49
65
  export function memoryEngineRemoteLanePosture(config) {
@@ -140,7 +156,7 @@ export function memorySpecForRequest(scope, projectKey, memoryWrite, defaultScop
140
156
  const projectScope = projectKey?.scope;
141
157
  if (projectScope !== undefined && projectScope === scope) {
142
158
  throw new Error(`memory project scope ${JSON.stringify(projectScope)} equals the global memory scope (MEMORY_SCOPE) — the project layer and the global layer would be one directory. ` +
143
- "Set MEMORY_SCOPE to a name outside the server-minted project-key family (see src/project-identity.ts).");
159
+ "Set MEMORY_SCOPE to a name outside the server-minted project-key families (see src/project-layer-key.ts).");
144
160
  }
145
161
  const isV2 = isV2ScopeKey;
146
162
  const extras = (defaultScopes ?? []).filter((s, i, arr) => typeof s === "string" && s.length > 0 && s !== scope && s !== projectScope && arr.indexOf(s) === i);