@sema-agent/server 7.139.0 → 7.141.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 (328) hide show
  1. package/MIGRATION.md +471 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +94 -26
  5. package/dist/adoption/runner.js +1 -1
  6. package/dist/adoption/sql.js +1 -1
  7. package/dist/approval-batch-census.d.ts +27 -0
  8. package/dist/approval-batch-census.js +27 -0
  9. package/dist/approval-card.d.ts +37 -22
  10. package/dist/approval-card.js +6 -3
  11. package/dist/approval.d.ts +15 -0
  12. package/dist/approval.js +10 -0
  13. package/dist/audit.d.ts +4 -2
  14. package/dist/audit.js +2 -2
  15. package/dist/bench/s1/live-deps.js +3 -2
  16. package/dist/bench/s1/row.d.ts +5 -2
  17. package/dist/bench/s1/row.js +2 -22
  18. package/dist/boot/adoption.js +1 -1
  19. package/dist/boot/device-lane.d.ts +11 -0
  20. package/dist/boot/device-lane.js +16 -0
  21. package/dist/boot/engine-lease.d.ts +24 -3
  22. package/dist/boot/engine-lease.js +9 -2
  23. package/dist/boot/execution-env.js +18 -12
  24. package/dist/boot/execution-lane.d.ts +8 -0
  25. package/dist/boot/execution-lane.js +6 -1
  26. package/dist/boot/memory-boundary.d.ts +16 -53
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +16 -12
  29. package/dist/boot/memory-consolidation.js +24 -13
  30. package/dist/boot/reapers.d.ts +2 -2
  31. package/dist/boot/resolve-spec.d.ts +3 -5
  32. package/dist/boot/resolve-spec.js +9 -8
  33. package/dist/boot/runner-deps.d.ts +3 -3
  34. package/dist/boot/runner-deps.js +1 -0
  35. package/dist/boot/session-faces.d.ts +1 -1
  36. package/dist/boot/session-faces.js +4 -3
  37. package/dist/boot/shutdown.js +5 -0
  38. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  39. package/dist/boot/stage-03-store-backend.js +2 -3
  40. package/dist/boot/stage-04-stores.d.ts +2 -6
  41. package/dist/boot/stage-04-stores.js +5 -5
  42. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  43. package/dist/boot/stage-05-execution-env.js +3 -1
  44. package/dist/boot/stage-06-runners.d.ts +2 -8
  45. package/dist/boot/stage-06-runners.js +3 -3
  46. package/dist/boot/stage-07-capability-layer.d.ts +2 -7
  47. package/dist/boot/stage-07-capability-layer.js +8 -10
  48. package/dist/boot/stage-08-reapers.d.ts +2 -7
  49. package/dist/boot/stage-09-leader.d.ts +2 -7
  50. package/dist/boot/stage-10-http-server.d.ts +2 -7
  51. package/dist/boot/stage-10-http-server.js +6 -4
  52. package/dist/boot/stores.d.ts +3 -6
  53. package/dist/boot/stores.js +37 -44
  54. package/dist/boot/task-list-lane.d.ts +1 -1
  55. package/dist/boot/task-list-lane.js +1 -1
  56. package/dist/boot/web-admin.js +36 -9
  57. package/dist/capabilities/execution-lane.d.ts +2 -2
  58. package/dist/capabilities/scenarios.d.ts +1 -1
  59. package/dist/capability-facts.d.ts +9 -0
  60. package/dist/capability-facts.js +9 -0
  61. package/dist/card-void.d.ts +38 -0
  62. package/dist/card-void.js +23 -0
  63. package/dist/config-catalog.js +12 -8
  64. package/dist/config-provider.d.ts +18 -17
  65. package/dist/config-provider.js +28 -36
  66. package/dist/config-types.d.ts +36 -9
  67. package/dist/config.js +56 -46
  68. package/dist/delegated-session-owner.d.ts +23 -20
  69. package/dist/delegated-session-owner.js +2 -2
  70. package/dist/deployment-shape.d.ts +9 -8
  71. package/dist/deployment-shape.js +4 -4
  72. package/dist/device-store.d.ts +7 -0
  73. package/dist/device-store.js +3 -0
  74. package/dist/execution-config-root.d.ts +22 -0
  75. package/dist/execution-config-root.js +35 -0
  76. package/dist/execution-lane-caps.d.ts +145 -107
  77. package/dist/execution-lane-caps.js +144 -83
  78. package/dist/execution-placement.d.ts +3 -0
  79. package/dist/execution-placement.js +7 -0
  80. package/dist/fleet/drain-entry.d.ts +3 -0
  81. package/dist/hosted-posture.d.ts +8 -0
  82. package/dist/hosted-posture.js +4 -0
  83. package/dist/http/admission.js +3 -3
  84. package/dist/http/caller-trust.d.ts +3 -2
  85. package/dist/http/caller-trust.js +2 -1
  86. package/dist/http/dispatch.js +4 -0
  87. package/dist/http/live-frame.d.ts +24 -0
  88. package/dist/http/live-frame.js +16 -0
  89. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  90. package/dist/http/memory-compliance-failure-reply.js +5 -0
  91. package/dist/http/memory-layer-reply.d.ts +30 -0
  92. package/dist/http/memory-layer-reply.js +24 -0
  93. package/dist/http/resume-legs.js +49 -35
  94. package/dist/http/retired-service-deps.d.ts +1 -1
  95. package/dist/http/retired-service-deps.js +1 -1
  96. package/dist/http/route-table.js +2 -0
  97. package/dist/http/routes/adoption.js +1 -1
  98. package/dist/http/routes/approvals-assistant.js +1 -0
  99. package/dist/http/routes/capabilities.js +5 -4
  100. package/dist/http/routes/diagnostics.d.ts +0 -19
  101. package/dist/http/routes/diagnostics.js +16 -2
  102. package/dist/http/routes/memory-bundle.d.ts +2 -2
  103. package/dist/http/routes/memory-bundle.js +4 -1
  104. package/dist/http/routes/memory-compliance.js +9 -2
  105. package/dist/http/routes/memory-consolidation.js +12 -1
  106. package/dist/http/routes/memory-origin.js +14 -2
  107. package/dist/http/routes/memory-policy.js +7 -2
  108. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  109. package/dist/http/routes/run-permission-mode.js +170 -0
  110. package/dist/http/routes/session-sync.d.ts +3 -6
  111. package/dist/http/routes/session-sync.js +7 -2
  112. package/dist/http/routes/sessions.js +33 -15
  113. package/dist/http/routes/tasks.js +67 -68
  114. package/dist/http/run-read-face.js +3 -3
  115. package/dist/http/send.d.ts +1 -1
  116. package/dist/http/send.js +2 -2
  117. package/dist/http/server.d.ts +14 -27
  118. package/dist/http/server.js +2 -2
  119. package/dist/http/sse-log.d.ts +3 -8
  120. package/dist/http/stored-body-replay.d.ts +3 -3
  121. package/dist/http/stored-body-replay.js +3 -1
  122. package/dist/identity-source.d.ts +1 -1
  123. package/dist/identity-source.js +1 -1
  124. package/dist/instruction-source-notice.d.ts +7 -0
  125. package/dist/instruction-source-notice.js +6 -6
  126. package/dist/lane-home-probe.d.ts +68 -42
  127. package/dist/lane-home-probe.js +53 -30
  128. package/dist/leader/wire.js +5 -4
  129. package/dist/mcp-assertion-header.d.ts +4 -0
  130. package/dist/mcp-assertion-header.js +5 -0
  131. package/dist/memory-bundle-engine.d.ts +16 -9
  132. package/dist/memory-bundle-engine.js +29 -6
  133. package/dist/memory-operator-faces.d.ts +85 -30
  134. package/dist/memory-operator-faces.js +136 -30
  135. package/dist/memory-posture.d.ts +1 -1
  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 +33 -9
  141. package/dist/memory-scope.js +17 -5
  142. package/dist/memory-sync-client.d.ts +34 -9
  143. package/dist/memory-sync-client.js +108 -28
  144. package/dist/observability/fail-open.d.ts +30 -2
  145. package/dist/observability/fail-open.js +30 -2
  146. package/dist/observability/metrics.js +0 -1
  147. package/dist/observability/run-terminal.d.ts +6 -2
  148. package/dist/observability/run-terminal.js +25 -26
  149. package/dist/observability/secret-env-scrub.d.ts +20 -1
  150. package/dist/observability/secret-env-scrub.js +14 -0
  151. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  152. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  153. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  154. package/dist/orchestration/workflow-notify-journal.js +2 -1
  155. package/dist/permission-mode-admission.d.ts +68 -0
  156. package/dist/permission-mode-admission.js +23 -0
  157. package/dist/permission-rule-list.d.ts +55 -6
  158. package/dist/permission-rule-list.js +52 -5
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-sql.js +14 -14
  161. package/dist/plugins/approval-nonce-store.js +4 -4
  162. package/dist/plugins/blob-backend.d.ts +4 -4
  163. package/dist/plugins/blob-backend.js +7 -7
  164. package/dist/plugins/breaker-state-sql.js +2 -2
  165. package/dist/plugins/caching-session-store.d.ts +6 -0
  166. package/dist/plugins/caching-session-store.js +3 -0
  167. package/dist/plugins/checkpoint-index-columns.js +2 -2
  168. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  169. package/dist/plugins/checkpoint-store-sql.js +90 -19
  170. package/dist/plugins/device-store-sql.d.ts +5 -2
  171. package/dist/plugins/device-store-sql.js +23 -20
  172. package/dist/plugins/ensure-index.d.ts +1 -2
  173. package/dist/plugins/execution-placement-sql.d.ts +19 -9
  174. package/dist/plugins/execution-placement-sql.js +20 -9
  175. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  176. package/dist/plugins/file-history-store-sql.js +4 -4
  177. package/dist/plugins/file-run-store.d.ts +2 -2
  178. package/dist/plugins/file-run-store.js +1 -1
  179. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  180. package/dist/plugins/fork-routing-session-store.js +2 -61
  181. package/dist/plugins/image-bake-store-sql.js +9 -9
  182. package/dist/plugins/image-index-sql.js +11 -11
  183. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  184. package/dist/plugins/k8s-exec-protocol.js +6 -1
  185. package/dist/plugins/leader-run-store-sql.js +4 -4
  186. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  187. package/dist/plugins/local-checkpoint-store.js +22 -0
  188. package/dist/plugins/local-session-store.d.ts +227 -35
  189. package/dist/plugins/local-session-store.js +518 -37
  190. package/dist/plugins/local-session-window.d.ts +17 -3
  191. package/dist/plugins/local-session-window.js +7 -13
  192. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  193. package/dist/plugins/mailbox-store-sql.js +4 -4
  194. package/dist/plugins/memory-engine-tidb.js +5 -5
  195. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  196. package/dist/plugins/memory-run-store.d.ts +2 -2
  197. package/dist/plugins/memory-run-store.js +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +52 -11
  200. package/dist/plugins/one-time-migrations.js +101 -39
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.js +2 -2
  203. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  204. package/dist/plugins/permission-rule-store-sql.js +16 -16
  205. package/dist/plugins/pg-pool.js +3 -2
  206. package/dist/plugins/pg-session-storage.d.ts +2 -0
  207. package/dist/plugins/pg-session-storage.js +3 -2
  208. package/dist/plugins/remote-env-adb.d.ts +7 -24
  209. package/dist/plugins/remote-env-adb.js +22 -5
  210. package/dist/plugins/remote-env-device.d.ts +2 -28
  211. package/dist/plugins/remote-env-device.js +2 -1
  212. package/dist/plugins/remote-env-e2b.d.ts +40 -27
  213. package/dist/plugins/remote-env-e2b.js +180 -20
  214. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  215. package/dist/plugins/remote-env-file-error.js +8 -2
  216. package/dist/plugins/remote-env-host.d.ts +19 -35
  217. package/dist/plugins/remote-env-host.js +26 -3
  218. package/dist/plugins/remote-env-k8s.d.ts +22 -14
  219. package/dist/plugins/remote-env-k8s.js +150 -26
  220. package/dist/plugins/remote-env-local-docker.d.ts +27 -24
  221. package/dist/plugins/remote-env-local-docker.js +91 -24
  222. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  223. package/dist/plugins/remote-env-ssh.js +4 -3
  224. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  225. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  226. package/dist/plugins/retention-lane-store-sql.js +2 -2
  227. package/dist/plugins/retention-store-sql.d.ts +1 -1
  228. package/dist/plugins/retention-store-sql.js +6 -6
  229. package/dist/plugins/run-store-sql.d.ts +3 -3
  230. package/dist/plugins/run-store-sql.js +19 -18
  231. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  232. package/dist/plugins/sandbox-read-floor.js +608 -0
  233. package/dist/plugins/schema-consistency.d.ts +6 -6
  234. package/dist/plugins/schema-consistency.js +1 -1
  235. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  236. package/dist/plugins/schema-ensure-legs.js +2 -2
  237. package/dist/plugins/schema-live-columns.d.ts +1 -1
  238. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  239. package/dist/plugins/session-ownership.d.ts +2 -1
  240. package/dist/plugins/session-placement.d.ts +17 -4
  241. package/dist/plugins/session-placement.js +2 -1
  242. package/dist/plugins/session-rules-column.d.ts +1 -1
  243. package/dist/plugins/session-rules-column.js +1 -1
  244. package/dist/plugins/session-store.d.ts +18 -20
  245. package/dist/plugins/session-store.js +4 -12
  246. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  247. package/dist/plugins/session-workspace-cell.js +1 -1
  248. package/dist/plugins/shared-memory-store-sql.js +2 -2
  249. package/dist/plugins/sql-driver.d.ts +10 -5
  250. package/dist/plugins/sql-driver.js +6 -6
  251. package/dist/plugins/sql-errors.js +1 -1
  252. package/dist/plugins/store-backend.d.ts +35 -6
  253. package/dist/plugins/store-backend.js +27 -10
  254. package/dist/plugins/store-contracts.d.ts +9 -2
  255. package/dist/plugins/task-list-store-sql.js +11 -11
  256. package/dist/plugins/tidb-pool.js +7 -6
  257. package/dist/plugins/tidb-session-storage.js +4 -4
  258. package/dist/plugins/tidb-session-store.d.ts +4 -1
  259. package/dist/plugins/tidb-session-store.js +16 -15
  260. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  261. package/dist/plugins/tool-result-store-sql.js +3 -3
  262. package/dist/plugins/user-input-ledger-sql.js +2 -2
  263. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  264. package/dist/plugins/workflow-run-store-sql.js +19 -19
  265. package/dist/plugins/write-behind-counter.d.ts +2 -3
  266. package/dist/plugins/write-behind-counter.js +5 -5
  267. package/dist/resource-suspend.d.ts +14 -12
  268. package/dist/resource-suspend.js +1 -1
  269. package/dist/resource-window.d.ts +4 -3
  270. package/dist/run-cancel-context.d.ts +34 -0
  271. package/dist/run-cancel-context.js +7 -0
  272. package/dist/run-local.js +14 -19
  273. package/dist/runs.d.ts +47 -16
  274. package/dist/runs.js +112 -113
  275. package/dist/security.d.ts +2 -2
  276. package/dist/server-secret-env.d.ts +2 -1
  277. package/dist/server-secret-env.js +2 -4
  278. package/dist/session-error-wire.d.ts +22 -4
  279. package/dist/session-error-wire.js +16 -0
  280. package/dist/session-incarnation.d.ts +10 -4
  281. package/dist/session-incarnation.js +1 -1
  282. package/dist/session-policy-wire.d.ts +1 -1
  283. package/dist/session-sync-kernel.d.ts +42 -1
  284. package/dist/session-sync-kernel.js +17 -0
  285. package/dist/session-sync.js +16 -4
  286. package/dist/sql-engine-coordinates.d.ts +49 -0
  287. package/dist/sql-engine-coordinates.js +27 -0
  288. package/dist/sql-engine-posture.d.ts +1 -1
  289. package/dist/sql-engine-posture.js +1 -1
  290. package/dist/standing-answer-grant.d.ts +31 -2
  291. package/dist/standing-answer-grant.js +14 -2
  292. package/dist/store-posture.d.ts +1 -1
  293. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  294. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  295. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  296. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  297. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  298. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  299. package/dist/task-a2a.d.ts +1 -1
  300. package/dist/task-a2a.js +5 -6
  301. package/dist/task-mcp.d.ts +21 -8
  302. package/dist/task-mcp.js +17 -9
  303. package/dist/task-settings.d.ts +52 -25
  304. package/dist/task-settings.js +91 -28
  305. package/dist/terminal.d.ts +103 -19
  306. package/dist/terminal.js +72 -20
  307. package/dist/trace/core-keyset-guard.d.ts +79 -2
  308. package/dist/trace/exit-seat.d.ts +7 -0
  309. package/dist/trace/exit-seat.js +1 -0
  310. package/dist/trace/model-segment.d.ts +2 -2
  311. package/dist/trace/redact.d.ts +3 -1
  312. package/dist/trace/redact.js +395 -110
  313. package/dist/trace/self-minted-wire.d.ts +226 -2
  314. package/dist/trace/self-minted-wire.js +79 -0
  315. package/dist/verifier-pause-card.d.ts +8 -0
  316. package/dist/verifier-pause-card.js +19 -0
  317. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  318. package/dist/web-admin-client/apply-effective.js +2 -2
  319. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  320. package/dist/web-admin-client/candidate-gate.js +1 -1
  321. package/dist/web-admin-client/http-client.d.ts +5 -4
  322. package/dist/web-admin-client/http-client.js +2 -2
  323. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  324. package/dist/web-admin-client/read-warnings.js +5 -9
  325. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  326. package/package.json +2 -2
  327. package/wire-changes/server@7.140.0.json +184 -0
  328. package/wire-changes/server@7.141.0.json +367 -0
