@sema-agent/server 7.140.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 (200) hide show
  1. package/MIGRATION.md +290 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +75 -13
  5. package/dist/approval-batch-census.d.ts +27 -0
  6. package/dist/approval-batch-census.js +27 -0
  7. package/dist/approval-card.d.ts +31 -16
  8. package/dist/approval-card.js +6 -3
  9. package/dist/approval.d.ts +15 -0
  10. package/dist/approval.js +10 -0
  11. package/dist/audit.d.ts +4 -2
  12. package/dist/audit.js +2 -2
  13. package/dist/boot/device-lane.d.ts +11 -0
  14. package/dist/boot/device-lane.js +16 -0
  15. package/dist/boot/engine-lease.d.ts +24 -3
  16. package/dist/boot/engine-lease.js +9 -2
  17. package/dist/boot/execution-env.js +8 -4
  18. package/dist/boot/memory-boundary.d.ts +2 -2
  19. package/dist/boot/memory-consolidation.d.ts +1 -1
  20. package/dist/boot/memory-consolidation.js +1 -1
  21. package/dist/boot/reapers.d.ts +2 -2
  22. package/dist/boot/resolve-spec.js +7 -4
  23. package/dist/boot/session-faces.d.ts +1 -1
  24. package/dist/boot/session-faces.js +4 -3
  25. package/dist/boot/shutdown.js +5 -0
  26. package/dist/boot/stage-05-execution-env.js +3 -1
  27. package/dist/boot/stage-06-runners.d.ts +0 -2
  28. package/dist/boot/stage-06-runners.js +1 -1
  29. package/dist/boot/stage-07-capability-layer.d.ts +0 -1
  30. package/dist/boot/stage-07-capability-layer.js +8 -10
  31. package/dist/boot/stage-08-reapers.d.ts +0 -1
  32. package/dist/boot/stage-09-leader.d.ts +0 -1
  33. package/dist/boot/stage-10-http-server.d.ts +0 -1
  34. package/dist/boot/stage-10-http-server.js +1 -0
  35. package/dist/boot/stores.js +10 -4
  36. package/dist/boot/web-admin.js +1 -0
  37. package/dist/capabilities/scenarios.d.ts +1 -1
  38. package/dist/capability-facts.d.ts +9 -0
  39. package/dist/capability-facts.js +9 -0
  40. package/dist/card-void.d.ts +38 -0
  41. package/dist/card-void.js +23 -0
  42. package/dist/config-catalog.js +11 -8
  43. package/dist/config-provider.d.ts +13 -13
  44. package/dist/config-provider.js +19 -33
  45. package/dist/config-types.d.ts +26 -8
  46. package/dist/config.js +55 -46
  47. package/dist/delegated-session-owner.d.ts +21 -19
  48. package/dist/delegated-session-owner.js +1 -1
  49. package/dist/device-store.d.ts +7 -0
  50. package/dist/device-store.js +3 -0
  51. package/dist/execution-config-root.d.ts +22 -0
  52. package/dist/execution-config-root.js +35 -0
  53. package/dist/execution-lane-caps.d.ts +49 -2
  54. package/dist/execution-lane-caps.js +90 -13
  55. package/dist/execution-placement.d.ts +3 -0
  56. package/dist/execution-placement.js +7 -0
  57. package/dist/fleet/drain-entry.d.ts +3 -0
  58. package/dist/hosted-posture.d.ts +8 -0
  59. package/dist/hosted-posture.js +4 -0
  60. package/dist/http/admission.js +3 -3
  61. package/dist/http/caller-trust.d.ts +3 -2
  62. package/dist/http/caller-trust.js +2 -1
  63. package/dist/http/dispatch.js +4 -0
  64. package/dist/http/live-frame.d.ts +24 -0
  65. package/dist/http/live-frame.js +16 -0
  66. package/dist/http/resume-legs.js +37 -20
  67. package/dist/http/route-table.js +2 -0
  68. package/dist/http/routes/approvals-assistant.js +1 -0
  69. package/dist/http/routes/capabilities.js +5 -4
  70. package/dist/http/routes/diagnostics.d.ts +0 -19
  71. package/dist/http/routes/diagnostics.js +16 -2
  72. package/dist/http/routes/memory-bundle.d.ts +2 -2
  73. package/dist/http/routes/memory-bundle.js +1 -1
  74. package/dist/http/routes/memory-compliance.js +1 -1
  75. package/dist/http/routes/memory-policy.js +2 -2
  76. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  77. package/dist/http/routes/run-permission-mode.js +170 -0
  78. package/dist/http/routes/session-sync.d.ts +3 -6
  79. package/dist/http/routes/session-sync.js +7 -2
  80. package/dist/http/routes/sessions.js +5 -4
  81. package/dist/http/routes/tasks.js +63 -66
  82. package/dist/http/run-read-face.js +3 -3
  83. package/dist/http/send.d.ts +1 -1
  84. package/dist/http/send.js +2 -2
  85. package/dist/http/server.d.ts +9 -2
  86. package/dist/http/sse-log.d.ts +3 -8
  87. package/dist/http/stored-body-replay.d.ts +3 -3
  88. package/dist/http/stored-body-replay.js +3 -1
  89. package/dist/instruction-source-notice.d.ts +7 -0
  90. package/dist/instruction-source-notice.js +6 -6
  91. package/dist/mcp-assertion-header.d.ts +4 -0
  92. package/dist/mcp-assertion-header.js +5 -0
  93. package/dist/memory-posture.d.ts +1 -1
  94. package/dist/memory-scope.d.ts +4 -4
  95. package/dist/memory-scope.js +1 -1
  96. package/dist/memory-sync-client.d.ts +14 -2
  97. package/dist/memory-sync-client.js +80 -18
  98. package/dist/observability/fail-open.d.ts +30 -2
  99. package/dist/observability/fail-open.js +30 -2
  100. package/dist/observability/metrics.js +0 -1
  101. package/dist/observability/run-terminal.d.ts +6 -2
  102. package/dist/observability/run-terminal.js +25 -26
  103. package/dist/observability/secret-env-scrub.d.ts +20 -1
  104. package/dist/observability/secret-env-scrub.js +14 -0
  105. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  106. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  107. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  108. package/dist/orchestration/workflow-notify-journal.js +2 -1
  109. package/dist/permission-mode-admission.d.ts +68 -0
  110. package/dist/permission-mode-admission.js +23 -0
  111. package/dist/permission-rule-list.d.ts +55 -6
  112. package/dist/permission-rule-list.js +52 -5
  113. package/dist/plugins/caching-session-store.d.ts +6 -0
  114. package/dist/plugins/caching-session-store.js +3 -0
  115. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  116. package/dist/plugins/checkpoint-store-sql.js +74 -3
  117. package/dist/plugins/device-store-sql.d.ts +5 -2
  118. package/dist/plugins/device-store-sql.js +4 -1
  119. package/dist/plugins/execution-placement-sql.d.ts +11 -1
  120. package/dist/plugins/execution-placement-sql.js +12 -1
  121. package/dist/plugins/file-run-store.d.ts +2 -2
  122. package/dist/plugins/file-run-store.js +1 -1
  123. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  124. package/dist/plugins/fork-routing-session-store.js +2 -61
  125. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  126. package/dist/plugins/k8s-exec-protocol.js +6 -1
  127. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  128. package/dist/plugins/local-checkpoint-store.js +22 -0
  129. package/dist/plugins/local-session-store.d.ts +227 -35
  130. package/dist/plugins/local-session-store.js +518 -37
  131. package/dist/plugins/local-session-window.d.ts +17 -3
  132. package/dist/plugins/local-session-window.js +7 -13
  133. package/dist/plugins/memory-run-store.d.ts +2 -2
  134. package/dist/plugins/memory-run-store.js +1 -1
  135. package/dist/plugins/one-time-migrations.d.ts +42 -2
  136. package/dist/plugins/one-time-migrations.js +65 -3
  137. package/dist/plugins/pg-pool.js +3 -2
  138. package/dist/plugins/pg-session-storage.d.ts +2 -0
  139. package/dist/plugins/pg-session-storage.js +3 -2
  140. package/dist/plugins/remote-env-e2b.d.ts +24 -3
  141. package/dist/plugins/remote-env-e2b.js +131 -18
  142. package/dist/plugins/remote-env-k8s.d.ts +20 -1
  143. package/dist/plugins/remote-env-k8s.js +148 -25
  144. package/dist/plugins/remote-env-local-docker.d.ts +25 -11
  145. package/dist/plugins/remote-env-local-docker.js +87 -21
  146. package/dist/plugins/run-store-sql.d.ts +3 -3
  147. package/dist/plugins/run-store-sql.js +4 -3
  148. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  149. package/dist/plugins/sandbox-read-floor.js +608 -0
  150. package/dist/plugins/session-ownership.d.ts +2 -1
  151. package/dist/plugins/session-placement.d.ts +17 -4
  152. package/dist/plugins/session-placement.js +2 -1
  153. package/dist/plugins/session-store.d.ts +18 -20
  154. package/dist/plugins/session-store.js +4 -12
  155. package/dist/plugins/store-backend.d.ts +31 -0
  156. package/dist/plugins/store-backend.js +21 -4
  157. package/dist/plugins/store-contracts.d.ts +9 -2
  158. package/dist/plugins/tidb-pool.js +2 -1
  159. package/dist/plugins/tidb-session-store.d.ts +4 -1
  160. package/dist/plugins/tidb-session-store.js +3 -2
  161. package/dist/run-cancel-context.d.ts +34 -0
  162. package/dist/run-cancel-context.js +7 -0
  163. package/dist/runs.d.ts +39 -12
  164. package/dist/runs.js +68 -53
  165. package/dist/security.d.ts +2 -2
  166. package/dist/server-secret-env.d.ts +2 -1
  167. package/dist/server-secret-env.js +2 -4
  168. package/dist/session-error-wire.d.ts +22 -4
  169. package/dist/session-error-wire.js +16 -0
  170. package/dist/session-incarnation.d.ts +9 -3
  171. package/dist/session-sync-kernel.d.ts +42 -1
  172. package/dist/session-sync-kernel.js +17 -0
  173. package/dist/session-sync.js +16 -4
  174. package/dist/sql-engine-coordinates.d.ts +49 -0
  175. package/dist/sql-engine-coordinates.js +27 -0
  176. package/dist/standing-answer-grant.d.ts +31 -2
  177. package/dist/standing-answer-grant.js +14 -2
  178. package/dist/store-posture.d.ts +1 -1
  179. package/dist/task-a2a.d.ts +1 -1
  180. package/dist/task-mcp.d.ts +1 -1
  181. package/dist/task-settings.d.ts +52 -25
  182. package/dist/task-settings.js +91 -28
  183. package/dist/terminal.d.ts +77 -20
  184. package/dist/terminal.js +66 -20
  185. package/dist/trace/core-keyset-guard.d.ts +79 -2
  186. package/dist/trace/exit-seat.d.ts +7 -0
  187. package/dist/trace/exit-seat.js +1 -0
  188. package/dist/trace/model-segment.d.ts +2 -2
  189. package/dist/trace/redact.js +323 -90
  190. package/dist/trace/self-minted-wire.d.ts +168 -2
  191. package/dist/trace/self-minted-wire.js +63 -0
  192. package/dist/verifier-pause-card.d.ts +8 -0
  193. package/dist/verifier-pause-card.js +19 -0
  194. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  195. package/dist/web-admin-client/apply-effective.js +2 -2
  196. package/dist/web-admin-client/candidate-gate.d.ts +1 -1
  197. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  198. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  199. package/package.json +2 -2
  200. package/wire-changes/server@7.141.0.json +367 -0
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import { Sandbox, CommandExitError, FileType } from "e2b";
3
3
  import { FileError, ExecutionError, RemoteExecutionError, RollingTailBuffer, markTruncated, } from "@sema-agent/core";
