@sema-agent/server 7.138.0 → 7.140.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 (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
package/dist/config.js CHANGED
@@ -9,6 +9,7 @@ import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
9
9
  import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
10
10
  import { buildRuleListJudgeOptions } from "./permission-rule-list.js";
11
11
  import { isV2ScopeKey, memoryReplicationCoverage, replicatesProjectLayersOf } from "./memory-scope.js";
12
+ import { memorySyncPeerOf } from "./memory-sync-client.js";
12
13
  import { assertProjectLayerOwnable } from "./project-identity.js";
13
14
  import { projectLayerOwnerOf } from "./project-layer-key.js";
14
15
  import { DEFAULT_QUESTION_THROTTLE } from "./question.js";
@@ -26,7 +27,7 @@ import { THINKING_LEVELS, THINKING_FORMATS, buildModelCompat, isThinkingFormat,
26
27
  import {} from "./posture-source.js";
27
28
  import { COST_QUOTA_WINDOW_SEC_RANGE, costQuotaWindowSecInRange } from "./observability/cost-quota.js";
28
29
  import { NAME_TABLE_BEHAVIOR, TOOL_NAME_ROSTER, describeUnmatchableRuleName, ruleEntryToolName, unmatchableRuleNameOf } from "./namespaced-rule-name.js";
29
- import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdentityRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal, operatorSingleUserRefusal, workflowParkDecisionDomainRefusal } from "./config-invariants.js";
30
+ import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdentityRefusal, halfOpenDirectDoorRefusal, leaderDurableStoreRefusal, operatorPrincipalsRequiredRefusal, operatorSingleUserRefusal, remoteConfigProviderUrlRefusal, retentionLaneRefusal, workflowParkDecisionDomainRefusal } from "./config-invariants.js";
30
31
  import { declaredSemaConfigDir } from "./sema-config-dir.js";
31
32
  import { lkgPathOverrideOf } from "./config-lkg.js";
32
33
  import { K8S_WORKSPACE_MOUNT_DEFAULT, WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE, parseWorkspaceSnapshotExclude, parseWorkspaceSnapshotPrefix } from "./workspace-space.js";
@@ -35,7 +36,7 @@ import { laneLivenessOf } from "./exec-bounds.js";
35
36
  import { SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS } from "./sandbox-egress-policy.js";
36
37
  import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE_MOUNT_RESERVED, WORKSPACE_CACHE_SIZE_DEFAULT, WORKSPACE_CACHE_WORDS, isK8sQuantity, mountsOverlap, reservedMountCollision } from "./workspace-cache.js";
37
38
  import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
38
- import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneOf, executionLaneRequiredRefusal, laneRequiredEnv } from "./execution-lane-caps.js";
39
+ import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneOf, executionLaneRequiredRefusal, laneMissingEnv } from "./execution-lane-caps.js";
39
40
  import { MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS } from "@sema-agent/settings-schema/execution-lanes";
40
41
  import { AUTONOMY_MODES } from "./runtime-governance.js";
41
42
  import { MAX_NAMED_INERT_ENTRIES, allowLayerEntryCountIssue, describeInertRuleEntries, inertRuleEntriesOf, inertRuleEntryGuidance, ruleEntryListShapeOk } from "./rule-entry-list.js";
@@ -195,7 +196,7 @@ function buildExecLaneNotice(remoteExec) {
195
196
  };
196
197
  }
197
198
  const ENV_VALUE_ECHO_CHARS = 120;