@@ -3,6 +3,8 @@ import { type SpaceCommitFence } from "../workspace-space-commit.js";
3
3
  import { type CacheMissMemory, type WorkspaceCacheCapability } from "../workspace-cache.js";
4
4
  import { type SandboxEgressGap, type SandboxIpv6 } from "../sandbox-egress-policy.js";
5
5
  import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, type BackgroundShellCapability, type BackgroundShellId, type BackgroundShellError, type BackgroundSpawnOptions, type BackgroundPoll, type ExecutionEnvCapabilities } from "@sema-agent/core";
6
+ import { type SandboxReadFloorKnob } from "../execution-lane-caps.js";
7
+ import { type SandboxReadFloor } from "./sandbox-read-floor.js";
6
8
  import type { PodSpecPatch } from "./k8s-exec-protocol.js";
7
9
  import { type LaneHomeConfig } from "../lane-home-probe.js";
8
10
  type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
@@ -162,6 +164,8 @@ export interface K8sEnvConfig extends LaneHomeConfig {
162
164
  readonly image: string;
163
165
  readonly origins: readonly string[];
164
166
  };
167
+ /** `SANDBOX_READ_FLOOR`(keep | off;缺席 ⇒ keep):本 pod 守不守读拒表的字节地板 = 读地板模块服务本车道 ∧ 本键不是 off(`sandbox-read-floor.ts`)。 */
168
+ sandboxReadFloor?: SandboxReadFloorKnob;
165
169
  }
