@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,6 +1,7 @@
1
1
  import { emitLeafAdvance } from "../session-leaf-bus.js";
2
2
  import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, admitSessionPlacement, } from "@sema-agent/core";
3
- import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
3
+ import { SESSION_TITLE_MAX_CHARS, sessionRowTagKey } from "../session-row.js";
4
+ import { writeSessionRowCellsSql } from "./session-row-sql.js";
4
5
  import { escapeLike } from "./sql-escape.js";
5
6
  import { decodeSessionEventPayload } from "./session-event-payload.js";
6
7
  import { scanEpochAnchor } from "./session-epoch-anchor.js";
@@ -16,11 +17,15 @@ import { isPgUniqueViolation } from "./sql-errors.js";
16
17
  import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
17
18
  import { SESSION_META_WORKSPACE_COLUMN, decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
18
19
  import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
19
- import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
20
+ import { SESSION_META_INCARNATION_COLUMN, carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
20
21
  import { ensureIndexes } from "./ensure-index.js";
21
22
  import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
22
23
  import { selectForkRows } from "@sema-agent/core";
23
- import { FORK_LOG_ROW_SHAPE, forkPointOf } from "./session-fork.js";
24
+ import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "./session-fork.js";
25
+ import { pgDriver } from "./sql-driver.js";
26
+ import { MIRROR_ON_COMMIT_GUARD, MIRROR_ON_COMMIT_SET, SESSION_META_PLACEMENT_READ_COLUMNS, createSqlExecutionPlacementSeat, importedRowCells, lockRootAndReadClaim, placementCellFromRow, readPlacementRowStatement, sessionMetaPlacementColumnsDdl } from "./execution-placement-sql.js";
27
+ import { SessionActiveRunError, SessionWriterPresentError } from "../execution-placement.js";
28
+ const rowsOnClient = (conn) => async (sql, values) => (await conn.query(sql, [...values])).rows;
24
29
  const isDupKey = isPgUniqueViolation;
25
30
  function baselineQuery(text, values) {
26
31
  return { text, values, query_timeout: BASELINE_IO_DEADLINE_MS };
@@ -34,7 +39,7 @@ export const PG_SESSION_SCHEMA = [
34
39
  leaf_id VARCHAR(64) COLLATE "C",
35
40
  leaf_seq BIGINT NOT NULL DEFAULT 0,
36
41
  -- title: one column, two writers (TiDB twin, same name / width; law in src/session-row.ts): the user's write
37
- -- (patchSessionMeta, unconditional; null = cleared back to automatic) and the auto-titler (setTitleIfNull / probeTitle
42
+ -- (writeSessionRowCells, unconditional unless the PATCH carries expectedRowTag; null = cleared back to automatic) and the auto-titler (setTitleIfNull / probeTitle
38
43
  -- — fills NULL only; NULL = "untitled", the three-state probe distinguishes it from "no such session").
39
44
  title VARCHAR(${SESSION_TITLE_MAX_CHARS}) COLLATE "C",
40
45
  -- pinned: the user's pin (TiDB twin); every list row carries it, false by default.
@@ -56,6 +61,8 @@ export const PG_SESSION_SCHEMA = [
56
61
  placed_at TIMESTAMPTZ(3),
57
62
  -- S-790 / S-506:会话化身(TiDB 孪生同名同义;列定义唯一属主 = src/session-incarnation.ts 的 SESSION_META_INCARNATION_COLUMN)。
58
63
  ${SESSION_META_INCARNATION_COLUMN.pg},
64
+ -- 执行放置(根会话行上这一位住客的一格:execution_* 七列)+ fork 键(fork_key)—— TiDB 孪生同名同义;列定义与语句的唯一属主 = src/plugins/execution-placement-sql.ts。
65
+ ${sessionMetaPlacementColumnsDdl("pg")},
59
66
  PRIMARY KEY (session_id)
60
67
  )`,
61
68
  `CREATE TABLE IF NOT EXISTS session_event (
@@ -184,10 +191,14 @@ export class PgSessionStore {
184
191
  placements = SQL_SESSION_PLACEMENTS;
185
192
  pending = new Map();
186
193
  log;
194
+ executionPlacement;
195
+ db;
187
196
  constructor(pool, opts = {}) {
188
197
  this.pool = pool;
189
198
  this.log = opts.log ?? createLogger();
190
199
  this.retention = opts.retention ?? UNMANAGED_RETENTION;
200
+ this.db = pgDriver(pool);
201
+ this.executionPlacement = createSqlExecutionPlacementSeat(this.db);
191
202
  }
192
203
  async acquire(sessionId, opts) {
193
204
  if (!sessionId) {
@@ -341,38 +352,25 @@ export class PgSessionStore {
341
352
  const sid = ph(opts.cursor.sessionId);
342
353
  where.push(`(sm.updated_at < ${a} OR (sm.updated_at = ${b} AND sm.session_id < ${sid}))`);
343
354
  }
344
- return this.selectSummaries({ where, params, limit: opts.limit });
355
+ return (await this.selectListRows({ where, params, limit: opts.limit })).map((a) => a.row);
345
356
  }
346
357
  async sessionSummaryOf(sessionId) {
347
- const rows = await this.selectSummaries({ where: ["sm.placement_kind IS NULL", "sm.session_id = $1"], params: [sessionId], limit: 1, runSessionId: sessionId });
348
- return rows[0];
358
+ return (await this.listRowOf(sessionId))?.row;
349
359
  }
350
- async patchSessionMeta(sessionId, patch, face) {
351
- const sets = [];
352
- const params = [];
353
- if (patch.title !== undefined) {
354
- params.push(patch.title);
355
- sets.push(`title = $${params.length}`);
356
- }
357
- if (patch.pinned !== undefined) {
358
- params.push(patch.pinned);
359
- sets.push(`pinned = $${params.length}`);
360
- }
361
- if (sets.length > 0) {
362
- params.push(sessionId, face.incarnation);
363
- await this.pool.query(`UPDATE session_meta SET ${sets.join(", ")} WHERE session_id = $${params.length - 1} AND incarnation = $${params.length} AND placement_kind IS NULL`, params);
364
- }
365
- const rows = await this.selectSummaries({ where: ["sm.placement_kind IS NULL", "sm.session_id = $1", "sm.incarnation = $2"], params: [sessionId, face.incarnation], limit: 1, runSessionId: sessionId });
366
- return rows[0];
360
+ async listRowOf(sessionId) {
361
+ return (await this.selectListRows({ where: ["sm.placement_kind IS NULL", "sm.session_id = $1"], params: [sessionId], limit: 1, runSessionId: sessionId }))[0];
367
362
  }
368
- async selectSummaries(q) {
363
+ async writeSessionRowCells(sessionId, cells, how) {
364
+ return writeSessionRowCellsSql(this.db, sessionId, cells, how, (id) => this.listRowOf(id));
365
+ }
366
+ async selectListRows(q) {
369
367
  const params = [...q.params];
370
368
  const ph = (v) => {
371
369
  params.push(v);
372
370
  return `$${params.length}`;
373
371
  };
374
372
  const runWhere = q.runSessionId !== undefined ? ` WHERE session_id = ${ph(q.runSessionId)}` : "";
375
- const sql = "SELECT sm.session_id, sm.owner, sm.updated_at AS last_activity, sm.created_at AS first_activity, sm.title, sm.pinned, " +
373
+ const sql = "SELECT sm.session_id, sm.owner, sm.incarnation, sm.updated_at AS last_activity, sm.created_at AS first_activity, sm.title, sm.pinned, " +
376
374
  " COALESCE(r.run_count, 0) AS run_count, latest.objective_preview, latest.status, latest.task_id AS last_run_id, latest.last_task_run_id " +
377
375
  "FROM session_meta sm " +
378
376
  "LEFT JOIN (SELECT session_id, COUNT(*) AS run_count FROM task_run" + runWhere + " GROUP BY session_id) r ON r.session_id = sm.session_id " +
@@ -383,9 +381,14 @@ export class PgSessionStore {
383
381
  const res = await this.pool.query(sql, params);
384
382
  return res.rows.map((r) => {
385
383
  const runId = r.last_task_run_id;
386
- return {
387
- sessionId: String(r.session_id),
388
- owner: r.owner ?? null,
384
+ const sessionId = String(r.session_id);
385
+ const owner = r.owner ?? null;
386
+ const incarnation = incarnationFromCell(sessionId, r.incarnation, "absent");
387
+ const title = r.title ?? null;
388
+ const pinned = r.pinned === true;
389
+ const row = {
390
+ sessionId,
391
+ owner,
389
392
  lastActivityAt: r.last_activity instanceof Date ? r.last_activity.toISOString() : String(r.last_activity),
390
393
  firstActivityAt: r.first_activity instanceof Date ? r.first_activity.toISOString() : String(r.first_activity),
391
394
  runCount: Number(r.run_count),
@@ -393,9 +396,11 @@ export class PgSessionStore {
393
396
  lastStatus: r.status == null ? "" : String(r.status),
394
397
  lastRunId: r.last_run_id ?? null,
395
398
  ...(typeof runId === "string" && runId !== "" ? { lastTaskRunId: runId } : {}),
396
- title: r.title ?? null,
397
- pinned: r.pinned === true,
399
+ title,
400
+ pinned,
401
+ ...sessionRowTagKey({ sessionId, owner, incarnation, cells: { title, pinned } }),
398
402
  };
403
+ return { row, occupant: { owner, incarnation } };
399
404
  });
400
405
  }
401
406
  async probeTitle(sessionId) {
@@ -431,6 +436,17 @@ export class PgSessionStore {
431
436
  await this.pool.query("UPDATE session_meta SET workspace = $1::jsonb WHERE session_id = $2 AND incarnation = $3", [encodeSessionWorkspaceCell(workspace, pgSafeJsonStringify, sessionId), sessionId, incarnation]);
432
437
  }
433
438
  async fork(sourceId, owner) {
439
+ return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner);
440
+ }
441
+ async forkTo(sourceId, owner, target) {
442
+ for (let attempt = 0; attempt < 2; attempt++) {
443
+ const out = await this.forkToOnce(sourceId, owner, target);
444
+ if (out !== "unsettled")
445
+ return out;
446
+ }
447
+ throw new ForkTargetUnsettledError(target.id);
448
+ }
449
+ async forkToOnce(sourceId, owner, target) {
434
450
  const metaRes = await this.pool.query("SELECT leaf_id, owner FROM session_meta WHERE session_id = $1", [sourceId]);
435
451
  if (!metaRes.rows[0])
436
452
  return null;
@@ -442,19 +458,29 @@ export class PgSessionStore {
442
458
  const point = await forkPointOf(storage);
443
459
  const eventRes = await this.pool.query(`SELECT entry_id, parent_id, type, ${jsonColumnText("pg", "payload")}, ts FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [sourceId]);
444
460
  const { rows, leafId: forkLeafId } = selectForkRows(eventRes.rows, leafId, point, FORK_LOG_ROW_SHAPE);
445
- const newId = uuidv7();
446
461
  const now = new Date();
447
462
  const conn = await this.pool.connect();
463
+ let collided = false;
448
464
  try {
449
465
  await conn.query("BEGIN");
450
- for (let seq = 0; seq < rows.length; seq++) {
451
- const r = rows[seq];
452
- const payload = String(r.payload);
453
- await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
466
+ try {
467
+ await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, fork_key) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)", [target.id, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation(), target.key ?? null]);
454
468
  }
455
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7)", [newId, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation()]);
456
- await conn.query("COMMIT");
457
- return newId;
469
+ catch (err) {
470
+ if (!isDupKey(err))
471
+ throw err;
472
+ collided = true;
473
+ }
474
+ if (!collided) {
475
+ for (let seq = 0; seq < rows.length; seq++) {
476
+ const r = rows[seq];
477
+ const payload = String(r.payload);
478
+ await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [target.id, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
479
+ }
480
+ await conn.query("COMMIT");
481
+ return { outcome: "created", id: target.id };
482
+ }
483
+ await conn.query("ROLLBACK");
458
484
  }
459
485
  catch (err) {
460
486
  try {
@@ -467,6 +493,9 @@ export class PgSessionStore {
467
493
  finally {
468
494
  conn.release();
469
495
  }
496
+ const existing = await this.pool.query("SELECT owner, fork_key FROM session_meta WHERE session_id = $1", [target.id]);
497
+ const row = existing.rows[0];
498
+ return row === undefined ? "unsettled" : classifyForkTarget(target, { owner: row.owner ?? null, forkKey: row.fork_key ?? null }, forkOwner);
470
499
  }
471
500
  async exportEntries(sessionId) {
472
501
  const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
@@ -506,7 +535,7 @@ export class PgSessionStore {
506
535
  const conn = await this.pool.connect();
507
536
  try {
508
537
  await conn.query("BEGIN");
509
- await this.insertValidatedLog(conn, sessionId, owner, validated, mintSessionIncarnation());
538
+ await this.writeImportedLog(conn, sessionId, owner, validated, { incarnation: mintSessionIncarnation(), forkKey: null }, "create");
510
539
  await conn.query("COMMIT");
511
540
  }
512
541
  catch (err) {
@@ -515,7 +544,7 @@ export class PgSessionStore {
515
544
  }
516
545
  catch {
517
546
  }
518
- if (err instanceof SessionError)
547
+ if (err instanceof SessionError || err instanceof SessionActiveRunError)
519
548
  throw err;
520
549
  throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
521
550
  }
@@ -529,14 +558,21 @@ export class PgSessionStore {
529
558
  const conn = await this.pool.connect();
530
559
  try {
531
560
  await conn.query("BEGIN");
532
- const placedRes = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
533
- if (placedRes.rows[0]?.placement_kind != null) {
561
+ const placedRes = await conn.query(`SELECT ${SESSION_META_PLACEMENT_READ_COLUMNS}, fork_key FROM session_meta WHERE session_id = $1 FOR UPDATE`, [sessionId]);
562
+ const prior = placedRes.rows[0];
563
+ if (prior?.placement_kind != null) {
534
564
  await conn.query("ROLLBACK");
535
- throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${placedRes.rows[0].placement_kind} placement partition — a session-sync replace cannot overwrite a delegation transcript`);
565
+ throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${String(prior.placement_kind)} placement partition — a session-sync replace cannot overwrite a delegation transcript`);
536
566
  }
537
- await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
538
- await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2 AND placement_kind IS NULL", [sessionId, owner]);
539
- await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(sessionId, placedRes.rows[0]));
567
+ const purged = await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2 AND placement_kind IS NULL", [sessionId, owner]);
568
+ const swapped = (purged.rowCount ?? 0) === 1 ? prior : undefined;
569
+ const cells = carriedRowCells(sessionId, swapped === undefined ? undefined : { incarnation: swapped.incarnation, forkKey: swapped.fork_key });
570
+ const priorCell = swapped === undefined ? undefined : placementCellFromRow(sessionId, swapped);
571
+ if (priorCell?.role === "writer") {
572
+ await conn.query("ROLLBACK");
573
+ throw new SessionWriterPresentError(sessionId, priorCell.lane);
574
+ }
575
+ await this.writeImportedLog(conn, sessionId, owner, validated, cells, "replace");
540
576
  await conn.query("COMMIT");
541
577
  }
542
578
  catch (err) {
@@ -545,7 +581,7 @@ export class PgSessionStore {
545
581
  }
546
582
  catch {
547
583
  }
548
- if (err instanceof SessionError)
584
+ if (err instanceof SessionError || err instanceof SessionWriterPresentError || err instanceof SessionActiveRunError)
549
585
  throw err;
550
586
  throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
551
587
  }
@@ -553,16 +589,29 @@ export class PgSessionStore {
553
589
  conn.release();
554
590
  }
555
591
  }
556
- async insertValidatedLog(conn, sessionId, owner, validated, incarnation) {
592
+ async writeImportedLog(conn, sessionId, owner, validated, cells, mode) {
557
593
  let leafId = null;
558
594
  for (const e of validated)
559
595
  leafId = leafIdAfterEntry(e);
560
596
  const now = new Date();
597
+ const imported = importedRowCells(cells.forkKey);
598
+ await conn.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${imported.columns}) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)` +
599
+ (mode === "create" ? " ON CONFLICT (session_id) DO NOTHING" : ""), [sessionId, owner, now, now, leafId, validated.length, cells.incarnation, ...imported.values]);
600
+ if (mode === "create") {
601
+ const held = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
602
+ if (String(held.rows[0]?.incarnation) !== cells.incarnation) {
603
+ throw new SessionError("storage", `import of session ${sessionId} failed`, new Error(`session ${sessionId} already exists — importEntries only creates a session (replaceEntries rewrites one); nothing was written`));
604
+ }
605
+ }
606
+ const claim = await lockRootAndReadClaim(rowsOnClient(conn), "pg", sessionId);
607
+ if (claim.activeTaskId !== null)
608
+ throw new SessionActiveRunError(sessionId, claim.activeTaskId);
609
+ if (mode === "replace")
610
+ await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
561
611
  for (let seq = 0; seq < validated.length; seq++) {
562
612
  const e = validated[seq];
563
613
  await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [sessionId, seq, e.id, e.parentId ?? null, e.type, pgSafeJsonStringify(e), new Date(e.timestamp)]);
564
614
  }
565
- await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7)", [sessionId, owner, now, now, leafId, validated.length, incarnation]);
566
615
  }
567
616
  beginImportStaging(realSessionId, token) {
568
617
  return new PgStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
@@ -587,6 +636,11 @@ export class PgSessionStore {
587
636
  const conn = await this.pool.connect();
588
637
  try {
589
638
  await conn.query("BEGIN");
639
+ const claim = await lockRootAndReadClaim(rowsOnClient(conn), "pg", sessionId);
640
+ if (claim.activeTaskId !== null) {
641
+ await conn.query("ROLLBACK");
642
+ throw new SessionActiveRunError(sessionId, claim.activeTaskId);
643
+ }
590
644
  const metaRes = await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2", [sessionId, owner]);
591
645
  if ((metaRes.rowCount ?? 0) > 0)
592
646
  await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
@@ -666,6 +720,11 @@ class PgStagingHandle {
666
720
  throw new StagedCommitOwnerChangedError(this.realSessionId);
667
721
  }
668
722
  }
723
+ const claim = await lockRootAndReadClaim(rowsOnClient(conn), "pg", this.realSessionId);
724
+ if (claim.activeTaskId !== null) {
725
+ await conn.query("ROLLBACK");
726
+ throw new SessionActiveRunError(this.realSessionId, claim.activeTaskId);
727
+ }
669
728
  const staged = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
670
729
  const dst = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
671
730
  const dstMeta = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = $1", [this.realSessionId]);
@@ -698,7 +757,16 @@ class PgStagingHandle {
698
757
  let leafId = null;
699
758
  if (lastRes.rows[0])
700
759
  leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRes.rows[0].payload, this.realSessionId, "last staged row"));
701
- await conn.query("UPDATE session_meta SET owner = $1, updated_at = $2, leaf_id = $3, leaf_seq = $4 WHERE session_id = $5", [owner, now, leafId, srcIds.length, this.realSessionId]);
760
+ const settle = await conn.query(`UPDATE session_meta SET owner = $1, updated_at = $2, leaf_id = $3, leaf_seq = $4, ${MIRROR_ON_COMMIT_SET} WHERE session_id = $5 AND ${MIRROR_ON_COMMIT_GUARD}`, [owner, now, leafId, srcIds.length, this.realSessionId]);
761
+ if ((settle.rowCount ?? 0) !== 1) {
762
+ const locked = readPlacementRowStatement("pg", this.realSessionId, { lock: true });
763
+ const lockedRow = (await conn.query(locked.text, [...locked.values])).rows[0];
764
+ const cell = lockedRow === undefined ? undefined : placementCellFromRow(this.realSessionId, lockedRow);
765
+ await conn.query("ROLLBACK");
766
+ if (cell?.role === "writer")
767
+ throw new SessionWriterPresentError(this.realSessionId, cell.lane);
768
+ throw new SessionError("storage", `staged commit for ${this.realSessionId}: the commit point matched no row under a held row lock and the row is not a writer — refusing to guess`);
769
+ }
702
770
  const afterMeta = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = $1", [this.realSessionId]);
703
771
  if (afterMeta.rows[0]?.placement_kind != null) {
704
772
  await conn.query("ROLLBACK");
@@ -713,7 +781,7 @@ class PgStagingHandle {
713
781
  await conn.query("ROLLBACK");
714
782
  }
715
783
  catch { }
716
- if (err instanceof SyncConflictError || err instanceof SessionError)
784
+ if (err instanceof SyncConflictError || err instanceof SessionError || err instanceof SessionWriterPresentError || err instanceof SessionActiveRunError)
717
785
  throw err;
718
786
  throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
719
787
  }
@@ -52,23 +52,8 @@ export interface AdbEnvDeps {
52
52
  * **结构**信号;adb 的 shell v2 没有等价物,所以这里只能靠标记词,**这条臂就把话说在这里**。 */
53
53
  export declare function isAdbTransportLost(stderr: string): boolean;
54
54
  export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
55
- /**
56
- * 🔴 **[ref] / core 7.20.0([ref])`canonicalPathAuthoritative: true`** —— 本适配器的 `canonicalPath`
57
- * 就是**命令真正跑的那块文件系统**的权威答案(`posix-shell-fs` 的 `canonicalPath` 原语,在设备 shell 里解到落点)。声明它 = 告诉引擎:整链答不出来是**解析器出了事**
58
- * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
59
- * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
60
- * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
61
- * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
62
- * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
63
- * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
64
- */
65
- /** `sharesProcessTable`(core 7.57.0,必填):命令跑在设备上(adb shell),不在本进程所在的进程表。 */
66
- readonly capabilities: {
67
- isolation: boolean;
68
- suspendable: boolean;
69
- canonicalPathAuthoritative: boolean;
70
- sharesProcessTable: boolean;
71
- };
55
+ /** 能力块的唯一作者 = `capabilitiesFor`(`execution-lane-caps.ts`;逐键表态与理由在那里,本类零能力字面量)。 */
56
+ readonly capabilities: import("@sema-agent/core").ExecutionEnvCapabilities;
72
57
  cwd: string;
73
58
  private connected;
74
59
  /** 🔴 拆除是**终态**(2026-07-25 补齐:此前本适配器是同族里唯一没有这个概念的一条腿)。
@@ -80,10 +65,13 @@ export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
80
65
  private destroyed;
81
66
  /** 在飞的那一次懒连接(单飞记忆)——见 {@link ensureConnected}。settle 即清,**不缓存败诺**。 */
82
67
  private connectPromise?;
83
- /** core `ExecutionEnv.homeDir`([ref]):设备 shell 里执行用户的 home —— 声明胜,否则首连时 `adb shell` 实测的 `$HOME`(Android 常为 `/` 或 `/data/local/tmp` 一类,读到什么填什么,绝对即可),见 {@link LaneHomeSeat}。读时才取(没声明时连上之前缺席)。 */
68
+ /** core `ExecutionEnv.homeDir`([ref]):设备 shell 里执行用户的 home —— 声明在场 ⇒ 声明(首连时与设备 shell 的 `$HOME` 对证过),否则首连时 `adb shell` 实测的 `$HOME`(Android 常为 `/` 或 `/data/local/tmp` 一类,读到什么填什么,绝对即可),见 {@link LaneHomeSeat}。读时才取(没声明时连上之前缺席)。 */
84
69
  get homeDir(): string | undefined;
85
- /** 家目录的座(声明胜 · 否则首连时实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。本车道**不对证**声明:Android 没有 POSIX 登录家目录,声明是运维的断言,照单全收。 */
70
+ /** 家目录的座(声明在场 ⇒ 首连时对证 · 否则首连时实测 · 实测缺席 ⇒ 缺席 + 一行 warn)。设备 shell 答得出 `$HOME`,命令里的 `~` / `$HOME` 展开的就是它 ——
71
+ * 声明与它不一致时规则守的是声明那个目录,所以与其余读得出家目录的车道同一只判官、同一句拒。 */
86
72
  private readonly homeSeat;
73
+ /** 家目录:设备答了而与声明不一致(或答的不是绝对路径)⇒ 这只环境的拒,记一次、此后每次连接原样回它(同一台设备同一个答案:不重试、不再问)。 */
74
+ private homeRefusal?;
87
75
  /** core 7.49.0 [ref]:shell 子进程真拿到的 `CDPATH` 条目 —— 本车道的命令跑在 Android 设备的 shell 里(设备自己的 env),本类不读那份 env ⇒ `"unknown"`(core 契约:说不出就说 unknown,
88
76
  * 不许报一张没读过的空表;删除守卫因此对裸 `cd <相对名>` 之后的删除问人,而不是按「没有 CDPATH」放行)。 */
89
77
  readonly cdPath: "unknown";
@@ -175,8 +163,19 @@ export declare class RemoteAdbExecutionEnv implements RemoteExecutionEnv {
175
163
  maxLines?: number;
176
164
  abortSignal?: AbortSignal;
177
165
  }): Promise<Result<string[], FileError>>;
166
+ /** 暂存写的两只面(规则与提交脚本的唯一属主 = `remote-shell.ts` `writeFileStaged` / `appendFileStaged`;父目录与私有暂存目录由编排的准备那一步建):
167
+ * 上传 = `adb push` 到私有目录里的暂存件;提交 = 文件面探针 exec。两者同一身份(push 与 `adb shell` 都由设备上的 adbd 以它的身份落盘 / 起进程)。 */
168
+ private stagedLane;
169
+ /** 整旧或整新(暂存 → 一次 `mv -f`)。修前 push 直落目标:传输中途断 / 取消时目标剩什么取决于设备侧 adbd 的落地实现,本适配器不依赖它。 */
178
170
  writeFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
171
+ /** 追加(暂存要加的字节 → 一次 `cat >>`;inode 不换、已有字节不重写)。修前 = 读旧 + 拼 + 整份 push(O(文件);中途断 ⇒ 目标没了或半份)。
172
+ * 目录不收新名字 / 私有目录收不成私有时的退路 = 修前那一形(push 没有追加写,只能把整份文件落到一个路径上):读旧(不在 ⇒ 按空;别的读错照实答、不 push)
173
+ * + 拼 + 整份 push 直落目标(与 e2b 共用 `rewriteAppend`)—— 非原子,push 中途断 / 取消 ⇒ 目标可能没了或只剩半份(只有 push 那一步经 `ctx.inPlace`:读旧时取消照答 `aborted`,目标未碰)。
174
+ * Android 共享存储(权限位由挂载派生)上收紧那一步按 sdcardfs / MediaProvider FUSE 的权限语义**推断**不过(未上真机,真机读数候 test),追加每次走这里。 */
179
175
  appendFile(p: string, content: string | Uint8Array, abortSignal?: AbortSignal): Promise<Result<void, FileError>>;
176
+ /** 把整份内容 `adb push` 到设备上的 `dest`(建或覆盖)。本机这一侧先落一只本地临时件再 push(二进制安全,不经 shell 转义载荷)——
177
+ * 临时件同样只在私有目录里出现(`mkdtemp` 建的目录生来 0700),本机别的用户读不到;文件自己的权限位照缺省(push 把它带到设备上,与修前同)。 */
178
+ private push;
180
179
  private posixFsInst?;
181
180
  private get posixFs();
182
181
  fileInfo(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;