198
- function parseConfigProvider() {
199
+ export function parseConfigProvider() {
199
200
  const raw = process.env.CONFIG_PROVIDER;
200
201
  if (raw === undefined || raw === "")
201
202
  return undefined;
@@ -509,11 +510,32 @@ function parseNameListEnv(name, raw, opts) {
509
510
  }
510
511
  const ENV_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
511
512
  const ENV_NAME_ALLOWLIST_WITHHELD = "<withheld: entries are not echoed — a value pasted in place of a name can pass the name check>";
512
- function ownCredentialPutBackProblem(name) {
513
+ const OWN_CREDENTIAL_HANDOFF = {
514
+ "host-put-back": { where: "a put-back list hands the named variable back, as written, to the child the engine withheld it from", whom: "that child" },
515
+ "sandbox-grant": { where: "a sandbox list puts the named variable, as written, into a sandbox the model can read", whom: "the sandbox" },
516
+ };
517
+ function ownCredentialNameProblem(name, channel) {
513
518
  const engine = inheritEnvPolicyProblem([name]);
514
- return engine === undefined
515
- ? undefined
516
- : `this deployment's own credential — a put-back list hands the named variable back, as written, to the child the engine withheld it from (the engine refuses it on any allow list: ${engine})`;
519
+ if (engine === undefined)
520
+ return undefined;
521
+ const { where, whom } = OWN_CREDENTIAL_HANDOFF[channel];
522
+ return `this deployment's own credential — ${where}; give ${whom} a credential of its own (another name and another value) and list that name instead (the engine refuses it on any allow list: ${engine})`;
523
+ }
524
+ const NAMES_IN_FORCE = {
525
+ LSP_ENV_ALLOW: (c) => c.lspEnvAllow,
526
+ E2B_SANDBOX_ENV: (c) => (c.remoteExec?.provider === "e2b" ? Object.keys(c.remoteExec.sandboxEnv ?? {}) : []),
527
+ K8S_SANDBOX_ENV: (c) => (c.remoteExec?.provider === "k8s" ? Object.keys(c.remoteExec.sandboxEnv ?? {}) : []),
528
+ DOCKER_SANDBOX_ENV: (c) => (c.remoteExec?.provider === "local-docker" ? Object.keys(c.remoteExec.env ?? {}) : []),
529
+ };
530
+ const SERVER_ENV_NAME_ALLOWLIST_KNOBS = Object.keys(NAMES_IN_FORCE);
531
+ export function sandboxEnvNamesInForce(seats) {
532
+ return SERVER_ENV_NAME_ALLOWLIST_KNOBS.filter((knob) => ENV_NAME_ALLOWLIST_KNOBS[knob].channel === "sandbox-grant").flatMap((knob) => NAMES_IN_FORCE[knob](seats));
533
+ }
534
+ export function ownCredentialHitsInForce(seats) {
535
+ return SERVER_ENV_NAME_ALLOWLIST_KNOBS.flatMap((knob) => NAMES_IN_FORCE[knob](seats).flatMap((name) => {
536
+ const problem = ownCredentialNameProblem(name, ENV_NAME_ALLOWLIST_KNOBS[knob].channel);
537
+ return problem === undefined ? [] : [{ knob, name, problem }];
538
+ }));
517
539
  }
518
540
  export function parseEnvNameAllowlist(knob, singleUserTurnkey) {
519
541
  const raw = process.env[knob];
@@ -528,9 +550,7 @@ export function parseEnvNameAllowlist(knob, singleUserTurnkey) {
528
550
  ? 'not an environment-variable name (letters, digits and "_", not starting with a digit)'
529
551
  : ENV_NAME_ALLOWLIST_KNOB_NAMES.has(seg.toUpperCase())
530
552
  ? `the name of a name-list knob (${[...ENV_NAME_ALLOWLIST_KNOB_NAMES].join(" / ")}: their raw text is withheld from every child process and sandbox; naming one would put that text back)`
531
- : spec.channel === "host-put-back"
532
- ? ownCredentialPutBackProblem(seg)
533
- : undefined;
553
+ : ownCredentialNameProblem(seg, spec.channel);
534
554
  if (problem === undefined)
535
555
  return;
536
556
  throw new Error(`env ${knob}: entry #${i + 1} of ${segments.length} is ${problem}` +
@@ -1704,6 +1724,7 @@ function parseMemoryDomain(ctx) {
1704
1724
  if (memorySyncToken === undefined) {
1705
1725
  throw new Error("MEMORY_SYNC_URL is set but MEMORY_SYNC_TOKEN is not — refusing to start half-configured (the central sync face requires a bearer token)");
1706
1726
  }
1727
+ memorySyncPeerOf(memorySyncUrl);
1707
1728
  if (memorySyncScopeRaw === undefined && memoryScope !== undefined && !isV2ScopeKey(memoryScope)) {
1708
1729
  throw new Error(`MEMORY_SYNC_URL is set but MEMORY_SYNC_SCOPE is not set and MEMORY_SCOPE (${JSON.stringify(memoryScope)}) is not a typed scope key — the sync leg replicates exactly ONE scope by name ` +
1709
1730
  "(core syncMemoryScope takes the entries whose scope === the sync scope and POSTs them under that same name), so deriving `user:<MEMORY_SCOPE>` would name a scope this deployment never writes and nothing would ever be synced (S-964). " +
@@ -1902,10 +1923,10 @@ function parseOrchestrationDomain(ctx) {
1902
1923
  throw new Error(`REMOTE_EXEC="${lane}" is not an execution lane — valid: ${REMOTE_EXEC_PROVIDER_WORDS.join(" | ")}. ` +
1903
1924
  `An unrecognized value used to fall through to the host/in-process lane silently; refusing to start instead (#157).`);
1904
1925
  }
1905
- const missing = laneRequiredEnv(word).filter((name) => !process.env[name]);
1926
+ const missing = laneMissingEnv(word, process.env);
1906
1927
  if (missing.length > 0) {
1907
- throw new Error(`REMOTE_EXEC=${lane} but required env ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing/empty — ` +
1908
- `refusing to start rather than silently downgrading to the host/in-process lane (#157; unset REMOTE_EXEC if you meant no sandbox lane).`);
1928
+ throw new Error(`REMOTE_EXEC=${lane} but required env ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} unset, empty or whitespace-only — it counts as absent: ` +
1929
+ `refusing to start rather than silently downgrading to the host/in-process lane (#157; set ${missing.length === 1 ? "it" : "them"}, or unset REMOTE_EXEC if you meant no sandbox lane).`);
1909
1930
  }
1910
1931
  }
1911
1932
  }
@@ -1923,6 +1944,7 @@ function parseOrchestrationDomain(ctx) {
1923
1944
  assertReadDenyBuiltinsResolvable(tiers, exclude);
1924
1945
  return { ...(tiers !== undefined ? { readDenyBuiltinTiers: tiers } : {}), ...(exclude !== undefined ? { readDenyBuiltinExclude: exclude } : {}) };
1925
1946
  })(),
1947
+ readDenyFloorRequired: boolEnv("READ_DENY_FLOOR_REQUIRED", false),
1926
1948
  ...((dir) => (dir !== undefined ? { agentConfigDir: dir } : {}))(declaredSemaConfigDir()),
1927
1949
  ...((p) => (p !== undefined ? { configLkgPath: p } : {}))(lkgPathOverrideOf(process.env.CONFIG_LKG_PATH)),
1928
1950
  configLkgEnabled: boolEnv("CONFIG_LKG_ENABLED", true),
@@ -1951,7 +1973,7 @@ function parseOrchestrationDomain(ctx) {
1951
1973
  orphanReclaim: boolEnv("E2B_ORPHAN_RECLAIM", true),
1952
1974
  }
1953
1975
  :
1954
- process.env.REMOTE_EXEC === "k8s" && process.env.K8S_SANDBOX_IMAGE
1976
+ process.env.REMOTE_EXEC === "k8s" && process.env.K8S_SANDBOX_IMAGE && process.env.K8S_NAMESPACE
1955
1977
  ? {
1956
1978
  provider: "k8s",
1957
1979
  image: process.env.K8S_SANDBOX_IMAGE,
@@ -1959,7 +1981,7 @@ function parseOrchestrationDomain(ctx) {
1959
1981
  ...(process.env.K8S_TOKEN ? { token: process.env.K8S_TOKEN } : {}),
1960
1982
  ...(process.env.K8S_CA_CERT_B64 ? { caCert: Buffer.from(process.env.K8S_CA_CERT_B64, "base64").toString("utf8") } : {}),
1961
1983
  ...(boolEnv("K8S_INSECURE_TLS", false) ? { insecureTls: true } : {}),
1962
- ...(process.env.K8S_NAMESPACE ? { namespace: process.env.K8S_NAMESPACE } : {}),
1984
+ namespace: process.env.K8S_NAMESPACE,
1963
1985
  ...(process.env.K8S_RUNTIME_CLASS != null ? { runtimeClass: process.env.K8S_RUNTIME_CLASS } : {}),
1964
1986
  ...(process.env.K8S_MOUNT_PATH ? { mountPath: process.env.K8S_MOUNT_PATH } : {}),
1965
1987
  ...((n) => (n !== undefined ? { timeoutMs: n } : {}))(optNumEnvStrict("K8S_TIMEOUT_MS")),
@@ -2450,7 +2472,7 @@ const ORCHESTRATION_GROUP_KEYS = [
2450
2472
  "workflowSizeGuideline", "backgroundAgentRetentionMs", "backgroundAgentStaleRunningMs",
2451
2473
  "backgroundAgentParkClaimStaleMs", "delegationEntryCaps", "rosterRetentionMs", "leaderRunStaleMs", "scratchpadSweepTtlMs", "sandboxPkgSource", "sandboxPkgSourceCustom",
2452
2474
  "sessionAutoTitle", "sessionAutoTitleSource", "selectEnvironmentTool", "envFactsEnabled", "toolDeferLongtail", "lspEnabled", "lspHostEnabled", "lspEnvAllow",
2453
- "imageBakes", "readFace", "readFaceSource", "taskWriteFaceEscalation", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude",
2475
+ "imageBakes", "readFace", "readFaceSource", "taskWriteFaceEscalation", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude", "readDenyFloorRequired",
2454
2476
  "agentConfigDir", "configLkgPath", "configLkgEnabled",
2455
2477
  ];
2456
2478
  const LIMITS_HTTP_GROUP_KEYS = [
@@ -2602,6 +2624,14 @@ export function loadConfig() {
2602
2624
  if (centerIdentity !== undefined) {
2603
2625
  throw new Error(centerIdentity);
2604
2626
  }
2627
+ const remoteSourceRefusal = remoteConfigProviderUrlRefusal({ configProvider: integrations.configProvider, webAdmin: integrations.webAdmin });
2628
+ if (remoteSourceRefusal !== undefined) {
2629
+ throw new Error(remoteSourceRefusal);
2630
+ }
2631
+ const retentionRefusal = retentionLaneRefusal({ retentionPolicy: auth.retentionPolicy, retentionSweep: auth.retentionSweep });
2632
+ if (retentionRefusal !== undefined) {
2633
+ throw new Error(retentionRefusal);
2634
+ }
2605
2635
  const { directApprovalDoor, durableApproval: dgDurable, approvalHmacKeys: dgHmacKeys } = approval;
2606
2636
  const { principalJwtPubkeys, principalJwtIss, principalJwtAud, operatorPrincipals: dgOperators, operatorSingleUser, authTokens } = auth;
2607
2637
  const directDoorActive = directApprovalDoor && dgDurable && dgHmacKeys.length > 0 && principalJwtPubkeys.length > 0 && !!principalJwtIss && !!principalJwtAud;
@@ -38,7 +38,7 @@
38
38
  * M23 回填了无主的父)⇒ 写下的仍是判断时的读数 —— 按律如此;那一行若因此无主,照上一节处置。
39
39
  * · **停驻挂点**(`plugins/session-store.ts` 的 `ensureChildSessionDurableWithPromotion`;快路 + 晋升):来源 = **调用方交出的委派树根,只有这一只**,连同
40
40
  * 调用方判断那一刻读到的根行住客({@link DelegationRootWitness};见证值,判断用,不进 CAS):请求腿 = 本请求经鉴权解析的会话,住客 = 受理时那一读
41
- * (`boot/resolve-spec.ts`);`/decide` 的赎回腿 = 行上的 `rootSessionId`,住客 = 赎回腿 claim 前后两读的那一位(`parked-revive-surface.ts`,每一行都交)。
41
+ * (`boot/resolve-spec.ts`);`/decide` 的赎回腿 = 行上的 `rootSessionId`,住客 = 赎回腿写口放行的那一位(认领之前解析器那一读;`parked-revive-surface.ts`,每一行都交)。
42
42
  * 挂点先读一次根行:它在而住客不是见证的那一位 ⇒ 根被删、同 id 换了住客 ⇒ **拒**(不按新住客晋升 / 认领,{@link assertRootStillWitnessed});它不在 ⇒ 律给哨兵
43
43
  * (根真没了)。不回落父:core 的挂点 `ensureChildSessionDurable(sessionId)` 只交子代 id,父 id 在这一写点拿不到;两条候选读法都读不到东西 ——
44
44
  * ① 读 `session_meta(子代).placement_parent_session_id`:晋升的拷贝分支恰恰只在子代**没有** placed 行时才走(有 placed 行 ⇒ 快路),这一列按构造为空;
@@ -52,6 +52,7 @@
52
52
  */
53
53
  import type { SessionPlacement } from "@sema-agent/core";
54
54
  import { type HeadRead, type SessionFace } from "./session-incarnation.js";
55
+ import type { SqlDialect } from "./plugins/sql-driver.js";
55
56
  /** 孤行哨兵属主的前缀(保留主体前缀:`security.ts` 的 `RESERVED_PRINCIPAL_PREFIXES` 登记它,任何身份来源报出带它的主体都在鉴权边界被拒)。 */
56
57
  export declare const ORPHAN_SESSION_OWNER_PREFIX = "_orphan:";
57
58
  /** 一只推不出属主的子代会话的哨兵属主(每只会话一个值:即便哪天有谁误配出这个串,也只够得着这一只)。 */
@@ -112,7 +113,7 @@ export declare function reportWrittenOwner(log: OrphanOwnerLog, site: DelegatedO
112
113
  * 不改变写进去的值(律:属主 = 判断时见证到的,本文件头注)。两方言只差占位符。
113
114
  * MySQL 协议下 mysql2 缺省带 FOUND_ROWS,但条件里有 `owner IS NULL` ⇒ 匹配行数 = 改写行数。
114
115
  */
115
- export declare const CLAIM_OWNERLESS_PLACED_SQL: Readonly<Record<"tidb" | "pg", string>>;
116
+ export declare const CLAIM_OWNERLESS_PLACED_SQL: Readonly<Record<SqlDialect, string>>;
116
117
  /** 认领句的绑参(两方言同序;店与 M23 都经它,不各排一份):`incarnation` = 判断时读到的子行化身。 */
117
118
  export declare function claimOwnerlessParams(sessionId: string, incarnation: string, owner: string): readonly [string, string, string];
118
119
  /** 店的认领口(SQL 两店 + 缓存包装层同一个签名):这只会话的那一位住客(`incarnation`)仍是无主的 placed 行 ⇒ 写 `owner`,返回是否写成。 */
@@ -134,7 +135,7 @@ export interface PlacedRowWriter {
134
135
  export declare function createPlacedSessionRow(sessionId: string, placement: PlacementSources, io: PlacedRowWriter, log: OrphanOwnerLog): Promise<void>;
135
136
  /**
136
137
  * 停驻挂点的**见证**:调用方交出的委派树根 + 调用方判断那一刻读到的根行住客(化身;`null` = 那一读里根行不在)。请求腿 = 鉴权解析的会话与受理时那一读;
137
- * 赎回腿 = 行上的根与 claim 前后两读的那一位住客。
138
+ * 赎回腿 = 行上的根与写口放行的那一位住客(认领之前解析器那一读)。
138
139
  */
139
140
  export interface DelegationRootWitness {
140
141
  readonly sessionId: string;
@@ -47,7 +47,7 @@ export function reportWrittenOwner(log, site, childId, judged) {
47
47
  });
48
48
  }
49
49
  export const CLAIM_OWNERLESS_PLACED_SQL = {
50
- tidb: "UPDATE session_meta SET owner = ? WHERE session_id = ? AND incarnation = ? AND owner IS NULL AND placement_kind IS NOT NULL",
50
+ mysql: "UPDATE session_meta SET owner = ? WHERE session_id = ? AND incarnation = ? AND owner IS NULL AND placement_kind IS NOT NULL",
51
51
  pg: "UPDATE session_meta SET owner = $1 WHERE session_id = $2 AND incarnation = $3 AND owner IS NULL AND placement_kind IS NOT NULL",
52
52
  };
53
53
  export function claimOwnerlessParams(sessionId, incarnation, owner) {
@@ -9,7 +9,7 @@
9
9
  * ## 两根轴,每根轴一个读口(原始配置键只在这两个函数里被读)
10
10
  * · `principal` —— 运维的 `REQUIRE_PRINCIPAL` 声明({@link readPrincipalAxis})。
11
11
  * · `lane` —— `REMOTE_EXEC` 的归一车道词(`executionLaneOf`,认不出 ⇒ `undefined`,行必须 fail-closed)+ 那条车道的能力表行 +
12
- * 车道子旋钮(k8s 运行时类 / 快照店、隐式 host 尾臂记号)({@link readLaneAxis})。
12
+ * 这份部署上执行环境的能力块(唯一作者 `capabilitiesFor`;k8s 的快照店是它的输入)+ 车道子旋钮(k8s 运行时类、隐式 host 尾臂记号)({@link readLaneAxis})。
13
13
  *
14
14
  * ## 轴列不是注释,是型
15
15
  * 每行的判官只收到它**声明**的那几根轴的事实({@link DeploymentShapeFacts}),读不到配置本身 —— 判官碰一根没声明的轴 = 编译红;
@@ -21,8 +21,9 @@
21
21
  * (`mcpInjectionHonored` = 锁 ∧ 合规 ∧ `!multiTenant`;`taskHooksHonored`;身份门 …),读行、不读原始键。
22
22
  * 车道表(`execution-lane-caps.ts`)按车道词键控的布尔列(`laneIsolated` 等)是车道轴自己的事实表,不是「部署」的谓词;行读它们。
23
23
  *
24
- * 依赖纪律:叶模块 —— 值导入只有 `execution-lane-caps.ts`(它本身是叶),零运行期环。
24
+ * 依赖纪律:叶模块 —— 值导入只有 `execution-lane-caps.ts`(它本身是叶),零运行期环(引擎只 type-import)。
25
25
  */
26
+ import type { ExecutionEnvCapabilities } from "@sema-agent/core";
26
27
  import { type ExecutionLane, type ExecutionLaneCaps } from "./execution-lane-caps.js";
27
28
  /** `principal` 轴读的配置键(`ServiceConfig` 结构上满足)。 */
28
29
  export interface PrincipalAxisInput {
@@ -48,10 +49,10 @@ export interface LaneAxisFacts {
48
49
  readonly lane: ExecutionLane | undefined;
49
50
  /** 那条车道的能力表行(认不出 ⇒ `undefined`)。 */
50
51
  readonly caps: ExecutionLaneCaps | undefined;
52
+ /** 这份部署上执行环境的能力块(`capabilitiesFor`;认不出 ⇒ `undefined`)。 */
53
+ readonly block: ExecutionEnvCapabilities | undefined;
51
54
  /** k8s 的 `runtimeClass`(别的车道 `undefined`)。 */
52
55
  readonly k8sRuntimeClass: string | undefined;
53
- /** k8s 配了 S3 工作区快照店(别的车道 `false`)。 */
54
- readonly k8sSnapshotStore: boolean;
55
56
  /** host 车道值上带「单机形缺省」记号(唯一写者 = `config.ts` 三元链的隐式 host 尾臂;显式 `REMOTE_EXEC=host` 与文件覆写都不带)。 */
56
57
  readonly singleMachineDefault: boolean;
57
58
  }
@@ -92,7 +93,7 @@ export declare const DEPLOYMENT_SHAPE_PREDICATES: Readonly<{
92
93
  * 签发方配置是身份验签轴、不算租户证据)—— 两个问题、一根轴、一个读口;「鉴权必需」与「多租形」拆成两个声明时只改这一行的判官。
93
94
  */
94
95
  multiTenant: DeploymentShapePredicate<"principal">;
95
- /** 读能力表 `callerCwd` 位(host = 单主才开;device = 路径落在发起者自己的设备上,归属由 `device_session` 绑定验过,不要求单主)。 */
96
+ /** 读能力表 `callerCwd` 位(host = 单主才开;device = 路径落在发起者自己的设备上,归属由根会话行执行格上的设备绑定验过,不要求单主)。 */
96
97
  callerCwd: DeploymentShapePredicate<"lane" | "principal">;
97
98
  /** 调用方给的宿主绝对路径(附加目录 / 项目上下文 / 本机 SendUserFile / 指令文件探针)只在「手就在本 worker 的盘上 ∧ 单主」时指向调用方自己的东西。 */
98
99
  callerOwnsWorkerDisk: DeploymentShapePredicate<"lane" | "principal">;
@@ -102,7 +103,7 @@ export declare const DEPLOYMENT_SHAPE_PREDICATES: Readonly<{
102
103
  inProcessSingleUser: DeploymentShapePredicate<"lane" | "principal">;
103
104
  /** 任务层替部署声明宿主姿态(bypassPermissions 开写面 / host shell 继承整份环境)的射程:device 车道上跨租户宿主穿越结构性不存在(clay 裁定③)。 */
104
105
  taskHostPostureDeclarations: DeploymentShapePredicate<"lane" | "principal">;
105
- /** 写门走 [ref] 代理裁决(文件平面不在 worker 本机);认不出的词 ⇒ 走代理(代理 slot 空 ⇒ 写门 fail-closed)。 */
106
+ /** 写门走代理裁决(执行环境块 `hostLocalPaths` 为假:路径不是本 worker 盘上的文件);认不出的词 ⇒ 走代理(代理 slot 空 ⇒ 写门 fail-closed)。 */
106
107
  sandboxPathAdjudication: DeploymentShapePredicate<"lane">;
107
108
  /** 读能力表 `sandboxSendUserFile` 位(e2b / k8s 每任务一只沙箱 ⇒ 任何租户;ssh 共享一台真机 ⇒ 只单主)。 */
108
109
  sandboxSendUserFile: DeploymentShapePredicate<"lane" | "principal">;
@@ -111,13 +112,13 @@ export declare const DEPLOYMENT_SHAPE_PREDICATES: Readonly<{
111
112
  /** 读能力表 `tenantSeparation` 位(值来自 settings-schema 车道词表正本);唯一读者 = 跨域不变量 `multi-tenant-shared-lane`。 */
112
113
  laneSeparatesTenants: DeploymentShapePredicate<"lane" | "principal">;
113
114
  /**
114
- * [ref] —— **VM 级**隔离强度(独立内核:e2b 的 Firecracker microVM;k8s 只在 Kata / gVisor 运行时类上)。与能力表 `isolation` 位
115
+ * [ref] —— **VM 级**隔离强度(独立内核:e2b 的 Firecracker microVM;k8s 只在 Kata / gVisor 运行时类上)。与执行环境块 `isolation` 键
115
116
  * (OS 级边界,容器即算;wire `executionLane.isolated` 的属主 `laneIsolated`)是两个问题,所以不叫「isolated」。读者:A 值路由。
116
117
  */
117
118
  vmIsolatedHands: DeploymentShapePredicate<"lane">;
118
119
  /**
119
- * [ref] —— 执行环境可挂起:能力表 `suspendable` 位(「结构上能不能」)∧ k8s 的前提旋钮(配了 S3 快照店)。真作者是各适配器对 core 的
120
- * `capabilities.suspendable` 声明(对账:`test/execution-lane-caps.test.ts` 拿真适配器实例逐车道核)。读者:boot `remote_exec_enabled.suspendable`。
120
+ * 执行环境可挂起 = 这份部署上执行环境块的 `suspendable`(唯一作者 `capabilitiesFor`,k8s 的快照店是它的输入;适配器的块是同一只函数铸的)。
121
+ * 读者:boot `remote_exec_enabled.suspendable`。
121
122
  */
122
123
  suspendableHands: DeploymentShapePredicate<"lane">;
123
124
  /** 读者两处:`config.ts` 车道落点一行的 `source`(覆写之前)与 `boot/execution-env.ts` 的 `remote_exec_real_system.note`(覆写之后)—— 同一个记号、两个时刻。 */
@@ -1,4 +1,4 @@
1
- import { executionLaneCaps, executionLaneOf, handsOnWorkerFilePlane } from "./execution-lane-caps.js";
1
+ import { capabilitiesFor, executionLaneCaps, executionLaneOf, handsOnWorkerFilePlane } from "./execution-lane-caps.js";
2
2
  function readPrincipalAxis(deployment) {
3
3
  return { declared: deployment.requirePrincipal === true };
4
4
  }
@@ -8,8 +8,8 @@ function readLaneAxis(deployment) {
8
8
  return {
9
9
  lane,
10
10
  caps: lane === undefined ? undefined : executionLaneCaps(lane),
11
+ block: capabilitiesFor(deployment),
11
12
  k8sRuntimeClass: lane === "k8s" ? declaration?.runtimeClass : undefined,
12
- k8sSnapshotStore: lane === "k8s" && Boolean(declaration?.s3Snapshot),
13
13
  singleMachineDefault: lane === "host" && declaration?.singleMachineDefault === true,
14
14
  };
15
15
  }
@@ -54,7 +54,7 @@ export const DEPLOYMENT_SHAPE_PREDICATES = Object.freeze({
54
54
  toolsOnThisHost: createPredicate(["lane"], "车道:工具跑的那台机器就是本进程这台(手挂上了且文件平面在本 worker 盘上;车道列 handsOnWorkerFilePlane)。", ({ lane }) => lane.lane !== undefined && handsOnWorkerFilePlane(lane.lane)),
55
55
  inProcessSingleUser: createPredicate(["lane", "principal"], "车道 × 租户:进程内车道(REMOTE_EXEC 未设)且部署只有一个主人。", ({ lane, principal }) => lane.lane === "in-process" && !principal.declared),
56
56
  taskHostPostureDeclarations: createPredicate(["lane", "principal"], "车道 × 租户 / 设备归属:任务层可以替部署声明宿主姿态 —— device 车道(发起者自己的设备),或单主部署。", ({ lane, principal }) => lane.lane === "device" || !principal.declared),
57
- sandboxPathAdjudication: createPredicate(["lane"], "车道:写门的路径事实要经代理到执行环境里取(文件平面不在本 worker 盘上;认不出的车道也走代理)。", ({ lane }) => lane.caps === undefined || lane.caps.hostFilePlane !== "supported"),
57
+ sandboxPathAdjudication: createPredicate(["lane"], "车道:写门的路径事实要经代理到执行环境里取(文件平面不在本 worker 盘上;认不出的车道也走代理)。", ({ lane }) => lane.block === undefined || !lane.block.hostLocalPaths),
58
58
  sandboxSendUserFile: createPredicate(["lane", "principal"], "车道 × 租户:SendUserFile 的沙箱直传腿可以挂(沙箱盘 = 租户自己的任务工作区,或共享真机上的单主部署)。", ({ lane, principal }) => lane.caps !== undefined && capabilityHolds(lane.caps.sandboxSendUserFile, principal)),
59
59
  remoteScratchpad: createPredicate(["lane"], "车道:执行环境里按会话分的远端 scratchpad 约定适用。", ({ lane }) => lane.caps !== undefined && lane.caps.remoteScratchpad === "supported"),
60
60
  laneSeparatesTenants: createPredicate(["lane", "principal"], "车道 × 租户:这条车道在这台部署上不让两个租户的命令落在同一个共享目标上(单主部署恒成立;认不出的车道不成立)。", ({ lane, principal }) => lane.caps !== undefined && capabilityHolds(lane.caps.tenantSeparation, principal)),
@@ -66,7 +66,7 @@ export const DEPLOYMENT_SHAPE_PREDICATES = Object.freeze({
66
66
  const runtimeClass = (lane.k8sRuntimeClass ?? "").trim().toLowerCase();
67
67
  return runtimeClass.startsWith("kata") || VM_ISOLATION_RUNTIME_CLASSES.has(runtimeClass);
68
68
  }),
69
- suspendableHands: createPredicate(["lane"], "车道:执行环境可挂起(e2b;k8s 以配了 S3 工作区快照店为前提)。", ({ lane }) => lane.caps !== undefined && lane.caps.suspendable === "supported" && (lane.lane !== "k8s" || lane.k8sSnapshotStore)),
69
+ suspendableHands: createPredicate(["lane"], "车道:执行环境可挂起(e2b;k8s 以配了 S3 工作区快照店为前提)。", ({ lane }) => lane.block?.suspendable === true),
70
70
  singleMachineDefaultLane: createPredicate(["lane"], "车道:host 车道是单机形缺省落点(REMOTE_EXEC 未设 ∧ CONFIG_PROVIDER=local,没人点名)。", ({ lane }) => lane.singleMachineDefault),
71
71
  });
72
72
  //# sourceMappingURL=deployment-shape.js.map
@@ -18,12 +18,21 @@
18
18
  * 上是**同一个** `device.enrollment_invalid`;设备读面的「不存在」与「不是你的」是同一个
19
19
  * `not_found.device`。细分只进审计。
20
20
  *
21
+ * ── 绑定从哪读(一只读)─────────────────────────────────────────────────────────────────────────────
22
+ * 根会话 ↔ 设备的绑定是根会话行上这一位住客的执行格 writer(device, D)(`execution-placement.ts` 头注)。准入链**不另读**:
23
+ * 读只有会话店的执行席 `seat.read(root)` 一只({@link DeviceEnrollment.readPlacement},装配点把会话店的执行席交进来 ——
24
+ * device 车道只在 SQL 后端 ⇒ 席恒在,缺 ⇒ 装配期响亮抛);{@link DeviceEnrollment.admitSession} 判的是调用方交进来的**那一读**,
25
+ * 于是提交意图「判已绑 / 未绑」与它记下的住客出自同一次行读。「这一格放在哪台设备上」只有一种读法 `boundDeviceOf`。
26
+ *
21
27
  * ── 吊销在途语义(§8-R4)──────────────────────────────────────────────────────────────────────────
22
28
  * `revoked` 是终态:①绑定的 session 后续提交 = `device.revoked`(admitSession 步 2,**先于** presence
23
29
  * 判定);②吊销同事务把连接租约作废并把 `conn_generation` 推进一格 ⇒ 在途连接的每一次带世代谓词的写
24
30
  * 都会影响 0 行,设备侧因此立刻失权(§4.6 步 2);③重新 enrollment = 新 deviceId,不复活旧行。
25
31
  */
26
- import { type DeviceErrorCode, type DeviceOwner, type DeviceRejectCode, type DeviceEnrollTokenRow, type DeviceRow, type DeviceSessionRow, type DeviceStore, type NewDeviceInput } from "./device-store.js";
32
+ import { type DeviceErrorCode, type DeviceOwner, type DeviceRejectCode, type DeviceEnrollTokenRow, type DeviceRow, type DeviceStore, type NewDeviceInput } from "./device-store.js";
33
+ import { type ExecutionDeviceCell, type ExecutionPlacementReading, type ExecutionPlacementSeat } from "./execution-placement.js";
34
+ import type { ExecutionLane } from "./execution-lane-caps.js";
35
+ import type { SessionFace } from "./session-incarnation.js";
27
36
  /** typed 拒绝 —— 端点层只负责把它翻成 HTTP(status 已在体内,不许调用方另算)。 */
28
37
  export interface DeviceRejection {
29
38
  /** [ref]:含两个借用位(`limit.rate_exceeded` 429 / `draining` 503);enrollment 本文件只铸前者,
@@ -68,10 +77,34 @@ export interface EnrollDeviceValue {
68
77
  }
69
78
  export interface AdmitSessionInput {
70
79
  rootSessionId: string;
80
+ /** 调用方读的**那一读**(会话店执行席 `seat.read(rootSessionId)` 的答案,经 {@link DeviceEnrollment.readPlacement})—— 准入链只判它,不另读。 */
81
+ placement: ExecutionPlacementReading | undefined;
71
82
  requester: DeviceOwner;
72
83
  /** 提交体携带的 deviceId(§4.3.2:仅根任务携;缺席 = 沿用绑定)。 */
73
84
  requestedDeviceId?: string;
74
85
  }
86
+ /**
87
+ * 首绑({@link DeviceEnrollment.bindPlacement})的结果:成立(`device` = 落下的格)、设备闭集的 typed 拒(异设备 `device.claim_conflict` /
88
+ * 设备 404 / 已吊销),或会话行那一侧的三种结局 —— 住客不是判过的那一位 / 行不在(`gone`)、写者在别处(`mirror`)、格在别的车道上(`tree_bound`)。
89
+ * 后三者不在设备闭集里(它们说的是会话,不是设备),由提交点(`http/execution-context.ts` 的 `commitExecutionContext`)各答各的码。
90
+ */
91
+ export type BindPlacementOutcome = {
92
+ readonly ok: true;
93
+ readonly device: ExecutionDeviceCell;
94
+ } | {
95
+ readonly ok: false;
96
+ readonly reject: DeviceRejection;
97
+ } | {
98
+ readonly ok: false;
99
+ readonly placement: "gone";
100
+ } | {
101
+ readonly ok: false;
102
+ readonly placement: "mirror";
103
+ } | {
104
+ readonly ok: false;
105
+ readonly placement: "tree_bound";
106
+ readonly boundLane: ExecutionLane;
107
+ };
75
108
  export interface AdmitSessionValue {
76
109
  deviceId: string;
77
110
  device: DeviceRow;
@@ -110,11 +143,15 @@ export interface DeviceEnrollment {
110
143
  /** §7-T5 五件的第 ①②③ 件的**写入侧**:绑定复合身份 + TTL + 一次性摘要落库(明文一现)。 */
111
144
  issueEnrollToken(input: IssueEnrollTokenRequest): Promise<DeviceOutcome<IssuedEnrollToken>>;
112
145
  enroll(input: EnrollDeviceInput): Promise<DeviceOutcome<EnrollDeviceValue>>;
146
+ /** 绑定的唯一读:会话店执行席的 `read(root)`(一次行读:住客 + 格;行不在 ⇒ `undefined`;读故障抛,不吞成「未绑」)。 */
147
+ readPlacement(rootSessionId: string): Promise<ExecutionPlacementReading | undefined>;
148
+ /** 首绑(认领之后的初始放置写):按意图那一读的住客定址,设备店一只事务。 */
113
149
  bindPlacement(input: {
114
150
  rootSessionId: string;
151
+ occupant: SessionFace;
115
152
  deviceId: string;
116
153
  requester: DeviceOwner;
117
- }): Promise<DeviceOutcome<DeviceSessionRow>>;
154
+ }): Promise<BindPlacementOutcome>;
118
155
  admitSession(input: AdmitSessionInput): Promise<DeviceOutcome<AdmitSessionValue>>;
119
156
  revoke(input: {
120
157
  deviceId: string;
@@ -125,6 +162,8 @@ export interface DeviceEnrollment {
125
162
  }
126
163
  export interface DeviceEnrollmentDeps {
127
164
  store: DeviceStore;
165
+ /** 会话店的执行席(绑定的唯一载体与唯一读;装配点交会话店的 `executionPlacement`)。 */
166
+ seat: ExecutionPlacementSeat;
128
167
  /** 缺席 = 不限速(端点车必须传;见文件头注 ⑤)。 */
129
168
  rateLimiter?: DeviceEnrollRateLimiter;
130
169
  /** 测试可注入确定性铸造;默认 `mintDeviceId()`。 */
@@ -1,4 +1,5 @@
1
1
  import { DEVICE_ERROR_CODES, DEVICE_ERROR_STATUS, DEVICE_RATE_LIMIT_CODE, assertDeviceIdShape, assertDeviceOwnerShape, assertDevicePubkeyShape, assertEnrollTokenHashShape, assertRootSessionIdShape, hashEnrollToken, mintDeviceId, mintEnrollToken, } from "./device-store.js";
2
+ import { boundDeviceOf } from "./execution-placement.js";
2
3
  export const DEFAULT_ENROLL_TOKEN_TTL_SEC = 900;
3
4
  export function buildDeviceRejection(errorCode, message) {
4
5
  return { code: errorCode, status: DEVICE_ERROR_STATUS[errorCode], message };
@@ -13,7 +14,7 @@ function rejectNotFound() {
13
14
  return reject(DEVICE_ERROR_CODES.NOT_FOUND, "device not found");
14
15
  }
15
16
  export function createDeviceEnrollment(deps) {
16
- const { store } = deps;
17
+ const { store, seat } = deps;
17
18
  const mintId = deps.mintId ?? mintDeviceId;
18
19
  async function rateGate(key) {
19
20
  if (!deps.rateLimiter)
@@ -87,21 +88,35 @@ export function createDeviceEnrollment(deps) {
87
88
  return rejectEnrollment();
88
89
  }
89
90
  }
91
+ async function readPlacement(rootSessionId) {
92
+ assertRootSessionIdShape(rootSessionId);
93
+ return seat.read(rootSessionId);
94
+ }
90
95
  async function bindPlacement(input) {
91
96
  assertRootSessionIdShape(input.rootSessionId);
92
97
  assertDeviceIdShape(input.deviceId);
93
98
  assertDeviceOwnerShape(input.requester);
94
- const result = await store.bindSession({ rootSessionId: input.rootSessionId, deviceId: input.deviceId, owner: input.requester });
99
+ const result = await store.placeDeviceExecution({ root: input.rootSessionId, occupant: input.occupant, deviceId: input.deviceId, owner: input.requester });
95
100
  switch (result.outcome) {
96
101
  case "bound":
97
- case "idempotent":
98
- return { ok: true, value: result.row };
99
- case "conflict":
102
+ case "same":
103
+ return { ok: true, device: result.device };
104
+ case "device_conflict":
100
105
  return reject(DEVICE_ERROR_CODES.CLAIM_CONFLICT, "this session is already bound to a different device");
101
106
  case "device_not_found":
102
107
  return rejectNotFound();
103
108
  case "device_revoked":
104
109
  return reject(DEVICE_ERROR_CODES.REVOKED, "device has been revoked");
110
+ case "gone":
111
+ return { ok: false, placement: "gone" };
112
+ case "mirror":
113
+ return { ok: false, placement: "mirror" };
114
+ case "tree_bound":
115
+ return { ok: false, placement: "tree_bound", boundLane: result.boundLane };
116
+ default: {
117
+ const unhandled = result;
118
+ throw new Error(`bindPlacement: unknown store outcome ${JSON.stringify(unhandled)}`);
119
+ }
105
120
  }
106
121
  }
107
122
  async function admitSession(input) {
@@ -109,22 +124,22 @@ export function createDeviceEnrollment(deps) {
109
124
  assertDeviceOwnerShape(input.requester);
110
125
  if (input.requestedDeviceId !== undefined)
111
126
  assertDeviceIdShape(input.requestedDeviceId);
112
- const binding = await store.getSessionBinding(input.rootSessionId);
113
- if (!binding)
127
+ const bound = input.placement === undefined ? undefined : boundDeviceOf(input.placement.cell);
128
+ if (!bound)
114
129
  return reject(DEVICE_ERROR_CODES.NOT_BOUND, "this session has no device binding");
115
- if (binding.owner.tenant !== input.requester.tenant || binding.owner.subject !== input.requester.subject)
130
+ if (bound.owner.tenant !== input.requester.tenant || bound.owner.subject !== input.requester.subject)
116
131
  return rejectNotFound();
117
- if (input.requestedDeviceId !== undefined && input.requestedDeviceId !== binding.deviceId) {
132
+ if (input.requestedDeviceId !== undefined && input.requestedDeviceId !== bound.id) {
118
133
  return reject(DEVICE_ERROR_CODES.IDENTITY_MISMATCH, "the requested device is not the one bound to this session");
119
134
  }
120
- const view = await store.readDeviceForAdmission(binding.deviceId, input.requester);
135
+ const view = await store.readDeviceForAdmission(bound.id, input.requester);
121
136
  if (!view)
122
137
  return rejectNotFound();
123
138
  if (view.row.status === "revoked")
124
139
  return reject(DEVICE_ERROR_CODES.REVOKED, "device has been revoked");
125
140
  if (!view.online)
126
141
  return reject(DEVICE_ERROR_CODES.OFFLINE, "device is not connected");
127
- return { ok: true, value: { deviceId: binding.deviceId, device: view.row, connGeneration: view.row.connGeneration } };
142
+ return { ok: true, value: { deviceId: bound.id, device: view.row, connGeneration: view.row.connGeneration } };
128
143
  }
129
144
  async function revoke(input) {
130
145
  assertDeviceIdShape(input.deviceId);
@@ -138,7 +153,7 @@ export function createDeviceEnrollment(deps) {
138
153
  return rejectNotFound();
139
154
  }
140
155
  }
141
- return { issueEnrollToken, enroll, bindPlacement, admitSession, revoke };
156
+ return { issueEnrollToken, enroll, readPlacement, bindPlacement, admitSession, revoke };
142
157
  }
143
158
  export function createFixedWindowRateLimiter(opts) {
144
159
  const now = opts.now ?? (() => Date.now());