166
170
  import { type SandboxSecretSeatKind, type SandboxSecretVerb } from "./k8s-exec-protocol.js";
167
171
  export { type PodSpecPatch, applyPodSpecPatch } from "./k8s-exec-protocol.js";
@@ -179,6 +183,8 @@ export interface ExecSocket {
179
183
  on(event: "open" | "message" | "close" | "error", listener: (...args: unknown[]) => void): void;
180
184
  close(): void;
181
185
  terminate?(): void;
186
+ /** 写一帧(读地板中继的 stdin:首字节 = 通道 0)。只读的 exec 不用;没有它的套接字起不了中继(附着按 `relay_silent` 拒)。 */
187
+ send?(data: Buffer): void;
182
188
  }
183
189
  /**
184
190
  * Test seam ([ref] §9.3 fault-injection): inject the REST `request` and/or the exec-socket factory, so pod
@@ -199,6 +205,8 @@ export interface K8sEnvDeps {
199
205
  connectOnce?: (signal?: AbortSignal) => Promise<ConnectResult>;
200
206
  /** Worker-side object-store requests (the space pointer, listing, deletes). Default: global `fetch`. */
201
207
  fetch?: typeof fetch;
208
+ /** 这只环境手里的读地板模块(缺席 ⇒ `createSandboxReadFloor` 按旋钮选;钉注入守地板的模块)。能力块的 `keepsReadFloor` 读它的状态。 */
209
+ readFloor?: SandboxReadFloor;
202
210
  }
203
211
  /**
204
212
  * One run's space occupant — the space key of both lanes keyed by it (the space lane, the cache volume) — a closed set of three
@@ -290,19 +298,10 @@ export type SandboxLeaseWrite = {
290
298
  readonly message: string;
291
299
  };
292
300
  export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, BackgroundShellCapability {
293
- /**
294
- * 🔴 **[ref] / core 7.20.0([ref])`canonicalPathAuthoritative: true`** —— 本适配器的 `canonicalPath`
295
- * 就是**命令真正跑的那块文件系统**的权威答案(`posix-shell-fs` 的 `canonicalPath` 原语,经 pod exec 解到落点)。声明它 = 告诉引擎:整链答不出来是**解析器出了事**
296
- * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
297
- * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
298
- * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
299
- * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
300
- * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
301
- * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
302
- */
303
- /** `sharesProcessTable`(core 7.57.0,必填):命令跑在沙箱 pod 自己的 PID 命名空间里(不开 hostPID),不在本进程所在的进程表。 */
304
- /** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
301
+ /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。快照店与读地板旋钮是它的输入(构造期铸,见下);{@link floor} 由同一只判据、同一个旋钮值选出。 */
305
302
  readonly capabilities: ExecutionEnvCapabilities;
303
+ /** 读拒表字节地板模块:pod 里每一次 exec 起动都先经它的 `wrap`({@link runExec},唯一接缝)。 */
304
+ private readonly floor;
306
305
  cwd: string;
307
306
  private podName?;
