@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
@@ -1,9 +1,12 @@
1
1
  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
+ import { capabilitiesFor } from "../execution-lane-caps.js";
5
+ import { LANE_HOME_PROBE_COMMAND, LaneHomeSeat } from "../lane-home-probe.js";
4
6
  import { fileErrorFromExec } from "./remote-env-file-error.js";
5
- import { numEnvOr, runSymlinkChain, shellQuote } from "./remote-shell.js";
7
+ import { appendFileStaged, rewriteAppend, numEnvOr, runSymlinkChain, shellQuote, writeFileStaged } from "./remote-shell.js";
6
8
  import { buildLivenessCut, commandBoundsOf, firstBoundOf, laneLivenessOf, LIVENESS_MS_DEFAULT } from "../exec-bounds.js";
9
+ import { createCutError } from "../exec-cut.js";
7
10
  import { BackgroundShellManager } from "./background-shell-support.js";
8
11
  const PROVIDER = "e2b";
9
12
  const DEFAULT_MOUNT_PATH = "/home/user";
@@ -18,7 +21,9 @@ const E2B_BG_PROVIDER_BACKSTOP_PAD_SEC = 30;
18
21
  const ok = (value) => ({ ok: true, value });
19
22
  export class RemoteContainerExecutionEnv {
20
23
  cwd;
21
- homeDir;
24
+ get homeDir() {
25
+ return this.homeSeat.home;
26
+ }
22
27
  cdPath = "unknown";
23
28
  withheldEnv = [];
24
29
  cfg;
@@ -28,6 +33,8 @@ export class RemoteContainerExecutionEnv {
28
33
  keepAlive;
29
34
  handle;
30
35
  destroyed = false;
36
+ homeSeat;
37
+ homeRefusal;
31
38
  connecting;
32
39
  activeCommands = 0;
33
40
  paused = false;
@@ -43,10 +50,10 @@ export class RemoteContainerExecutionEnv {
43
50
  };
44
51
  this.laneLiveness = laneLivenessOf(this.cfg.livenessMs, "E2B_LIVENESS_MS");
45
52
  this.cwd = this.cfg.mountPath;
46
- this.homeDir = config.homeDir;
53
+ this.homeSeat = new LaneHomeSeat(PROVIDER, config);
47
54
  this.sandboxApi = deps.sandboxApi ?? Sandbox;
48
55
  }
49
- capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true, sharesProcessTable: false };
56
+ capabilities = capabilitiesFor({ remoteExec: { provider: "e2b" } });
50
57
  workspaceHandle() {
51
58
  if (!this.handle) {
52
59
  throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
@@ -68,6 +75,8 @@ export class RemoteContainerExecutionEnv {
68
75
  }
69
76
  if (this.destroyed)
70
77
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
78
+ if (this.homeRefusal)
79
+ return { ok: false, error: this.homeRefusal };
71
80
  if (this.paused)
72
81
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env is suspended (paused) — resumeVM(snapshotId) before use") };
73
82
  if (this.sbx)
@@ -94,6 +103,12 @@ export class RemoteContainerExecutionEnv {
94
103
  await sbx.kill().catch(() => { });
95
104
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
96
105
  }
106
+ const home = await this.homeVerdict(sbx, config?.abortSignal);
107
+ if (home !== undefined || this.destroyed) {
108
+ await sbx.kill().catch(() => { });
109
+ this.cfg.metrics?.inc("remote_env_connect_total", { provider: PROVIDER, result: "error" });
110
+ return { ok: false, error: home ?? new RemoteExecutionError("connect_failed", "execution env destroyed during connect") };
111
+ }
97
112
  this.sbx = sbx;
98
113
  this.paused = false;
99
114
  this.startKeepAlive();
@@ -146,6 +161,8 @@ export class RemoteContainerExecutionEnv {
146
161
  async resumeVM(snapshotId, options) {
147
162
  if (this.destroyed)
148
163
  return { ok: false, error: new RemoteExecutionError("connect_failed", "env already destroyed") };
164
+ if (this.homeRefusal)
165
+ return { ok: false, error: this.homeRefusal };
149
166
  if (this.sbx && !this.paused && this.sbx.sandboxId === snapshotId)
150
167
  return ok(this.workspaceHandle());
151
168
  if (this.connecting)
@@ -163,6 +180,16 @@ export class RemoteContainerExecutionEnv {
163
180
  await sbx.kill().catch(() => { });
164
181
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
165
182
  }
183
+ const home = await this.homeVerdict(sbx, options?.abortSignal);
184
+ if (this.destroyed) {
185
+ await sbx.kill().catch(() => { });
186
+ return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env destroyed during resume") };
187
+ }
188
+ if (home !== undefined) {
189
+ await this.rpc("sandbox.pause", (signal) => sbx.pause({ signal }), undefined, this.cfg.dataTimeoutMs).catch((e) => this.cfg.logger?.warn?.("remote_env_home_refused_repause_failed", { sandboxId: sbx.sandboxId, error: errMsg(e) }));
190
+ this.cfg.metrics?.inc("remote_env_resume_total", { provider: PROVIDER, result: "error" });
191
+ return { ok: false, error: home };
192
+ }
166
193
  this.sbx = sbx;
167
194
  this.paused = false;
168
195
  this.startKeepAlive();
@@ -195,6 +222,27 @@ export class RemoteContainerExecutionEnv {
195
222
  return { ok: false, error: this.remoteError(e, "post_resume_failed") };
196
223
  }
197
224
  }
225
+ async homeVerdict(sbx, signal) {
226
+ if (!this.homeSeat.asks)
227
+ return undefined;
228
+ let probe;
229
+ try {
230
+ const r = await this.rpc("sandbox.home", (s) => sbx.commands.run(LANE_HOME_PROBE_COMMAND, { signal: s }), signal);
231
+ probe = r.exitCode === 0 ? { ok: true, stdout: r.stdout } : { ok: false, reason: `exit ${r.exitCode}` };
232
+ }
233
+ catch (e) {
234
+ probe = { ok: false, reason: e instanceof CommandExitError ? `exit ${e.exitCode}` : errMsg(e) };
235
+ }
236
+ if (signal?.aborted)
237
+ return new RemoteExecutionError("aborted", "connect aborted while reading the sandbox's home");
238
+ const verdict = this.homeSeat.settle(probe);
239
+ if (verdict === undefined)
240
+ return undefined;
241
+ const error = new RemoteExecutionError("connect_failed", verdict.refusal);
242
+ if (verdict.answered)
243
+ this.homeRefusal = error;
244
+ return error;
245
+ }
198
246
  startKeepAlive() {
199
247
  this.stopKeepAlive();
200
248
  const everyMs = Math.max(30_000, Math.floor(this.cfg.timeoutMs * 0.8));
@@ -251,6 +299,8 @@ export class RemoteContainerExecutionEnv {
251
299
  stderr: { tail: new RollingTailBuffer(), total: 0, cursor: 0 },
252
300
  }
253
301
  : undefined;
302
+ let started;
303
+ const captured = () => ({ stdout: started?.stdout ?? "", stderr: started?.stderr ?? "" });
254
304
  let detached = false;
255
305
  const teePush = (s, d) => {
256
306
  const b = Buffer.from(d, "utf8");
@@ -299,10 +349,10 @@ export class RemoteContainerExecutionEnv {
299
349
  this.settleAbandonedStart(starting);
300
350
  const cut = e instanceof RemoteExecutionError && e.code === "timeout" && !options?.abortSignal?.aborted;
301
351
  if (!cut)
302
- return { ok: false, error: this.execError(e, options?.abortSignal) };
303
- const bc = boundCut(first);
304
- return { ok: false, error: new ExecutionError(bc.code, bc.message, undefined, undefined, bc.noOutputSec) };
352
+ return { ok: false, error: this.execError(e, options?.abortSignal, captured()) };
353
+ return { ok: false, error: createCutError(boundCut(first), captured()) };
305
354
  }
355
+ started = handle;
306
356
  const onAbort = () => { if (!detached)
307
357
  void handle.kill().catch(() => { }); };
308
358
  if (options?.abortSignal) {
@@ -352,6 +402,7 @@ export class RemoteContainerExecutionEnv {
352
402
  disarmed: () => detached,
353
403
  ...(tee ? { wall: { totalMs: this.commandTimeoutMs(options?.timeout), leftMs: Math.max(0, this.commandTimeoutMs(options?.timeout) - (Date.now() - startedAt)) } } : {}),
354
404
  liveness: bounds.liveness,
405
+ captured,
355
406
  });
356
407
  const winner = await (detachedP ? Promise.race([fg, detachedP]) : fg);
357
408
  if (winner === "detached") {
@@ -365,18 +416,18 @@ export class RemoteContainerExecutionEnv {
365
416
  });
366
417
  }
367
418
  if (options?.abortSignal?.aborted && !detached) {
368
- return { ok: false, error: new ExecutionError("aborted", "exec aborted (remote process killed)") };
419
+ return { ok: false, error: createCutError({ code: "aborted", message: "exec aborted (remote process killed)" }, captured()) };
369
420
  }
370
421
  return ok({ stdout: winner.stdout, stderr: winner.stderr, exitCode: winner.exitCode });
371
422
  }
372
423
  catch (e) {
373
424
  if (options?.abortSignal?.aborted && !detached) {
374
- return { ok: false, error: this.execError(e, options.abortSignal) };
425
+ return { ok: false, error: this.execError(e, options.abortSignal, captured()) };
375
426
  }
376
427
  if (e instanceof CommandExitError) {
377
428
  return ok({ stdout: e.stdout, stderr: e.stderr, exitCode: e.exitCode });
378
429
  }
379
- return { ok: false, error: this.execError(e, options?.abortSignal) };
430
+ return { ok: false, error: this.execError(e, options?.abortSignal, captured()) };
380
431
  }
381
432
  finally {
382
433
  fgSettled = true;
@@ -659,37 +710,33 @@ export class RemoteContainerExecutionEnv {
659
710
  return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
660
711
  }
661
712
  }
713
+ stagedLane(sbx, content) {
714
+ const data = typeof content === "string" ? content : toArrayBuffer(content);
715
+ return {
716
+ upload: async (dest, signal) => {
717
+ try {
718
+ await this.rpc("files.write", (s) => sbx.files.write(dest, data, { signal: s }), signal, this.cfg.dataTimeoutMs);
719
+ return ok(undefined);
720
+ }
721
+ catch (e) {
722
+ return { ok: false, error: this.fileError(e, dest, signal) };
723
+ }
724
+ },
725
+ run: (cmd, signal) => this.exec(cmd, { cwd: "/", ...(signal !== undefined ? { abortSignal: signal } : {}) }),
726
+ };
727
+ }
662
728
  async writeFile(p, content, abortSignal) {
663
729
  const sbx = await this.fsReady(p);
664
730
  if (!sbx.ok)
665
731
  return sbx;
666
- try {
667
- const data = typeof content === "string" ? content : toArrayBuffer(content);
668
- await this.rpc("files.write", (signal) => sbx.value.files.write(this.resolve(p), data, { signal }), abortSignal, this.cfg.dataTimeoutMs);
669
- return ok(undefined);
670
- }
671
- catch (e) {
672
- return { ok: false, error: this.fileError(e, this.resolve(p), abortSignal) };
673
- }
732
+ return writeFileStaged(this.stagedLane(sbx.value, content), this.resolve(p), abortSignal);
674
733
  }
675
734
  async appendFile(p, content, abortSignal) {
735
+ const sbx = await this.fsReady(p);
736
+ if (!sbx.ok)
737
+ return sbx;
676
738
  const abs = this.resolve(p);
677
- const existing = await this.readBinaryFile(abs, abortSignal);
678
- let base;
679
- if (existing.ok) {
680
- base = existing.value;
681
- }
682
- else if (existing.error.code === "not_found") {
683
- base = new Uint8Array(0);
684
- }
685
- else {
686
- return existing;
687
- }
688
- const add = typeof content === "string" ? new TextEncoder().encode(content) : content;
689
- const merged = new Uint8Array(base.length + add.length);
690
- merged.set(base, 0);
691
- merged.set(add, base.length);
692
- return this.writeFile(abs, merged, abortSignal);
739
+ return appendFileStaged(this.stagedLane(sbx.value, content), abs, abortSignal, (ctx) => rewriteAppend((signal) => this.readBinaryFile(abs, signal), (bytes, signal) => this.stagedLane(sbx.value, bytes).upload(abs, signal), content, ctx));
693
740
  }
694
741
  async fileInfo(p, abortSignal) {
695
742
  const sbx = await this.fsReady(p);
@@ -851,16 +898,15 @@ export class RemoteContainerExecutionEnv {
851
898
  async waitWithLiveness(handle, lastProgress, opts) {
852
899
  let timer;
853
900
  let wallTimer;
854
- const liveness = opts?.liveness;
901
+ const liveness = opts.liveness;
855
902
  const idle = new Promise((_, reject) => {
856
903
  const arm = (bound) => {
857
904
  timer = setTimeout(() => {
858
- if (opts?.disarmed?.())
905
+ if (opts.disarmed?.())
859
906
  return;
860
907
  const idleFor = Date.now() - lastProgress();
861
908
  if (idleFor >= bound.ms) {
862
- const cut = buildLivenessCut(bound);
863
- reject(new ExecutionError(cut.code, cut.message, undefined, undefined, cut.noOutputSec));
909
+ reject(createCutError(buildLivenessCut(bound), opts.captured()));
864
910
  void handle.kill().catch(() => { });
865
911
  this.cfg.metrics?.inc("remote_env_exec_idle_timeout_total", { provider: PROVIDER });
866
912
  }
@@ -871,18 +917,18 @@ export class RemoteContainerExecutionEnv {
871
917
  };
872
918
  if (liveness !== undefined)
873
919
  arm(liveness);
874
- if (opts?.wall !== undefined) {
920
+ if (opts.wall !== undefined) {
875
921
  const wall = opts.wall;
876
922
  wallTimer = setTimeout(() => {
877
923
  if (opts.disarmed?.())
878
924
  return;
879
- reject(new ExecutionError("timeout", wallCutMessage(wall.totalMs)));
925
+ reject(createCutError({ code: "timeout", message: wallCutMessage(wall.totalMs) }, opts.captured()));
880
926
  void handle.kill().catch(() => { });
881
927
  }, wall.leftMs);
882
928
  }
883
929
  });
884
930
  try {
885
- return await Promise.race([opts?.waitP ?? handle.wait(), idle]);
931
+ return await Promise.race([opts.waitP ?? handle.wait(), idle]);
886
932
  }
887
933
  finally {
888
934
  if (timer)
@@ -906,7 +952,7 @@ export class RemoteContainerExecutionEnv {
906
952
  async fsReady(p) {
907
953
  const conn = await this.ensureConnected();
908
954
  if (!conn.ok)
909
- return { ok: false, error: new FileError("unknown", `remote connect failed: ${conn.error.message}`, this.resolve(p), conn.error) };
955
+ return { ok: false, error: fileErrorFromExec(conn.error, this.resolve(p)) };
910
956
  return ok(conn.value);
911
957
  }
912
958
  tempResult(r, op) {
@@ -953,15 +999,15 @@ export class RemoteContainerExecutionEnv {
953
999
  return new FileError("is_directory", msg, p);
954
1000
  return new FileError("unknown", msg, p);
955
1001
  }
956
- execError(e, abortSignal) {
1002
+ execError(e, abortSignal, captured) {
957
1003
  if (e instanceof ExecutionError)
958
1004
  return e;
959
1005
  const msg = errMsg(e);
960
1006
  const name = e?.name;
961
1007
  if (abortSignal?.aborted || name === "AbortError")
962
- return new ExecutionError("aborted", msg);
1008
+ return createCutError({ code: "aborted", message: msg }, captured);
963
1009
  if (providerTimedOut(e))
964
- return new ExecutionError("timeout", msg);
1010
+ return createCutError({ code: "timeout", message: msg }, captured);
965
1011
  return new ExecutionError("unknown", msg, e instanceof Error ? e : undefined);
966
1012
  }
967
1013
  remoteError(e, code = "unknown") {
@@ -15,9 +15,11 @@
15
15
  * 那条教训:宣布"类修完成"而三个实现只落了一个)。收成一处之后,判据只有一条 ——
16
16
  * **文件面的 exec 包装必须走这里**,而这条能被机器检查(见 `test/env-adapter-file-error-parity.test.ts`)。
17
17
  */
18
- import { FileError, type ExecutionError, type RemoteExecutionError } from "@sema-agent/core";
18
+ import { FileError, RemoteExecutionError, type ExecutionError } from "@sema-agent/core";
19
19
  /** exec 层的错误码 → 文件层的码。只映射**有对应语义**的那些,其余落 `unknown`(诚实降级)。 */
20
20
  export declare function fileErrorFromExec(e: ExecutionError | RemoteExecutionError, path?: string): FileError;
21
+ /** POSIX 的一个名字段上限(NAME_MAX,字节;Linux / macOS / 常见文件系统同值)。也是远端写的私有暂存目录名的上限(`remote-shell.ts` `buildStagingPaths`)。 */
22
+ export declare const NAME_MAX_BYTES = 255;
21
23
  /**
22
24
  * core 7.56.0 [ref]:`ENAMETOOLONG` 的**一只读法**(本文件的 stderr 分类器与 host 车道的 errno 映射同调)—— 路径有一段超过 NAME_MAX(按 UTF-8 字节量)⇒
23
25
  * `name_too_long`:没有目录装得下这个名字,引擎读站把它读作缺席(与 `not_found` 同读);段都短、只是整条超 PATH_MAX ⇒ `unknown`(那种文件可能存在,从下层目录够得着)。
@@ -1,9 +1,15 @@
1
- import { FileError } from "@sema-agent/core";
1
+ import { FileError, RemoteExecutionError } from "@sema-agent/core";
2
+ function neverOpened(e) {
3
+ if (e.code === "connect_failed")
4
+ return true;
5
+ const cause = e.cause;
6
+ return cause instanceof RemoteExecutionError && cause.code === "connect_failed";
7
+ }
2
8
  export function fileErrorFromExec(e, path) {
3
- const code = e.code === "aborted" ? "aborted" : "unknown";
9
+ const code = e.code === "aborted" ? "aborted" : neverOpened(e) ? "target_unavailable" : "unknown";
4
10
  return new FileError(code, e.message, path, e);
5
11
  }
6
- const NAME_MAX_BYTES = 255;
12
+ export const NAME_MAX_BYTES = 255;
7
13
  export function nameTooLongCode(p) {
8
14
  return p !== undefined && p.split(/[\\/]/).some((part) => Buffer.byteLength(part, "utf8") > NAME_MAX_BYTES) ? "name_too_long" : "unknown";
9
15
  }
@@ -1,4 +1,5 @@
1
1
  import { FileError, type InheritEnvPolicy, ExecutionError, RemoteExecutionError, SchedulerError, type SchedulerCapability, type ScheduledIntent, type SchedulerContext, type ScheduledTaskId, type ScheduledTaskSummary, type ExecutionEnv, type RemoteExecutionEnv, type WorkspaceHandle, type FileInfo, type Result, type SymlinkChain, type BoundFileObject, type OutputChunk, type ExecStreamOptions, type RemoteConnectConfig, type SnapshotId, type SessionToken, type VmLifecycleOptions, type ExecutionEnvFactory, BackgroundShellError, type BackgroundShellCapability, type BackgroundShellId, type BackgroundHardKillOutcome, type BackgroundPoll, type BackgroundSpawnOptions, type SecretEnvFinding } from "@sema-agent/core";
2
+ import { type LaneHomeConfig } from "../lane-home-probe.js";
2
3
  /** Options accepted by `ExecutionEnv.exec` (core does not re-export the type; derive it from the seam). */
3
4
  type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
4
5
  /** SIGTERM → grace → SIGKILL (no host isolation). 🔴 宽限的**唯一属主**:本 driver 的 `kill` 梯子用它排升级定时器;会话级收敛
@@ -6,7 +7,7 @@ type ExecOpts = Parameters<ExecutionEnv["exec"]>[1];
6
7
  * 这把梯子的用户面宽限取消),不另抄数。启动期读一次;`0` 被 `numEnvOr` 读作缺席(回落 1000),要几乎不等写 `1`。 */
7
8
  export declare const HOST_BG_KILL_GRACE_MS: number;
8
9
  /** Construction config for {@link RemoteHostExecutionEnv}. */
9
- export interface HostEnvConfig {
10
+ export interface HostEnvConfig extends LaneHomeConfig {
10
11
  /**
11
12
  * Base directory under which this adapter creates its per-task working directory. Default {@link os.tmpdir}.
12
13
  * The actual workspace is `<workspaceBase>/sema-host-<id>` and is `rm -rf`'d on {@link destroy}.
@@ -24,15 +25,6 @@ export interface HostEnvConfig {
24
25
  * own `process.env` is the default base (a local coding agent inherits the user's environment).
25
26
  */
26
27
  env?: Record<string, string>;
27
- /**
28
- * [ref]④ / core [ref] —— 本执行环境的 **home 目录**(`ExecutionEnv.homeDir` 座,绝对路径,本环境自己的
29
- * 命名空间里)。`~/…` 形权限规则(`Edit(~/.ssh/**)`)对**跑在本 env 里的调用**就以它为基。
30
- * 缺席 ⇒ 本 env 声明「我没有 home」,core 对 `~/` 规则判 `unreadable`(fail-closed 的 ask),
31
- * 绝不回落引擎进程自己的 home(那在远端腿上是另一个用户的另一个目录 —— 静默守错目录正是 [ref] 的病)。
32
- * 值由 `boot/execution-env.ts` 从**唯一属主** `execution-lane-caps.ts` 的 `executionLaneHomeDir` 递进来;
33
- * adapter 不自算(两处各算一份 = 422 门与引擎对同一条规则给相反答案)。
34
- */
35
- homeDir?: string;
36
28
  /** Per-command wall-clock bound (ms) when the caller gives none. Default {@link DEFAULT_COMMAND_TIMEOUT_MS}. */
37
29
  commandTimeoutMs?: number;
38
30
  /** Shell-child env policy — core's `InheritEnvPolicy`, read by core's `inheritedHostEnv` (the three words are judged
@@ -64,24 +56,8 @@ export interface HostEnvConfig {
64
56
  backgroundShell?: boolean;
65
57
  }
66
58
  export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, SchedulerCapability, BackgroundShellCapability {
67
- /** [ref] §9 / DUAL-MODE-DESIGN §5: a `host` env is the operator's real machine — no isolation, no snapshot. */
68
- /**
69
- * 🔴 **[ref] / core 7.20.0([ref])`canonicalPathAuthoritative: true`** —— 本适配器的 `canonicalPath`
70
- * 就是**命令真正跑的那块文件系统**的权威答案(worker 本机的 `fs.realpath`)。声明它 = 告诉引擎:整链答不出来是**解析器出了事**
71
- * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
72
- * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
73
- * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
74
- * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
75
- * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
76
- * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
77
- */
78
- /** `sharesProcessTable`(core 7.57.0,必填):命令是本进程 spawn 的子进程(同一张进程表),引擎的模式杀守卫对它照 NodeExecutionEnv 一样问 `pgrep`。 */
79
- readonly capabilities: {
80
- isolation: boolean;
81
- suspendable: boolean;
82
- canonicalPathAuthoritative: boolean;
83
- sharesProcessTable: boolean;
84
- };
59
+ /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。 */
60
+ readonly capabilities: import("@sema-agent/core").ExecutionEnvCapabilities;
85
61
  /** R7 自唤醒: structural delegation to the injected scheduler backend. `hasScheduler(env)` checks BOTH the method
86
62
  * presence (always, since we implement the interface) AND `schedulerCapabilities.supported===true` — which is only
87
63
  * true when a backend was injected, so a host env without one stays INERT (the four scheduler tools don't mount). */
@@ -93,8 +69,13 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
93
69
  list(ctx: SchedulerContext): Promise<Result<ScheduledTaskSummary[], SchedulerError>>;
94
70
  /** Working directory; relative paths resolve against it (ExecutionEnv contract). Starts at the workspace root. */
95
71
  cwd: string;
96
- /** core `ExecutionEnv.homeDir`([ref]):本沙箱/目标机上执行用户的 home。见配置同名字段。 */
97
- readonly homeDir?: string | undefined;
72
+ /** core `ExecutionEnv.homeDir`([ref]):本机执行用户的 home —— 主张在场 ⇒ 主张(构造时与子进程拿到的 `HOME` 对证过),否则构造时读到的那个 `HOME`,见 {@link LaneHomeSeat}。 */
73
+ get homeDir(): string | undefined;
74
+ /** 家目录的座(主张在场 ⇒ 构造时对证 · 否则构造时实测)。读数 = {@link childHome}:本类自己组合给子进程的那份 env,不 spawn。 */
75
+ private readonly homeSeat;
76
+ /** 家目录:主张与子进程拿到的 `HOME` 对不上 ⇒ 这只环境的拒句;`shellReady` / `fsReady` / `connect` 三道门原样回它,一条命令、一次文件动作都不做。
77
+ * 读数是本类自己组合的 env(纯函数,同一份输入同一个答案)⇒ 构造时判一次、记一辈子。 */
78
+ private readonly homeRefusal;
98
79
  /**
99
80
  * core 7.49.0 [ref]:shell 子进程**真拿到的** `CDPATH` 条目(`[]` = 没有)—— 删除守卫按它解裸 `cd <dir>`。本车道的命令就是本机子进程,
100
81
  * env 是本类自己组合的(继承姿态 + `cfg.env`),所以说得出:读同一只组合({@link childEnv} 的**读面**,不披露、不钉 shell),切法与 core
@@ -299,8 +280,9 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
299
280
  maxLines?: number;
300
281
  abortSignal?: AbortSignal;
301
282
  }): Promise<Result<string[], FileError>>;
302
- /** core `NodeExecutionEnv` 的绑定读器 —— 首次 {@link openBound} 时建(构造无副作用,只记 cwd)。 */
303
- private boundReader;
283
+ /** core `NodeExecutionEnv` 的一只实例 —— 本车道把**落盘那一步**委托给它的两只面({@link openBound} 的持有读、{@link writeFile} 的整文件替换);
284
+ * 首次用时建(构造无副作用,只记 cwd)。门(`fsReady`)、在飞登记(`withInflightFs`)与工作区相对解析留在本适配器。 */
285
+ private coreFs;
304
286
  /**
305
287
  * core 7.50.0 `FileSystem.openBound`:开着并**持有**一只对象(叶链接不跟,开后身份对拍),调用方判的与读的是同一只对象 ——
306
288
  * LSP 会话的读口与路径附件只经它读,不实现 ⇒ 两者在本车道一律拒。
@@ -310,8 +292,15 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
310
292
  */
311
293
  openBound(p: string, abortSignal?: AbortSignal): Promise<Result<BoundFileObject, FileError>>;
312
294
  readBinaryFile(p: string, abortSignal?: AbortSignal): Promise<Result<Uint8Array, FileError>>;
313
- /** [ref]:整只方法体(**含** `fsReady` 那道门)走 `withInflightFs` —— 登记必须与调用方同一同步段,
314
- * 否则 destroy 的快照可能刚好在 `await this.fsReady(...)` 的让出点上取完。见那处头注。 */
295
+ /**
296
+ * 整文件替换:**整旧或整新;`aborted` ⇒ 目标未碰**。
297
+ * 🔴 **不重写**:落盘那一步委托 core `NodeExecutionEnv.writeFile`(同目录暂存 + fsync + rename;父目录自建;单跳链接改写它指的
298
+ * 常规文件、链接链 / 非常规文件 / rename 不可用的文件系统回落就地写;已有文件的权限位带过去)—— 该原语的唯一属主在 core。
299
+ * 本适配器只给它与其余写面同一的入口纪律:[ref] 在飞登记(整只方法体**含** `fsReady` 那道门走 `withInflightFs` —— 登记必须与
300
+ * 调用方同一同步段,否则 destroy 的快照可能刚好在 `await this.fsReady(...)` 的让出点上取完;见那处头注)、销毁后拒、工作区相对解析。
301
+ * 修前这里是裸 `fs.writeFile(abs, data, { signal })`:open 即 O_TRUNC,中途取消 / 出错 ⇒ 目标 = 新内容的前缀,原件不可恢复。
302
+ * 已知语义变化(与 core / CC 同形):rename 断开硬链接 —— 另一条链接留旧内容。
303
+ */
315
304
  writeFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
316
305
  /** [ref](写面一律 `withInflightFs`,见那处头注)。本面是唯一能在 `destroyed` **之后**过门的写面
317
306
  * (`lingeringBgWrite`):那一族登记在 destroy 的快照**之后**,所以 destroy 不会去等它们 —— 正确,
@@ -441,6 +430,14 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
441
430
  * 引擎对点到自有凭据名的白名单在这里**抛**(spawn 时的拒;构造器已先问过同一只判)—— 子进程不起,拒句带名。
442
431
  */
443
432
  private childEnv;
433
+ /**
434
+ * 家目录的读数:本类组合给子进程的那份 env 里的 `HOME`(组合层,不 spawn、不记剥除 —— 与 {@link cdPath} 同一只读面)。env 里没有 `HOME` ⇒ 读
435
+ * 账户的家目录(`os.userInfo().homedir`):那正是子进程的 shell 对缺席 `HOME` 的 `~` 展开(POSIX 取账户 passwd 行;win32 是账户的配置目录)。
436
+ * 不读 `os.homedir()`:它先读**服务进程自己的** `HOME` —— 子进程没继承到的那个值(对抗轮 r1 [high] 实测:服务 `HOME` 指别处 + `inheritEnv: []` 时两者分叉)。
437
+ * 账户查不到(无 passwd 行)⇒ 读不出(有主张 ⇒ 不开;没主张 ⇒ 座缺席 + warn)。
438
+ * 残余(引擎 `NodeExecutionEnv.homeDir` 注同一句):POSIX 上 `HOME` 缺席时 shell 里的 `$HOME` 字面量展开为空,与 `~` 不是一个值 —— 本读数按 `~` 记。
439
+ */
440
+ private childHome;
444
441
  private fileError;
445
442
  }
446
443
  /**