4
4
  import { capabilitiesFor } from "../execution-lane-caps.js";
5
+ import { ADAPTER_START, BOOTSTRAP_START, createSandboxReadFloor, e2bFloorCommand, engineStart, } from "./sandbox-read-floor.js";
5
6
  import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
6
7
  import { fileErrorFromExec } from "./remote-env-file-error.js";
7
8
  import { appendFileStaged, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
@@ -52,8 +53,90 @@ export class RemoteContainerExecutionEnv {
52
53
  this.cwd = this.cfg.mountPath;
53
54
  this.homeSeat = new LaneHomeSeat(PROVIDER, config);
54
55
  this.sandboxApi = deps.sandboxApi ?? Sandbox;
56
+ this.floor = deps.readFloor ?? createSandboxReadFloor({ lane: PROVIDER, knob: config.sandboxReadFloor, ...(config.logger !== undefined ? { logger: config.logger } : {}) });
57
+ this.capabilities = capabilitiesFor({ remoteExec: { provider: PROVIDER }, sandboxReadFloor: config.sandboxReadFloor });
58
+ }
59
+ capabilities;
60
+ floor;
61
+ startIn(start, command, envs) {
62
+ const launch = this.floor.wrap(start);
63
+ return launch.ok ? ok(e2bFloorCommand(launch.value, command, envs)) : launch;
64
+ }
65
+ runIn(sbx, s, opts = {}) {
66
+ const { background, ...rest } = opts;
67
+ const o = { ...rest, ...(s.user !== undefined ? { user: s.user } : {}), ...(s.envs !== undefined ? { envs: s.envs } : {}) };
68
+ return background ? sbx.commands.run(s.cmd, { ...o, background: true }) : sbx.commands.run(s.cmd, o);
69
+ }
70
+ floorTransport(sbx) {
71
+ const bootstrap = async (command) => {
72
+ const s = this.startIn(BOOTSTRAP_START, command, undefined);
73
+ if (!s.ok)
74
+ return { ok: false, error: s.error.message };
75
+ try {
76
+ const r = await this.rpc("floor.bootstrap", (signal) => this.runIn(sbx, s.value, { signal, timeoutMs: this.cfg.rpcTimeoutMs }));
77
+ return { ok: true, exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr };
78
+ }
79
+ catch (e) {
80
+ if (e instanceof CommandExitError)
81
+ return { ok: true, exitCode: e.exitCode, stdout: e.stdout, stderr: e.stderr };
82
+ return { ok: false, error: errMsg(e) };
83
+ }
84
+ };
85
+ return {
86
+ bootstrap,
87
+ place: async (dest, bytes) => {
88
+ const dir = path.posix.dirname(dest);
89
+ const tmp = `${dest}.${Math.random().toString(16).slice(2, 10)}`;
90
+ const mk = await bootstrap(`umask 022 && mkdir -p ${shellQuote(dir)}`);
91
+ if (!mk.ok || mk.exitCode !== 0)
92
+ return { ok: false, error: mk.ok ? mk.stderr.trim() || `exit ${mk.exitCode}` : mk.error };
93
+ try {
94
+ const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
95
+ await this.rpc("files.write", (signal) => sbx.files.write(tmp, ab, { user: "root", signal }), undefined, this.cfg.dataTimeoutMs);
96
+ }
97
+ catch (e) {
98
+ return { ok: false, error: errMsg(e) };
99
+ }
100
+ const mv = await bootstrap(`chmod 0755 ${shellQuote(tmp)} && mv -f ${shellQuote(tmp)} ${shellQuote(dest)}`);
101
+ return mv.ok && mv.exitCode === 0 ? { ok: true } : { ok: false, error: mv.ok ? mv.stderr.trim() || `exit ${mv.exitCode}` : mv.error };
102
+ },
103
+ openRelay: (identity, command, events) => {
104
+ let handle;
105
+ let closed = false;
106
+ const pending = [];
107
+ const user = (() => {
108
+ switch (identity) {
109
+ case "root":
110
+ return { user: "root" };
111
+ case "exec-user":
112
+ return {};
113
+ }
114
+ })();
115
+ sbx.commands
116
+ .run(command, { background: true, stdin: true, timeoutMs: 0, ...user, onStdout: (d) => events.data(d) })
117
+ .then((h) => {
118
+ handle = h;
119
+ if (closed)
120
+ return void h.kill().catch(() => { });
121
+ for (const line of pending.splice(0))
122
+ void h.sendStdin(line).catch(() => { });
123
+ h.wait().then(() => events.closed("relay exited"), (e) => events.closed(`relay exited: ${errMsg(e)}`));
124
+ }, (e) => events.closed(`relay start failed: ${errMsg(e)}`));
125
+ return {
126
+ send: (line) => {
127
+ if (handle !== undefined)
128
+ void handle.sendStdin(line).catch(() => { });
129
+ else
130
+ pending.push(line);
131
+ },
132
+ close: () => {
133
+ closed = true;
134
+ void handle?.kill().catch(() => { });
135
+ },
136
+ };
137
+ },
138
+ };
55
139
  }
56
- capabilities = capabilitiesFor({ remoteExec: { provider: "e2b" } });
57
140
  workspaceHandle() {
58
141
  if (!this.handle) {
59
142
  throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
@@ -66,8 +149,12 @@ export class RemoteContainerExecutionEnv {
66
149
  }
67
150
  async startBackground(command) {
68
151
  const conn = await this.ensureConnected();
69
- if (conn.ok)
70
- await conn.value.commands.run(command, { background: true });
152
+ if (!conn.ok)
153
+ return;
154
+ const s = this.startIn(engineStart(undefined), command, undefined);
155
+ if (!s.ok)
156
+ return void this.cfg.logger?.warn?.("remote_env_background_refused", { error: s.error.message });
157
+ await this.runIn(conn.value, s.value, { background: true });
71
158
  }
72
159
  async connect(config) {
73
160
  if (config?.snapshotId) {
@@ -109,6 +196,11 @@ export class RemoteContainerExecutionEnv {
109
196
  this.cfg.metrics?.inc("remote_env_connect_total", { provider: PROVIDER, result: "error" });
110
197
  return { ok: false, error: home ?? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
111
198
  }
199
+ await this.floor.attach(this.floorTransport(sbx), { origin: "created", workspaceRoot: this.cfg.mountPath });
200
+ if (this.destroyed) {
201
+ await sbx.kill().catch(() => { });
202
+ return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
203
+ }
112
204
  this.sbx = sbx;
113
205
  this.paused = false;
114
206
  this.startKeepAlive();
@@ -180,6 +272,12 @@ export class RemoteContainerExecutionEnv {
180
272
  await sbx.kill().catch(() => { });
181
273
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
182
274
  }
275
+ const restoredRoot = options?.priorHandle?.mountPath ?? this.cfg.mountPath;
276
+ await this.floor.attach(this.floorTransport(sbx), { origin: "resumed", workspaceRoot: restoredRoot });
277
+ if (this.destroyed) {
278
+ await sbx.kill().catch(() => { });
279
+ return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
280
+ }
183
281
  const home = await this.homeVerdict(sbx, options?.abortSignal);
184
282
  if (this.destroyed) {
185
283
  await sbx.kill().catch(() => { });
@@ -194,7 +292,6 @@ export class RemoteContainerExecutionEnv {
194
292
  this.paused = false;
195
293
  this.startKeepAlive();
196
294
  this.cfg.logger?.info?.("remote_env_resume", { requested: snapshotId, connected: sbx.sandboxId });
197
- const restoredRoot = options?.priorHandle?.mountPath ?? this.cfg.mountPath;
198
295
  this.cwd = restoredRoot;
199
296
  this.handle = { sandboxId: sbx.sandboxId, provider: PROVIDER, mountPath: restoredRoot, snapshotId, sessionToken: sbx.sandboxId };
200
297
  this.cfg.metrics?.inc("remote_env_resume_total", { provider: PROVIDER, result: "ok" });
@@ -227,7 +324,10 @@ export class RemoteContainerExecutionEnv {
227
324
  return undefined;
228
325
  let probe;
229
326
  try {
230
- const r = await this.rpc("sandbox.home", (s) => sbx.commands.run(LANE_HOME_PROBE_COMMAND, { signal: s }), signal);
327
+ const start = this.startIn(ADAPTER_START, LANE_HOME_PROBE_COMMAND, undefined);
328
+ if (!start.ok)
329
+ throw start.error;
330
+ const r = await this.rpc("sandbox.home", (s) => this.runIn(sbx, start.value, { signal: s }), signal);
231
331
  probe = r.exitCode === 0 ? { ok: true, stdout: r.stdout } : { ok: false, reason: `exit ${r.exitCode}` };
232
332
  }
233
333
  catch (e) {
@@ -265,6 +365,7 @@ export class RemoteContainerExecutionEnv {
265
365
  this.destroyed = true;
266
366
  this.connecting = undefined;
267
367
  this.stopKeepAlive();
368
+ this.floor.dispose();
268
369
  await this.disposeBackgroundShells();
269
370
  const sbx = this.sbx;
270
371
  this.sbx = undefined;
@@ -285,12 +386,21 @@ export class RemoteContainerExecutionEnv {
285
386
  await this.destroy();
286
387
  }
287
388
  async exec(command, options) {
389
+ return this.execAs(engineStart(options?.readFloor), command, options);
390
+ }
391
+ execInternal(command, options) {
392
+ return this.execAs(ADAPTER_START, command, options);
393
+ }
394
+ async execAs(start, command, options) {
288
395
  if (options?.abortSignal?.aborted)
289
396
  return { ok: false, error: new ExecutionError("aborted", "exec aborted before start") };
290
397
  const conn = await this.ensureConnected();
291
398
  if (!conn.ok)
292
399
  return { ok: false, error: new ExecutionError("shell_unavailable", `remote connect failed: ${conn.error.message}`, conn.error) };
293
400
  const sbx = conn.value;
401
+ const launch = this.startIn(start, command, options?.env);
402
+ if (!launch.ok)
403
+ return launch;
294
404
  this.activeCommands++;
295
405
  const detachSignal = options?.detachSignal;
296
406
  const tee = detachSignal
@@ -315,10 +425,9 @@ export class RemoteContainerExecutionEnv {
315
425
  let starting;
316
426
  let startCut = false;
317
427
  try {
318
- handle = await this.rpc("commands.run", () => (starting = sbx.commands.run(command, {
428
+ handle = await this.rpc("commands.run", () => (starting = this.runIn(sbx, launch.value, {
319
429
  background: true,
320
430
  cwd: this.resolve(options?.cwd ?? this.cwd),
321
- ...(options?.env ? { envs: options.env } : {}),
322
431
  timeoutMs: tee
323
432
  ? Math.max(this.commandTimeoutMs(options?.timeout), (E2B_BG_MAX_TIMEOUT_SEC + E2B_BG_PROVIDER_BACKSTOP_PAD_SEC) * 1000)
324
433
  : this.commandTimeoutMs(options?.timeout),
@@ -442,6 +551,9 @@ export class RemoteContainerExecutionEnv {
442
551
  if (!conn.ok)
443
552
  throw conn.error;
444
553
  const sbx = conn.value;
554
+ const launch = this.startIn(engineStart(undefined), command, options?.env);
555
+ if (!launch.ok)
556
+ throw new RemoteExecutionError("unsupported", launch.error.message, launch.error);
445
557
  const queue = [];
446
558
  let finished = false;
447
559
  let failure;
@@ -468,10 +580,9 @@ export class RemoteContainerExecutionEnv {
468
580
  let handle;
469
581
  let starting;
470
582
  try {
471
- handle = await this.rpc("commands.run", () => (starting = sbx.commands.run(command, {
583
+ handle = await this.rpc("commands.run", () => (starting = this.runIn(sbx, launch.value, {
472
584
  background: true,
473
585
  cwd: this.resolve(options?.cwd ?? this.cwd),
474
- ...(options?.env ? { envs: options.env } : {}),
475
586
  timeoutMs: this.commandTimeoutMs(options?.timeout),
476
587
  onStdout: (d) => push("stdout", d),
477
588
  onStderr: (d) => push("stderr", d),
@@ -599,12 +710,14 @@ export class RemoteContainerExecutionEnv {
599
710
  if (!conn.ok)
600
711
  throw new Error(`remote connect failed: ${conn.error.message}`);
601
712
  const sbx = conn.value;
713
+ const launch = self.startIn(engineStart(options?.readFloor), command, options?.env);
714
+ if (!launch.ok)
715
+ throw launch.error;
602
716
  const stdout = { tail: new RollingTailBuffer(), total: 0, cursor: 0 };
603
717
  const stderr = { tail: new RollingTailBuffer(), total: 0, cursor: 0 };
604
- const handle = await self.rpc("commands.run.bg", () => sbx.commands.run(command, {
718
+ const handle = await self.rpc("commands.run.bg", () => self.runIn(sbx, launch.value, {
605
719
  background: true,
606
720
  cwd: self.resolve(options?.cwd ?? self.cwd),
607
- ...(options?.env ? { envs: options.env } : {}),
608
721
  timeoutMs: (ctx.bgTimeoutSec + E2B_BG_PROVIDER_BACKSTOP_PAD_SEC) * 1000,
609
722
  onStdout: (d) => {
610
723
  const b = Buffer.from(d, "utf8");
@@ -722,7 +835,7 @@ export class RemoteContainerExecutionEnv {
722
835
  return { ok: false, error: this.fileError(e, dest, signal) };
723
836
  }
724
837
  },
725
- run: (cmd, signal) => this.exec(cmd, { cwd: "/", ...(signal !== undefined ? { abortSignal: signal } : {}) }),
838
+ run: (cmd, signal) => this.execInternal(cmd, { cwd: "/", ...(signal !== undefined ? { abortSignal: signal } : {}) }),
726
839
  };
727
840
  }
728
841
  async writeFile(p, content, abortSignal) {
@@ -764,7 +877,7 @@ export class RemoteContainerExecutionEnv {
764
877
  }
765
878
  async readLink(p, abortSignal) {
766
879
  const abs = this.resolve(p);
767
- const r = await this.exec(`readlink -- ${shellQuote(abs)}`, { abortSignal });
880
+ const r = await this.execInternal(`readlink -- ${shellQuote(abs)}`, { abortSignal });
768
881
  if (!r.ok)
769
882
  return { ok: false, error: fileErrorFromExec(r.error, abs) };
770
883
  if (r.value.exitCode !== 0)
@@ -772,11 +885,11 @@ export class RemoteContainerExecutionEnv {
772
885
  return ok(r.value.stdout.trim());
773
886
  }
774
887
  async canonicalChain(p, abortSignal) {
775
- return runSymlinkChain(this.resolve(p), (cmd) => this.exec(cmd, { abortSignal }));
888
+ return runSymlinkChain(this.resolve(p), (cmd) => this.execInternal(cmd, { abortSignal }));
776
889
  }
777
890
  async canonicalPath(p, abortSignal) {
778
891
  const abs = this.resolve(p);
779
- const r = await this.exec(`realpath -- ${shellQuote(abs)}`, { abortSignal });
892
+ const r = await this.execInternal(`realpath -- ${shellQuote(abs)}`, { abortSignal });
780
893
  if (!r.ok)
781
894
  return { ok: false, error: fileErrorFromExec(r.error, abs) };
782
895
  if (r.value.exitCode !== 0) {
@@ -823,7 +936,7 @@ export class RemoteContainerExecutionEnv {
823
936
  const abs = this.resolve(p);
824
937
  if (options?.recursive) {
825
938
  const flags = options.force ? "-rf" : "-r";
826
- const r = await this.exec(`rm ${flags} -- ${shellQuote(abs)}`, { abortSignal: options.abortSignal });
939
+ const r = await this.execInternal(`rm ${flags} -- ${shellQuote(abs)}`, { abortSignal: options.abortSignal });
827
940
  if (!r.ok)
828
941
  return { ok: false, error: fileErrorFromExec(r.error, abs) };
829
942
  if (r.value.exitCode !== 0)
@@ -842,14 +955,14 @@ export class RemoteContainerExecutionEnv {
842
955
  }
843
956
  }
844
957
  async createTempDir(prefix = "tmp-", abortSignal) {
845
- const r = await this.exec(`mktemp -d -t ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal });
958
+ const r = await this.execInternal(`mktemp -d -t ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal });
846
959
  return this.tempResult(r, "mktemp -d");
847
960
  }
848
961
  async createTempFile(options) {
849
962
  const prefix = options?.prefix ?? "";
850
963
  const suffix = options?.suffix ?? "";
851
964
  const suffixArg = suffix ? `--suffix=${shellQuote(suffix)} -t` : "-t";
852
- const r = await this.exec(`mktemp ${suffixArg} ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal: options?.abortSignal });
965
+ const r = await this.execInternal(`mktemp ${suffixArg} ${shellQuote(`${prefix}XXXXXX`)}`, { abortSignal: options?.abortSignal });
853
966
  return this.tempResult(r, "mktemp");
854
967
  }
855
968
  resolve(p) {
@@ -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,8 +298,10 @@ export type SandboxLeaseWrite = {
290
298
  readonly message: string;
291
299
  };
292
300
  export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, BackgroundShellCapability {
293
- /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。快照店是它的输入(构造期铸,见下)。 */
301
+ /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。快照店与读地板旋钮是它的输入(构造期铸,见下);{@link floor} 由同一只判据、同一个旋钮值选出。 */
294
302
  readonly capabilities: ExecutionEnvCapabilities;
303
+ /** 读拒表字节地板模块:pod 里每一次 exec 起动都先经它的 `wrap`({@link runExec},唯一接缝)。 */
304
+ private readonly floor;
295
305
  cwd: string;
296
306
  private podName?;
297
307
  /** Cluster-internal pod address, captured when the pod reaches Running — the LSP `getHost` surface.
@@ -731,6 +741,15 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
731
741
  * being claimed) uploads nothing, and is that same upload failure.
732
742
  */
733
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 通道。 */
734
753
  private execUrl;
735
754
  /**
736
755
  * Run a command over the exec WebSocket. Frames: byte0=channel (1 stdout / 2 stderr / 3 status JSON with the