308
307
  /** Cluster-internal pod address, captured when the pod reaches Running — the LSP `getHost` surface.
@@ -327,9 +326,9 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
327
326
  * the space restore (`latest` names an archive that is there and did not come back — {@link restoreSnapshot}) and the pod's admission (the cluster created
328
327
  * the pod and it ended `Failed` before it ran — {@link createPod}). Neither goes away by asking again: the same request answers the same. */
329
328
  private connectRefusal?;
330
- /** core `ExecutionEnv.homeDir`([ref]):pod 里执行用户的 home —— 声明胜,否则 pod 就绪那一拍实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时 pod 起来之前缺席)。 */
329
+ /** core `ExecutionEnv.homeDir`([ref]):pod 里执行用户的 home —— 声明在场 ⇒ 声明(开环境那一拍与 `$HOME` 对证过),否则 pod 就绪那一拍实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时 pod 起来之前缺席)。 */
331
330
  get homeDir(): string | undefined;
332
- /** 家目录的座(声明胜 · 否则 pod 就绪那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。本车道对声明对证(与 local-docker / ssh 同一只判官、同一句拒)。 */
331
+ /** 家目录的座(声明在场 ⇒ 开环境那一拍对证 · 否则 pod 就绪那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。与其余读得出家目录的车道同一只判官、同一句拒。 */
333
332
  private readonly homeSeat;
334
333
  /** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在 pod 里(镜像与 pod spec 的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
335
334
  * 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
@@ -742,6 +741,15 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
742
741
  * being claimed) uploads nothing, and is that same upload failure.
743
742
  */
744
743
  private persistWorkspace;
744
+ /**
745
+ * 守地板的 pod 的二进制座:本包带的每个架构一只 ConfigMap(名 = 架构 + 摘要前 16 位,不可变;201 = 新建,409 = 同名同内容已在)。建不成
746
+ * ⇒ 告诉地板模块(附着按 `binary_digest` 拒,pod 照起、不带这只卷);不守地板 ⇒ `undefined`(pod 与改前逐字同)。部署前提:命名空间里 configmaps 的 create。
747
+ */
748
+ private ensureFloorSeat;
749
+ /** 地板模块的三件原语。引导命令经 {@link runExec}(起动形 `bootstrap`:只在附着中合法);放置由 pod 的只读卷完成(自检 `--digest` 证字节);
750
+ * 中继 = 一只开着 stdin 的长连 exec(与命令同一身份 —— k8s 的 exec 没有换身份的口),stdout 帧是中继的问,stdin 帧(首字节 0)是判官的答。 */
751
+ private floorTransport;
752
+ /** exec 的 URL:`argv` 原样作 `command` 参数(唯一来源 = {@link runExec} 里模块答案的编码,或地板模块自己的中继);`stdin` ⇒ 开 stdin 通道。 */
745
753
  private execUrl;
746
754
  /**
747
755
  * Run a command over the exec WebSocket. Frames: byte0=channel (1 stdout / 2 stderr / 3 status JSON with the
@@ -18,6 +18,8 @@ import { buildLivenessCut, commandBoundsOf, laneLivenessOf, LIVENESS_MS_DEFAULT
18
18
  import { buildCapturedOutput, createCutError } from "../exec-cut.js";
19
19
  import { SANDBOX_IPV6_DEFAULT, sandboxEgressGaps, sandboxPodSysctls } from "../sandbox-egress-policy.js";
20
20
  import { FileError, ExecutionError, RemoteExecutionError, withRetry, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
21
+ import { capabilitiesFor } from "../execution-lane-caps.js";
22
+ import { ADAPTER_START, BOOTSTRAP_START, buildFloorPodSeat, createSandboxReadFloor, FLOOR_K8S_CONFIGMAP_KEY, engineStart, floorArgv, } from "./sandbox-read-floor.js";
21
23
  import { fileErrorFromExec, classifyFsStderr } from "./remote-env-file-error.js";
22
24
  import { createPosixShellFs } from "./posix-shell-fs.js";
23
25
  import { randomUUID } from "node:crypto";
@@ -201,6 +203,7 @@ function brokerContainer(image) {
201
203
  }
202
204
  export class RemoteK8sExecutionEnv {
203
205
  capabilities;
206
+ floor;
204
207
  cwd;
205
208
  podName;
206
209
  podIP;
@@ -258,7 +261,7 @@ export class RemoteK8sExecutionEnv {
258
261
  ...(config.snapshotExclude && config.snapshotExclude.length > 0 ? { snapshotExclude: config.snapshotExclude } : {}),
259
262
  };
260
263
  this.cwd = this.cfg.mountPath;
261
- this.homeSeat = new LaneHomeSeat({ lane: PROVIDER, declared: config.homeDir, verifiesDeclared: true, onAbsent: config.onHomeAbsent });
264
+ this.homeSeat = new LaneHomeSeat(PROVIDER, config);
262
265
  this.deps = deps ?? {};
263
266
  this.laneLiveness = laneLivenessOf(this.cfg.livenessMs, "K8S_LIVENESS_MS");
264
267
  const occupant = occupantKeyOf(config.spaceOccupant);
@@ -268,7 +271,8 @@ export class RemoteK8sExecutionEnv {
268
271
  this.logger = config.logger;
269
272
  this.onWorkspaceSnapshot = config.onWorkspaceSnapshot;
270
273
  this.cacheMisses = config.cacheMisses;
271
- this.capabilities = { isolation: true, suspendable: !!this.cfg.s3Snapshot, canonicalPathAuthoritative: true, sharesProcessTable: false, hostLocalPaths: false, externalContentTarget: false, io: true, keepsReadFloor: false };
274
+ this.floor = this.deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor, ...(config.logger !== undefined ? { logger: config.logger } : {}) });
275
+ this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER, s3Snapshot: this.cfg.s3Snapshot }, sandboxReadFloor: config.sandboxReadFloor });
272
276
  const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
273
277
  this.backgroundCapabilities = {
274
278
  supported: true,
@@ -666,6 +670,8 @@ export class RemoteK8sExecutionEnv {
666
670
  ];
667
671
  const pkgSourceMarked = Boolean(this.cfg.podEnv?.SEMA_PKG_SOURCE || secretEnv.SEMA_PKG_SOURCE);
668
672
  const sysctls = sandboxPodSysctls(this.cfg.ipv6);
673
+ const floorSeat = await this.ensureFloorSeat(signal);
674
+ const mounts = [...(cache ? [{ name: CACHE_VOLUME, mountPath: cache.mount }] : []), ...(floorSeat?.mounts ?? [])];
669
675
  const pod = {
670
676
  apiVersion: "v1",
671
677
  kind: "Pod",
@@ -690,7 +696,7 @@ export class RemoteK8sExecutionEnv {
690
696
  ? ["sh", "-c", "[ -f /opt/sema/pkg-source.sh ] && sh /opt/sema/pkg-source.sh || true; sleep infinity"]
691
697
  : ["sh", "-c", "sleep infinity"],
692
698
  ...(envEntries.length > 0 ? { env: envEntries } : {}),
693
- ...(cache ? { volumeMounts: [{ name: CACHE_VOLUME, mountPath: cache.mount }] } : {}),
699
+ ...(mounts.length > 0 ? { volumeMounts: mounts } : {}),
694
700
  resources: { limits: { cpu: this.cfg.cpu, memory: this.cfg.memory } },
695
701
  },
696
702
  ...(broker !== undefined ? [brokerContainer(broker.image)] : []),
@@ -698,6 +704,7 @@ export class RemoteK8sExecutionEnv {
698
704
  ...((volumes) => (volumes.length > 0 ? { volumes } : {}))([
699
705
  ...(cache ? [{ name: CACHE_VOLUME, persistentVolumeClaim: { claimName: cache.claim.name } }] : []),
700
706
  ...(leaseName !== undefined ? [{ name: BROKER_VOLUME_NAME, secret: { secretName: leaseName, items: [{ key: BROKER_LEASE_KEY, path: BROKER_LEASE_KEY }], defaultMode: 0o400 } }] : []),
707
+ ...(floorSeat?.volumes ?? []),
701
708
  ]),
702
709
  },
703
710
  };
@@ -805,7 +812,7 @@ export class RemoteK8sExecutionEnv {
805
812
  this.podIP = podIP;
806
813
  this.leaseRef = leaseName !== undefined && broker !== undefined ? { namespace: ns, podName: name, leaseSecretName: leaseName, origins: [...broker.origins] } : undefined;
807
814
  const askHome = this.homeSeat.asks;
808
- const mk = await this.runExec(`mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
815
+ const mk = await this.runExec(ADAPTER_START, `mkdir -p ${shellQuote(this.cfg.mountPath)}${askHome ? ` && { ${LANE_HOME_PROBE_COMMAND} || true; }` : ""}`);
809
816
  if (!mk.ok || mk.value.exitCode !== 0) {
810
817
  await this.deletePod(name).catch(() => { });
811
818
  this.podName = undefined;
@@ -824,6 +831,7 @@ export class RemoteK8sExecutionEnv {
824
831
  }
825
832
  if (cache?.fresh)
826
833
  this.logger?.warn("workspace_cache_fresh", { space: incarnationLogPrefix(cache.spaceKey), claim: cache.claim.name, size: cache.size });
834
+ await this.floor.attach(this.floorTransport(), { origin: "created", workspaceRoot: this.cfg.mountPath });
827
835
  return ok(this.workspaceHandle());
828
836
  }
829
837
  async createPodSecret(secretName, podName, podUid, values, signal) {
@@ -1183,7 +1191,7 @@ export class RemoteK8sExecutionEnv {
1183
1191
  const excludes = (this.cfg.snapshotExclude ?? []).map((item) => ` --exclude=${shellQuote(`./${item}`)}`).join("");
1184
1192
  const cmd = `tar czf ${tmp}${excludes} -C ${shellQuote(this.cfg.mountPath)} . && curl -fsS -X PUT -T ${tmp} ${shellQuote(url)}; ` +
1185
1193
  `rc=$?; rm -f ${tmp}; exit $rc`;
1186
- const r = await this.runExec(cmd, { timeoutMs: boundMs });
1194
+ const r = await this.runExec(ADAPTER_START, cmd, { timeoutMs: boundMs });
1187
1195
  if (!r.ok)
1188
1196
  return { ok: false, error: new RemoteExecutionError("connect_failed", `workspace snapshot upload failed: ${this.redactUrl(r.error.message, url)}`, r.error) };
1189
1197
  if (r.value.exitCode !== 0)
@@ -1219,7 +1227,7 @@ export class RemoteK8sExecutionEnv {
1219
1227
  const c = await this.provision(abortSignal);
1220
1228
  if (!c.ok)
1221
1229
  return c;
1222
- const r = await this.runExec(cmd, { timeoutMs: this.cfg.dataTimeoutMs });
1230
+ const r = await this.runExec(ADAPTER_START, cmd, { timeoutMs: this.cfg.dataTimeoutMs });
1223
1231
  const failure = !r.ok
1224
1232
  ? { cls: r.error.code === "timeout" ? "timeout" : "transport", detail: `workspace snapshot restore failed: ${this.redactUrl(r.error.message, url)}`, cause: r.error }
1225
1233
  : r.value.exitCode !== 0
@@ -1325,6 +1333,7 @@ export class RemoteK8sExecutionEnv {
1325
1333
  }
1326
1334
  async destroy() {
1327
1335
  await this.disposeBackgroundShells().catch(() => { });
1336
+ this.floor.dispose();
1328
1337
  this.destroyed = true;
1329
1338
  const lane = this.spaceLane;
1330
1339
  switch (lane.state) {
@@ -1409,7 +1418,7 @@ export class RemoteK8sExecutionEnv {
1409
1418
  async killPodProcesses() {
1410
1419
  if (!this.podName)
1411
1420
  return false;
1412
- const r = await this.runExec(buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
1421
+ const r = await this.runExec(ADAPTER_START, buildKillAllScript(), { timeoutMs: this.cfg.rpcTimeoutMs + KILL_ALL_RECHECK_MS });
1413
1422
  return r.ok && r.value.exitCode === 0;
1414
1423
  }
1415
1424
  async persistWorkspace(space) {
@@ -1462,23 +1471,124 @@ export class RemoteK8sExecutionEnv {
1462
1471
  await this.endSpace(space);
1463
1472
  }
1464
1473
  }
1465
- execUrl(command) {
1474
+ async ensureFloorSeat(signal) {
1475
+ const seat = buildFloorPodSeat(this.floor);
1476
+ if (seat === undefined)
1477
+ return undefined;
1478
+ if (seat.configMaps.length === 0) {
1479
+ this.floor.refusePlacement("this server carries no pinned supervisor build");
1480
+ return undefined;
1481
+ }
1482
+ for (const cm of seat.configMaps) {
1483
+ const body = { apiVersion: "v1", kind: "ConfigMap", metadata: { name: cm.name }, immutable: true, binaryData: { [FLOOR_K8S_CONFIGMAP_KEY]: cm.binaryDataB64 } };
1484
+ let r;
1485
+ try {
1486
+ r = await this.request("POST", `/api/v1/namespaces/${this.cfg.namespace}/configmaps`, body, { signal });
1487
+ }
1488
+ catch (e) {
1489
+ this.floor.refusePlacement(`creating the supervisor ConfigMap ${cm.name} failed: ${errorMessageOf(e)}`);
1490
+ return undefined;
1491
+ }
1492
+ if (r.status !== 409 && (r.status < 200 || r.status >= 300)) {
1493
+ this.floor.refusePlacement(`creating the supervisor ConfigMap ${cm.name} was answered ${r.status}${r.status === 401 || r.status === 403 ? " (the server's service account needs create on configmaps in this namespace)" : ""}`);
1494
+ return undefined;
1495
+ }
1496
+ }
1497
+ return seat;
1498
+ }
1499
+ floorTransport() {
1500
+ return {
1501
+ bootstrap: async (command) => {
1502
+ const r = await this.runExec(BOOTSTRAP_START, command, { timeoutMs: this.cfg.rpcTimeoutMs });
1503
+ return r.ok ? { ok: true, exitCode: r.value.exitCode, stdout: r.value.stdout, stderr: r.value.stderr } : { ok: false, error: r.error.message };
1504
+ },
1505
+ place: async () => ({ ok: true }),
1506
+ openRelay: (identity, command, events) => {
1507
+ let closed = false;
1508
+ const close = (why) => {
1509
+ if (closed)
1510
+ return;
1511
+ closed = true;
1512
+ events.closed(why);
1513
+ };
1514
+ switch (identity) {
1515
+ case "root":
1516
+ queueMicrotask(() => close("a k8s exec cannot start the relay as root"));
1517
+ return { send: () => { }, close: () => { } };
1518
+ case "exec-user":
1519
+ break;
1520
+ }
1521
+ let sock;
1522
+ try {
1523
+ const { url, headers } = this.execUrl(["sh", "-c", command], true);
1524
+ sock = this.openExec(url, headers);
1525
+ }
1526
+ catch (e) {
1527
+ queueMicrotask(() => close(`the relay exec could not be opened: ${errorMessageOf(e)}`));
1528
+ return { send: () => { }, close: () => { } };
1529
+ }
1530
+ const decoder = new StringDecoder("utf8");
1531
+ let open = false;
1532
+ const pending = [];
1533
+ const write = (frame) => {
1534
+ if (sock.send === undefined)
1535
+ return close("the exec socket cannot write (no stdin channel)");
1536
+ try {
1537
+ sock.send(frame);
1538
+ }
1539
+ catch (e) {
1540
+ close(`writing to the relay failed: ${errorMessageOf(e)}`);
1541
+ }
1542
+ };
1543
+ sock.on("open", () => {
1544
+ open = true;
1545
+ for (const f of pending.splice(0))
1546
+ write(f);
1547
+ });
1548
+ sock.on("message", (...args) => {
1549
+ const data = args[0];
1550
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
1551
+ if (buf.length > 1 && buf[0] === 1)
1552
+ events.data(decoder.write(buf.subarray(1)));
1553
+ });
1554
+ sock.on("close", () => close("the relay exec closed"));
1555
+ sock.on("error", (e) => close(`the relay exec failed: ${errorMessageOf(e)}`));
1556
+ return {
1557
+ send: (line) => {
1558
+ const frame = Buffer.concat([Buffer.from([0]), Buffer.from(line, "utf8")]);
1559
+ if (open)
1560
+ write(frame);
1561
+ else
1562
+ pending.push(frame);
1563
+ },
1564
+ close: () => (sock.terminate ?? sock.close).call(sock),
1565
+ };
1566
+ },
1567
+ };
1568
+ }
1569
+ execUrl(argv, stdin = false) {
1466
1570
  const { apiUrl, token } = this.creds();
1467
1571
  const base = apiUrl.replace(/^http/, "ws");
1468
1572
  const q = new URLSearchParams();
1469
1573
  q.append("container", "sandbox");
1574
+ if (stdin)
1575
+ q.append("stdin", "true");
1470
1576
  q.append("stdout", "true");
1471
1577
  q.append("stderr", "true");
1472
- for (const part of ["sh", "-c", command])
1578
+ for (const part of argv)
1473
1579
  q.append("command", part);
1474
1580
  return { url: `${base}/api/v1/namespaces/${this.cfg.namespace}/pods/${this.podName}/exec?${q.toString()}`, headers: { authorization: `Bearer ${token}` } };
1475
1581
  }
1476
- runExec(command, opts) {
1582
+ runExec(start, command, opts) {
1583
+ const launch = this.floor.wrap(start);
1584
+ if (!launch.ok)
1585
+ return Promise.resolve(launch);
1586
+ opts?.onLaunch?.(launch.value);
1477
1587
  let sock;
1478
1588
  let headers;
1479
1589
  let url;
1480
1590
  try {
1481
- ({ url, headers } = this.execUrl(command));
1591
+ ({ url, headers } = this.execUrl(floorArgv(launch.value, ["sh", "-c", command])));
1482
1592
  sock = this.openExec(url, headers);
1483
1593
  }
1484
1594
  catch (e) {
@@ -1672,12 +1782,12 @@ export class RemoteK8sExecutionEnv {
1672
1782
  });
1673
1783
  }
1674
1784
  async exec(command, options) {
1675
- return this.execCall(createCwdGuardedCall("k8s", command, this.workingDirOf(options?.cwd), options?.env), options);
1785
+ return this.execCall(engineStart(options?.readFloor), createCwdGuardedCall("k8s", command, this.workingDirOf(options?.cwd), options?.env), options);
1676
1786
  }
1677
1787
  fsExec(command, abortSignal) {
1678
- return this.execCall(createCwdGuardedCall("k8s", command, undefined, undefined), { abortSignal });
1788
+ return this.execCall(ADAPTER_START, createCwdGuardedCall("k8s", command, undefined, undefined), { abortSignal });
1679
1789
  }
1680
- async execCall(call, options) {
1790
+ async execCall(start, call, options) {
1681
1791
  const c = await this.ensureConnected();
1682
1792
  if (!c.ok)
1683
1793
  return c;
@@ -1686,7 +1796,7 @@ export class RemoteK8sExecutionEnv {
1686
1796
  const abortSignal = options?.abortSignal ? AbortSignal.any([options.abortSignal, inflight.signal]) : inflight.signal;
1687
1797
  try {
1688
1798
  if (!detachSignal) {
1689
- return call.settle(await this.runExec(call.command, {
1799
+ return call.settle(await this.runExec(start, call.command, {
1690
1800
  signal: abortSignal,
1691
1801
  timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
1692
1802
  boundOutput: true,
@@ -1698,7 +1808,10 @@ export class RemoteK8sExecutionEnv {
1698
1808
  const adoptId = randomUUID();
1699
1809
  const dir = `${K8S_BG_DIR_ROOT}/exec-${adoptId}`;
1700
1810
  const tee = { out: seedMemStream(""), err: seedMemStream("") };
1701
- return call.settle(await this.runExec(buildDetachCapableExec(dir, call.command), {
1811
+ let startId;
1812
+ let adoptedHere = false;
1813
+ const settled = call.settle(await this.runExec(start.kind === "engine" ? engineStart(start.readFloor, true) : start, buildDetachCapableExec(dir, call.command), {
1814
+ onLaunch: (l) => void (startId = l.mode === "run" ? l.startId : undefined),
1702
1815
  signal: abortSignal,
1703
1816
  timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
1704
1817
  boundOutput: true,
@@ -1715,12 +1828,17 @@ export class RemoteK8sExecutionEnv {
1715
1828
  data: (channel, payload) => feedMemStream(channel === 1 ? mem.out : mem.err, payload, K8S_BG_MEM_CAP),
1716
1829
  closed: (terminal) => ctx.onTerminal(terminal.failed, terminal.exitCode),
1717
1830
  });
1718
- return { jobId: adoptId, dir, stdoutCursor: 0, stderrCursor: 0, adopt: { mem, sock: io.sock } };
1831
+ return { jobId: adoptId, dir, stdoutCursor: 0, stderrCursor: 0, adopt: { mem, sock: io.sock }, ...(startId !== undefined ? { startId } : {}) };
1719
1832
  });
1833
+ if (adopted !== undefined)
1834
+ adoptedHere = true;
1720
1835
  return adopted?.shellId;
1721
1836
  },
1722
1837
  },
1723
1838
  }));
1839
+ if (!adoptedHere)
1840
+ this.floor.release(startId);
1841
+ return settled;
1724
1842
  }
1725
1843
  finally {
1726
1844
  this.endExec(inflight);
@@ -1758,7 +1876,7 @@ export class RemoteK8sExecutionEnv {
1758
1876
  const cap = options?.maxOutputBytes;
1759
1877
  let bytes = 0;
1760
1878
  const signal = options?.signal ? AbortSignal.any([options.signal, ac.signal]) : ac.signal;
1761
- const done = this.runExec(buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
1879
+ const done = this.runExec(engineStart(undefined), buildCwdEnvCommand(command, this.workingDirOf(options?.cwd), options?.env, "raw").command, {
1762
1880
  signal,
1763
1881
  timeoutMs: options?.timeout != null ? options.timeout * 1000 : undefined,
1764
1882
  readTimeoutMs: options?.readTimeoutMs,
@@ -1827,7 +1945,8 @@ export class RemoteK8sExecutionEnv {
1827
1945
  }
1828
1946
  makeBgDriver() {
1829
1947
  const self = this;
1830
- const bgExec = (script, boundOutput) => self.runExec(script, { timeoutMs: self.cfg.rpcTimeoutMs, boundOutput });
1948
+ const bgExec = (start, script, boundOutput, onLaunch) => self.runExec(start, script, { timeoutMs: self.cfg.rpcTimeoutMs, boundOutput, ...(onLaunch !== undefined ? { onLaunch } : {}) });
1949
+ const under = (state) => ({ kind: "under", startId: state.startId });
1831
1950
  return {
1832
1951
  async launch(command, options, ctx) {
1833
1952
  const conn = await self.ensureConnected();
@@ -1838,14 +1957,17 @@ export class RemoteK8sExecutionEnv {
1838
1957
  const inner = buildCwdEnvCommand(command, self.workingDirOf(options?.cwd), options?.env, "raw").command;
1839
1958
  const cmdB64 = Buffer.from(inner, "utf8").toString("base64");
1840
1959
  const runnerB64 = Buffer.from(buildBgRunnerScript(dir), "utf8").toString("base64");
1841
- const r = await bgExec(buildBgLauncherScript(dir, cmdB64, runnerB64), true);
1960
+ let startId;
1961
+ const r = await bgExec(engineStart(options?.readFloor, true), buildBgLauncherScript(dir, cmdB64, runnerB64), true, (l) => void (startId = l.mode === "run" ? l.startId : undefined));
1962
+ if (!r.ok || r.value.exitCode !== 0 || !r.value.stdout.includes("SEMABG_LAUNCHED"))
1963
+ self.floor.release(startId);
1842
1964
  if (!r.ok)
1843
1965
  throw new Error(`background launch failed: ${r.error.message}`);
1844
1966
  if (r.value.exitCode !== 0 || !r.value.stdout.includes("SEMABG_LAUNCHED")) {
1845
1967
  throw new Error(`background launch exited ${r.value.exitCode}: ${r.value.stderr.slice(0, 200)}`);
1846
1968
  }
1847
1969
  void ctx;
1848
- return { jobId, dir, stdoutCursor: 0, stderrCursor: 0 };
1970
+ return { jobId, dir, stdoutCursor: 0, stderrCursor: 0, ...(startId !== undefined ? { startId } : {}) };
1849
1971
  },
1850
1972
  async read(state) {
1851
1973
  if (state.adopt) {
@@ -1858,7 +1980,7 @@ export class RemoteK8sExecutionEnv {
1858
1980
  bytesDroppedBeforeCursor: o.dropped + e.dropped,
1859
1981
  };
1860
1982
  }
1861
- const r = await bgExec(buildBgPollScript(state.dir, state.stdoutCursor, state.stderrCursor, K8S_BG_READ_CAP), false);
1983
+ const r = await bgExec(under(state), buildBgPollScript(state.dir, state.stdoutCursor, state.stderrCursor, K8S_BG_READ_CAP), false);
1862
1984
  if (!r.ok)
1863
1985
  throw new Error(`background poll failed: ${r.error.message}`);
1864
1986
  const p = parseBgPollOutput(r.value.stdout);
@@ -1873,7 +1995,7 @@ export class RemoteK8sExecutionEnv {
1873
1995
  state.stderrCursor = p.errTotal;
1874
1996
  let terminal = p.exitCode !== undefined ? { failed: false, exitCode: p.exitCode } : !p.alive ? { failed: true } : undefined;
1875
1997
  if (!terminal && (p.outTotal > K8S_BG_FILE_CAP || p.errTotal > K8S_BG_FILE_CAP)) {
1876
- void bgExec(buildBgKillScript(state.dir), true).catch(() => { });
1998
+ void bgExec(under(state), buildBgKillScript(state.dir), true).catch(() => { });
1877
1999
  terminal = { failed: true };
1878
2000
  }
1879
2001
  return {
@@ -1885,7 +2007,7 @@ export class RemoteK8sExecutionEnv {
1885
2007
  };
1886
2008
  },
1887
2009
  async kill(state) {
1888
- await bgExec(buildBgKillScript(state.dir), true).catch(() => { });
2010
+ await bgExec(under(state), buildBgKillScript(state.dir), true).catch(() => { });
1889
2011
  },
1890
2012
  async disposeOne(state) {
1891
2013
  const closeAdopted = () => {
@@ -1898,10 +2020,12 @@ export class RemoteK8sExecutionEnv {
1898
2020
  };
1899
2021
  if (self.destroyed) {
1900
2022
  closeAdopted();
2023
+ self.floor.release(state.startId);
1901
2024
  return;
1902
2025
  }
1903
- await bgExec(buildBgDisposeScript(state.dir), true).catch(() => { });
2026
+ await bgExec(under(state), buildBgDisposeScript(state.dir), true).catch(() => { });
1904
2027
  closeAdopted();
2028
+ self.floor.release(state.startId);
1905
2029
  },
1906
2030
  };
1907
2031
  }
@@ -2012,7 +2136,7 @@ export class RemoteK8sExecutionEnv {
2012
2136
  const c = await this.ensureConnected();
2013
2137
  if (!c.ok)
2014
2138
  return c;
2015
- return this.runExec(command, { signal: abortSignal, capMs: this.cfg.dataTimeoutMs });
2139
+ return this.runExec(ADAPTER_START, command, { signal: abortSignal, capMs: this.cfg.dataTimeoutMs });
2016
2140
  }
2017
2141
  workingDirOf(cwd) {
2018
2142
  const dir = cwd ?? this.cwd;
@@ -1,6 +1,8 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { type LaneHomeConfig } from "../lane-home-probe.js";
3
- import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory } from "@sema-agent/core";
3
+ import { FileError, ExecutionError, RemoteExecutionError, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, type ExecutionEnvCapabilities } from "@sema-agent/core";
4
+ import { type SandboxReadFloorKnob } from "../execution-lane-caps.js";
5
+ import { type SandboxReadFloor } from "./sandbox-read-floor.js";
4
6
  /** Options accepted by `ExecutionEnv.exec` (core does not re-export the type; derive it from the seam). */
5
7
  type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
6
8
  /** Construction config for {@link RemoteLocalDockerExecutionEnv}. */
@@ -55,28 +57,23 @@ export interface LocalDockerEnvConfig extends LaneHomeConfig {
55
57
  /** Stable id mixed into the container NAME (e.g. the sessionId). A random suffix is always appended so two
56
58
  * envs sharing an id never collide. Default = a random id. */
57
59
  id?: string;
60
+ /** `SANDBOX_READ_FLOOR`(keep | off;缺席 ⇒ keep):本容器守不守读拒表的字节地板 = 读地板模块服务本车道 ∧ 本键不是 off(`sandbox-read-floor.ts`)。 */
61
+ sandboxReadFloor?: SandboxReadFloorKnob;
58
62
  }
59
63
  /** Inject `spawn` for tests (a fake docker CLI). Defaults to node's child_process spawn. */
60
64
  export interface LocalDockerEnvDeps {
61
65
  spawn?: typeof spawn;
66
+ /** 这只环境手里的读地板模块(缺席 ⇒ `createSandboxReadFloor` 按旋钮选;钉注入守地板的模块)。能力块的 `keepsReadFloor` 读它的状态。 */
67
+ readFloor?: SandboxReadFloor;
62
68
  }
63
69
  export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv {
64
- /** DUAL-MODE-DESIGN §5: a container is a real isolation boundary; v1 has no managed memory snapshot. */
65
- /** `sharesProcessTable`(core 7.57.0,必填):命令跑在本机容器自己的 PID 命名空间里(不传 `--pid=host`),不在本进程所在的进程表。 */
66
- /** 能力块(引擎 [ref],八键必填、各键只经引擎读器读):`hostLocalPaths` = 这条车道答出的路径是不是本进程所在机器上的文件;`externalContentTarget` = 产出是否来自部署信任边界之外;`io` = 文件与 shell 两面真到达目标;`keepsReadFloor` = 是否守住读拒表的字节地板(本车道不守 ⇒ 读拒表非空的 run 被告知,部署要求地板时准备期拒)。 */
67
- readonly capabilities: {
68
- isolation: true;
69
- suspendable: false;
70
- canonicalPathAuthoritative: true;
71
- sharesProcessTable: false;
72
- hostLocalPaths: false;
73
- externalContentTarget: false;
74
- io: true;
75
- keepsReadFloor: false;
76
- };
70
+ /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。读地板旋钮是它的输入,{@link floor} 由同一只判据、同一个旋钮值选出。 */
71
+ readonly capabilities: ExecutionEnvCapabilities;
72
+ /** 读拒表字节地板模块:容器里每一次 `docker exec` 起动都先经它的 `wrap`({@link startArgv},唯一接缝)。 */
73
+ private readonly floor;
77
74
  /** Working directory inside the container; relative paths resolve against it (ExecutionEnv contract). */
78
75
  cwd: string;
79
- /** core `ExecutionEnv.homeDir`([ref]):本容器里执行用户的 home —— 声明胜,否则开容器那一拍实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时连上之前缺席)。 */
76
+ /** core `ExecutionEnv.homeDir`([ref]):本容器里执行用户的 home —— 声明在场 ⇒ 声明(开环境那一拍与 `$HOME` 对证过),否则开容器那一拍实测的 `$HOME`,见 {@link LaneHomeSeat}。读时才取(没声明时连上之前缺席)。 */
80
77
  get homeDir(): string | undefined;
81
78
  /** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在容器里(镜像的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
82
79
  * 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
@@ -97,7 +94,7 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
97
94
  private connecting?;
98
95
  /** 家目录:环境答了而与声明不一致 ⇒ 这只环境的拒,记一次、此后每次连接原样回它(同一镜像同一答案:不再起容器、不再问)。 */
99
96
  private homeRefusal?;
100
- /** 家目录的座(声明胜 · 否则开容器那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。本车道对声明对证。 */
97
+ /** 家目录的座(声明在场 ⇒ 开环境那一拍对证 · 否则开容器那一拍实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。与其余读得出家目录的车道同一只判官、同一句拒。 */
101
98
  private readonly homeSeat;
102
99
  constructor(config: LocalDockerEnvConfig, deps?: LocalDockerEnvDeps);
103
100
  private resolve;
@@ -170,7 +167,7 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
170
167
  }): Promise<Result<string[], FileError>>;
171
168
  /** 暂存写的两只面(规则与提交脚本的唯一属主 = `remote-shell.ts` `writeFileStaged` / `appendFileStaged`;父目录与私有暂存目录由编排的准备那一步建):
172
169
  * 上传 = 容器内 `cat` 从 stdin 收整份字节落到私有目录里的暂存件({@link pipeIn});提交 = 容器内一次 shell 往返。两者同一身份 ——
173
- * 同一只 argv 构造({@link execArgv}),都是镜像的用户。不用 `docker cp`:它由守护进程以 root 落盘,镜像用户 ≠ root 时暂存件属 root、
170
+ * 同一只 argv 构造({@link startArgv}),都是镜像的用户。不用 `docker cp`:它由守护进程以 root 落盘,镜像用户 ≠ root 时暂存件属 root、
174
171
  * 提交方拿不走也清不掉,「目录不收新名字」也探不出来(root 哪里都写得进)。 */
175
172
  private stagedLane;
176
173
  /** 整旧或整新(暂存 → 一次 `mv -f`)。修前 `docker cp` 直落目标(以守护进程的 root 身份):传输中途断 / 取消时目标剩什么取决于守护进程的解包实现,本适配器不依赖它。 */
@@ -216,17 +213,23 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
216
213
  /**
217
214
  * 交给宿主 `docker` CLI 的 env —— 宿主子进程 env 的唯一组合(`server-secret-env.ts` `hostSubprocessEnv`),继承词 `"all"`:本服务自有凭据(起服声明的名与值)
218
215
  * 任何词都不交,CLI 与它拉起的凭据助手 / CLI 插件要读的变量(`DOCKER_*` / `AWS_*` …)照交 —— 形状剥会剥 `AWS_SECRET_ACCESS_KEY` 一类,ECR / GCR 凭据助手因此断。
219
- * 命令本身跑在容器里,容器的 env 只由 `execArgv` 的 `-e K=V` 显式给,本 env 到不了容器。每次起进程现算一次(与 host shell 同)。
216
+ * 命令本身跑在容器里,容器的 env 只由 `startArgv` 的 `-e K=V` 显式给,本 env 到不了容器。每次起进程现算一次(与 host shell 同)。
220
217
  */
221
218
  private cliEnv;
222
219
  /** Prepend `-H <dockerHost>` to a docker argv when configured (per-invocation, not the process env). */
223
220
  private withHostFlag;
224
- /** Build the `docker exec` argv for a command, applying cwd (`-w`) and per-command env (`-e K=V`).
225
- * argv elements are SEPARATE (no host-side shell join) — the command runs in the CONTAINER's `/bin/sh -c`.
226
- * `stdin` ⇒ `-i` (the payload of {@link pipeIn}); nothing else differs, so a piped upload runs as the same user as every other exec. */
227
- private execArgv;
228
- /** Like {@link execArgv} but for an internal FileSystem helper command (uses the workspace cwd + adapter env). */
229
- private execRaw;
221
+ /**
222
+ * **唯一起动接缝**:容器里的每一次 `docker exec` 起动(引擎的命令、文件面助手、探针、地板的引导命令)都从这里拿 argv —— 先问地板模块
223
+ * `wrap(起动形)`,再按答案编码(身份旗标 + 监督者命令行头;不守地板 ⇒ 与改前逐字同)。本适配器对地板零判断;模块拒 ⇒ `spawn_error`,命令从未起。
224
+ * argv elements are SEPARATE (no host-side shell join) — the command runs in the CONTAINER's shell (`-c`). `stdin` ⇒ `-i` (the payload of
225
+ * {@link pipeIn}); nothing else differs, so a piped upload runs as the same user as every other exec. cwd (`-w`) and per-command env (`-e K=V`;
226
+ * adapter base env under, per-command wins) — except `containerDefaults`: the command asks about the container's OWN defaults (the shell probe),
227
+ * so it carries neither (the bare argv is then `exec <name> <shell> -c <cmd>`, byte-identical to the pre-floor probe).
228
+ */
229
+ private startArgv;
230
+ /** 地板模块的三件原语(引导命令 / 放置 / 中继)。引导与放置经 {@link startArgv}(起动形 `bootstrap`:只在附着中合法);中继以模块给的身份起一只
231
+ * 长连的 `docker exec -i`(stdin / stdout 作双工),随本环境 `destroy` 由模块关。 */
232
+ private floorTransport;
230
233
  /** Spawn the docker binary with argv; collect stdout/stderr (+ raw bytes when binary), bound by timeout/abort. */
231
234
  private docker;
232
235
  /** Lazy connect for an exec path → ExecutionError on failure (fail closed, never throws). */