@sema-agent/server 7.125.1 → 7.126.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 (248) hide show
  1. package/MIGRATION.md +195 -2
  2. package/README.md +2 -2
  3. package/README.zh-CN.md +2 -2
  4. package/USAGE.md +263 -140
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +47 -0
  7. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +5 -1
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
  9. package/deploy/sema-up/chart/templates/server.yaml +1 -1
  10. package/deploy/sema-up/chart/values.yaml +12 -0
  11. package/deploy/sema-up/cluster-up.sh +10 -10
  12. package/deploy/sema-up/kube-up.sh +11 -10
  13. package/deploy/sema-up/lib/dataroot.sh +1 -1
  14. package/deploy/sema-up/lib/envf.sh +5 -5
  15. package/deploy/sema-up/lib/flagshape.sh +5 -5
  16. package/deploy/sema-up/lib/lanes.py +1 -1
  17. package/deploy/sema-up/lib/parse-machines.py +2 -2
  18. package/deploy/sema-up/lib/shell-access.sh +1 -1
  19. package/deploy/sema-up/preflight.sh +4 -4
  20. package/deploy/sema-up/sema-up.sh +33 -33
  21. package/deploy/sema-up/smoke.sh +3 -3
  22. package/deploy/sema-up/stack/compose.yaml +1 -1
  23. package/deploy/sema-up/stack/sema-stack-cluster.yaml +6 -2
  24. package/dist/approval-card.d.ts +20 -4
  25. package/dist/approval-content-kind.d.ts +38 -22
  26. package/dist/approval-content-kind.js +20 -4
  27. package/dist/audit.js +2 -0
  28. package/dist/bench/s1/arms.d.ts +9 -37
  29. package/dist/bench/s1/arms.js +4 -61
  30. package/dist/bench/s1/live-deps.d.ts +4 -10
  31. package/dist/bench/s1/live-deps.js +1 -222
  32. package/dist/bench/s1/oracle.d.ts +1 -1
  33. package/dist/bench/s1/reviewer.d.ts +1 -1
  34. package/dist/bench/s1/row.d.ts +5 -7
  35. package/dist/bench/s1/row.js +0 -2
  36. package/dist/bench/s1/run-firm.d.ts +2 -2
  37. package/dist/bench/s1/run-firm.js +1 -4
  38. package/dist/bench/s1/runner-ctx.d.ts +17 -29
  39. package/dist/bench/s1/runner-ctx.js +3 -7
  40. package/dist/bench/s1/tasks.d.ts +9 -46
  41. package/dist/bench/s1/tasks.js +4 -63
  42. package/dist/boot/execution-env.d.ts +38 -26
  43. package/dist/boot/execution-env.js +71 -38
  44. package/dist/boot/memory-boundary.d.ts +1 -1
  45. package/dist/boot/parked-revive-gate.d.ts +0 -3
  46. package/dist/boot/parked-revive-gate.js +2 -2
  47. package/dist/boot/resolve-spec.d.ts +0 -2
  48. package/dist/boot/resolve-spec.js +12 -17
  49. package/dist/boot/retention-lane.d.ts +12 -1
  50. package/dist/boot/retention-lane.js +2 -0
  51. package/dist/boot/runner-deps.d.ts +5 -5
  52. package/dist/boot/runner-deps.js +5 -16
  53. package/dist/boot/session-faces.d.ts +5 -1
  54. package/dist/boot/session-faces.js +37 -1
  55. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  56. package/dist/boot/stage-03-store-backend.js +2 -2
  57. package/dist/boot/stage-04-stores.d.ts +1 -0
  58. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  59. package/dist/boot/stage-05-execution-env.js +7 -4
  60. package/dist/boot/stage-06-runners.d.ts +2 -3
  61. package/dist/boot/stage-06-runners.js +8 -7
  62. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  63. package/dist/boot/stage-07-capability-layer.js +3 -4
  64. package/dist/boot/stage-08-reapers.d.ts +2 -3
  65. package/dist/boot/stage-08-reapers.js +2 -2
  66. package/dist/boot/stage-09-leader.d.ts +2 -3
  67. package/dist/boot/stage-09-leader.js +2 -1
  68. package/dist/boot/stage-10-http-server.d.ts +2 -3
  69. package/dist/boot/stage-10-http-server.js +2 -2
  70. package/dist/boot/stores.d.ts +1 -0
  71. package/dist/boot/stores.js +4 -3
  72. package/dist/boot/web-admin.d.ts +5 -0
  73. package/dist/boot/web-admin.js +28 -14
  74. package/dist/brain.d.ts +2 -2
  75. package/dist/brain.js +1 -1
  76. package/dist/capabilities/builtin-tools.d.ts +2 -1
  77. package/dist/capabilities/composite-members.d.ts +1 -1
  78. package/dist/capabilities/composite-members.js +1 -1
  79. package/dist/capabilities/hands-lane.d.ts +9 -6
  80. package/dist/capabilities/hands-lane.js +2 -2
  81. package/dist/capabilities/memory-notice.d.ts +27 -22
  82. package/dist/capabilities/memory-notice.js +15 -17
  83. package/dist/capabilities/scenarios.d.ts +9 -14
  84. package/dist/capabilities/scenarios.js +5 -50
  85. package/dist/capability-facts.d.ts +22 -11
  86. package/dist/capability-facts.js +13 -6
  87. package/dist/config-catalog.js +11 -4
  88. package/dist/config-provider.d.ts +5 -1
  89. package/dist/config-provider.js +23 -4
  90. package/dist/config-types.d.ts +14 -1
  91. package/dist/config.js +39 -6
  92. package/dist/deployment-governance.d.ts +19 -21
  93. package/dist/deployment-governance.js +7 -8
  94. package/dist/env-name-allowlist-knobs.d.ts +5 -5
  95. package/dist/env-name-allowlist-knobs.js +1 -0
  96. package/dist/execution-lane-caps.js +2 -2
  97. package/dist/hooks/hook-runner.d.ts +84 -30
  98. package/dist/hooks/hook-runner.js +74 -41
  99. package/dist/http/admission.js +12 -9
  100. package/dist/http/durable-run-leg.d.ts +5 -4
  101. package/dist/http/durable-run-leg.js +1 -1
  102. package/dist/http/idempotency.d.ts +28 -6
  103. package/dist/http/idempotency.js +10 -2
  104. package/dist/http/principal-gate.d.ts +14 -0
  105. package/dist/http/principal-gate.js +7 -0
  106. package/dist/http/request-guards.d.ts +2 -1
  107. package/dist/http/request-guards.js +14 -14
  108. package/dist/http/resume-legs.d.ts +2 -2
  109. package/dist/http/resume-legs.js +5 -4
  110. package/dist/http/route-ctx.d.ts +8 -6
  111. package/dist/http/routes/a2a-serve.js +11 -7
  112. package/dist/http/routes/approvals-assistant.js +18 -3
  113. package/dist/http/routes/background.d.ts +1 -1
  114. package/dist/http/routes/background.js +1 -1
  115. package/dist/http/routes/capabilities.js +10 -2
  116. package/dist/http/routes/mcp-reconnect.js +1 -1
  117. package/dist/http/routes/runs.d.ts +4 -3
  118. package/dist/http/routes/runs.js +12 -13
  119. package/dist/http/routes/session-sync.js +14 -11
  120. package/dist/http/routes/sessions.js +9 -9
  121. package/dist/http/routes/side-query.js +6 -19
  122. package/dist/http/routes/tasks.js +12 -11
  123. package/dist/http/server.d.ts +4 -0
  124. package/dist/http/server.js +2 -2
  125. package/dist/http/submit-replay.d.ts +15 -8
  126. package/dist/http/submit-replay.js +6 -0
  127. package/dist/http/wire-types.d.ts +7 -9
  128. package/dist/index.d.ts +0 -1
  129. package/dist/index.js +0 -1
  130. package/dist/internal-run-env.d.ts +30 -0
  131. package/dist/internal-run-env.js +11 -0
  132. package/dist/leader/wire.js +3 -2
  133. package/dist/memory-scope.d.ts +19 -7
  134. package/dist/memory-scope.js +25 -8
  135. package/dist/observability/fail-open.d.ts +15 -3
  136. package/dist/observability/fail-open.js +15 -3
  137. package/dist/observability/metrics.d.ts +0 -1
  138. package/dist/observability/metrics.js +0 -3
  139. package/dist/observability/run-terminal.js +69 -4
  140. package/dist/permission-rule-vocab.d.ts +3 -2
  141. package/dist/permission-rule-vocab.js +1 -1
  142. package/dist/plan-cache-probe.d.ts +3 -2
  143. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  144. package/dist/plugins/caching-session-store.d.ts +3 -0
  145. package/dist/plugins/caching-session-store.js +3 -0
  146. package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
  147. package/dist/plugins/checkpoint-store-sql.js +57 -11
  148. package/dist/plugins/file-run-store.d.ts +25 -21
  149. package/dist/plugins/file-run-store.js +70 -34
  150. package/dist/plugins/local-checkpoint-store.d.ts +8 -5
  151. package/dist/plugins/local-checkpoint-store.js +5 -15
  152. package/dist/plugins/local-session-store.d.ts +66 -7
  153. package/dist/plugins/local-session-store.js +168 -101
  154. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
  155. package/dist/plugins/memory-run-store.d.ts +19 -3
  156. package/dist/plugins/memory-run-store.js +38 -3
  157. package/dist/plugins/one-time-migrations.d.ts +141 -39
  158. package/dist/plugins/one-time-migrations.js +32 -143
  159. package/dist/plugins/pg-pool.js +11 -1
  160. package/dist/plugins/pg-session-storage.d.ts +2 -0
  161. package/dist/plugins/pg-session-storage.js +9 -5
  162. package/dist/plugins/remote-env-adb.d.ts +3 -0
  163. package/dist/plugins/remote-env-adb.js +1 -0
  164. package/dist/plugins/remote-env-device.d.ts +3 -0
  165. package/dist/plugins/remote-env-device.js +1 -0
  166. package/dist/plugins/remote-env-e2b.d.ts +4 -0
  167. package/dist/plugins/remote-env-e2b.js +2 -1
  168. package/dist/plugins/remote-env-host.d.ts +12 -0
  169. package/dist/plugins/remote-env-host.js +11 -1
  170. package/dist/plugins/remote-env-k8s.d.ts +227 -39
  171. package/dist/plugins/remote-env-k8s.js +450 -75
  172. package/dist/plugins/remote-env-local-docker.d.ts +4 -0
  173. package/dist/plugins/remote-env-local-docker.js +2 -1
  174. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  175. package/dist/plugins/remote-env-ssh.js +1 -0
  176. package/dist/plugins/retention-store-sql.d.ts +4 -1
  177. package/dist/plugins/retention-store-sql.js +11 -9
  178. package/dist/plugins/run-store-sql.d.ts +44 -6
  179. package/dist/plugins/run-store-sql.js +92 -27
  180. package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
  181. package/dist/plugins/sql-driver.d.ts +2 -1
  182. package/dist/plugins/store-backend.js +1 -1
  183. package/dist/plugins/store-contracts.d.ts +17 -3
  184. package/dist/plugins/tidb-pool.js +15 -3
  185. package/dist/plugins/tidb-session-storage.js +3 -2
  186. package/dist/plugins/tidb-session-store.d.ts +2 -0
  187. package/dist/plugins/tidb-session-store.js +6 -2
  188. package/dist/request-key-closure.d.ts +12 -0
  189. package/dist/request-key-closure.js +3 -3
  190. package/dist/router/route-orchestration.d.ts +3 -11
  191. package/dist/router/route-orchestration.js +0 -4
  192. package/dist/rule-entry-list.js +0 -4
  193. package/dist/rules-consent.js +3 -0
  194. package/dist/run-local.d.ts +5 -4
  195. package/dist/run-local.js +23 -17
  196. package/dist/runs.js +1 -0
  197. package/dist/security.d.ts +7 -0
  198. package/dist/security.js +5 -0
  199. package/dist/server-secret-env.d.ts +10 -4
  200. package/dist/server-secret-env.js +4 -1
  201. package/dist/session-incarnation.d.ts +46 -2
  202. package/dist/session-incarnation.js +14 -3
  203. package/dist/session-sync-kernel.d.ts +10 -3
  204. package/dist/spec-fields.d.ts +24 -8
  205. package/dist/spec-fields.js +21 -2
  206. package/dist/task-workflow.d.ts +6 -4
  207. package/dist/task-workflow.js +3 -2
  208. package/dist/tool-approval.d.ts +78 -6
  209. package/dist/tool-approval.js +7 -3
  210. package/dist/trace/core-keyset-guard.d.ts +4 -4
  211. package/dist/trace/engine-notice-wire.d.ts +26 -1
  212. package/dist/trace/engine-notice-wire.js +7 -2
  213. package/dist/trace/project.d.ts +8 -3
  214. package/dist/trace/project.js +15 -1
  215. package/dist/trace/self-minted-wire.d.ts +104 -2
  216. package/dist/trace/self-minted-wire.js +35 -0
  217. package/dist/web-admin-client/apply-effective.d.ts +13 -7
  218. package/dist/web-admin-client/apply-effective.js +11 -26
  219. package/dist/web-admin-client/apply-ledger.d.ts +2 -2
  220. package/dist/web-admin-client/candidate-gate.d.ts +39 -4
  221. package/dist/web-admin-client/candidate-gate.js +9 -2
  222. package/dist/web-admin-client/facade.d.ts +2 -2
  223. package/dist/web-admin-client/http-client.d.ts +3 -1
  224. package/dist/web-admin-client/http-client.js +4 -3
  225. package/dist/web-admin-client/read-warnings.d.ts +33 -5
  226. package/dist/web-admin-client/read-warnings.js +41 -6
  227. package/dist/web-admin-client/types.d.ts +11 -5
  228. package/dist/workspace-cache.d.ts +45 -0
  229. package/dist/workspace-cache.js +30 -0
  230. package/dist/workspace-space-commit.d.ts +116 -0
  231. package/dist/workspace-space-commit.js +117 -0
  232. package/dist/workspace-space.d.ts +75 -9
  233. package/dist/workspace-space.js +52 -1
  234. package/package.json +4 -4
  235. package/dist/boot/session-incarnation-seat.d.ts +0 -41
  236. package/dist/boot/session-incarnation-seat.js +0 -69
  237. package/dist/capabilities/code-review-council.d.ts +0 -49
  238. package/dist/capabilities/code-review-council.js +0 -111
  239. package/dist/capabilities/collab-wire.d.ts +0 -36
  240. package/dist/capabilities/collab-wire.js +0 -2
  241. package/dist/capabilities/collab-workflows.d.ts +0 -47
  242. package/dist/capabilities/collab-workflows.js +0 -90
  243. package/dist/capabilities/prompts/code-review.d.ts +0 -25
  244. package/dist/capabilities/prompts/code-review.js +0 -39
  245. package/dist/capabilities/prompts/team.d.ts +0 -10
  246. package/dist/capabilities/prompts/team.js +0 -6
  247. package/dist/capabilities/team.d.ts +0 -113
  248. package/dist/capabilities/team.js +0 -70
@@ -10,8 +10,9 @@ import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
10
10
  import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
11
11
  import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
12
12
  import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
13
- import { k8sExecutionEnvFactory } from "../plugins/remote-env-k8s.js";
13
+ import { createSpaceReclaimer, k8sExecutionEnvFactory } from "../plugins/remote-env-k8s.js";
14
14
  import { createRunWorkspaceSnapshotSeat } from "../fleet/workspace-snapshot-residual.js";
15
+ import { createSpaceCommitFence } from "../workspace-space-commit.js";
15
16
  import { localDockerExecutionEnvFactory } from "../plugins/remote-env-local-docker.js";
16
17
  import { sshExecutionEnvFactory } from "../plugins/remote-env-ssh.js";
17
18
  import { isRemoteScratchpadLane, withRemoteScratchpad } from "../plugins/remote-scratchpad.js";
@@ -22,6 +23,7 @@ import { reapOrphanWorktrees, withWorktreeIsolation } from "../plugins/worktree-
22
23
  import { customPkgSourceFromEnv, derivePkgSourceEnv } from "../sandbox-pkg-source.js";
23
24
  import { effectiveHostWorkspace } from "../task-cwd.js";
24
25
  import { taskInheritEnvRefusal } from "../hosted-posture.js";
26
+ import { workspaceSnapshotTimeoutOf } from "../workspace-space.js";
25
27
  import { isSandboxPathAdjudicationLane, sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-path-env.js";
26
28
  import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneHomeDir, executionLaneOf, isSingleMachineDefaultLane, laneIsolated } from "../execution-lane-caps.js";
27
29
  function boundedSessionSetter(map, maxEntries) {
@@ -35,10 +37,10 @@ function boundedSessionSetter(map, maxEntries) {
35
37
  function forwardedSandboxEnvKeys(remoteExec) {
36
38
  switch (remoteExec.provider) {
37
39
  case "e2b":
40
+ case "k8s":
38
41
  return Object.keys(remoteExec.sandboxEnv ?? {}).sort();
39
42
  case "local-docker":
40
43
  return Object.keys(remoteExec.env ?? {}).sort();
41
- case "k8s":
42
44
  case "ssh":
43
45
  case "adb":
44
46
  case "host":
@@ -50,18 +52,20 @@ function forwardedSandboxEnvKeys(remoteExec) {
50
52
  }
51
53
  }
52
54
  }
55
+ function buildSandboxLaneEnv(config, explicit) {
56
+ const merged = { ...derivePkgSourceEnv(config.sandboxPkgSource, customPkgSourceFromEnv()), ...(explicit ?? {}) };
57
+ return Object.keys(merged).length > 0 ? merged : undefined;
58
+ }
53
59
  const MAX_CWD_SESSIONS = 4096;
54
60
  export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
55
61
  const perSessionCwd = new Map();
56
62
  const perSessionShellEnv = new Map();
57
63
  const perSessionInheritEnv = new Map();
58
- const perSessionIncarnation = new Map();
59
64
  const owners = new Map();
60
65
  const dropSession = (sid) => {
61
66
  perSessionCwd.delete(sid);
62
67
  perSessionShellEnv.delete(sid);
63
68
  perSessionInheritEnv.delete(sid);
64
- perSessionIncarnation.delete(sid);
65
69
  owners.delete(sid);
66
70
  };
67
71
  const touch = (sid, owner) => {
@@ -96,40 +100,71 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
96
100
  perSessionCwd.delete(sid);
97
101
  perSessionShellEnv.delete(sid);
98
102
  perSessionInheritEnv.delete(sid);
99
- perSessionIncarnation.delete(sid);
100
103
  }
101
104
  touch(sid, owner);
102
105
  },
103
106
  dropSession,
104
- noteSessionIncarnation: (sid, incarnation) => {
105
- if (incarnation === undefined) {
106
- perSessionIncarnation.delete(sid);
107
- return;
108
- }
109
- perSessionIncarnation.set(sid, incarnation);
110
- touch(sid, undefined);
111
- },
112
- sessionIncarnationOf: (sid) => perSessionIncarnation.get(sid),
113
107
  };
114
108
  }
115
109
  export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
116
- return (ctx) => {
117
- const incarnation = seats.incarnationOf(ctx.sessionId);
110
+ return async (ctx) => {
111
+ const onWorkspaceSnapshot = createRunWorkspaceSnapshotSeat(ctx.sessionId);
112
+ const child = ctx.placementRootSessionId !== ctx.sessionId;
113
+ const s3Snapshot = child && k8sCfg.s3Snapshot !== undefined ? spaceOff(k8sCfg.s3Snapshot) : k8sCfg.s3Snapshot;
114
+ const spaceOccupant = child ? undefined : await occupantOf(seats, ctx.sessionId, ctx.taskId);
115
+ const { cache, ...deploymentCfg } = k8sCfg;
118
116
  return k8sExecutionEnvFactory({
119
- ...k8sCfg,
117
+ ...deploymentCfg,
118
+ ...(cache !== undefined && !child ? { cache } : {}),
119
+ ...(s3Snapshot !== undefined ? { s3Snapshot } : {}),
120
120
  image: seats.imageOf(ctx.sessionId) ?? k8sCfg.image,
121
- ...(incarnation !== undefined ? { spaceIncarnation: incarnation } : {}),
122
- onWorkspaceSnapshot: createRunWorkspaceSnapshotSeat(ctx.sessionId),
121
+ ...(spaceOccupant !== undefined ? { spaceOccupant } : {}),
122
+ onWorkspaceSnapshot,
123
123
  }, deps)(ctx);
124
124
  };
125
125
  }
126
- export function assertWorkspaceSpaceReadable(space, sessionFaceRead, sessionStoreLabel) {
127
- if (!space.available || !persistNeedsOccupant(space.policy.persist) || sessionFaceRead !== undefined)
126
+ const STORE_ERROR_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
127
+ async function occupantOf(seats, sessionId, taskId) {
128
+ let answer;
129
+ try {
130
+ answer = await seats.incarnationOf?.(sessionId);
131
+ }
132
+ catch (e) {
133
+ const code = e?.code;
134
+ return { fault: typeof code === "string" && STORE_ERROR_CODE.test(code) ? code : "incarnation_read_failed" };
135
+ }
136
+ if (answer === undefined)
137
+ return { unread: "incarnation_unknown" };
138
+ if (typeof answer !== "string" || answer === "")
139
+ return { fault: "incarnation_read_failed" };
140
+ const rows = seats.spaceCommits();
141
+ return rows !== undefined ? { incarnation: answer, fence: createSpaceCommitFence(rows, answer, taskId) } : { unread: "incarnation_unknown" };
142
+ }
143
+ function spaceOff(store) {
144
+ const { space: _off, ...parkOnly } = store;
145
+ return parkOnly;
146
+ }
147
+ export function assertWorkspaceSpaceReadable(space, seats, cache, sessionStoreLabel) {
148
+ if (space.available && persistNeedsOccupant(space.policy.persist)) {
149
+ const missing = seats.incarnationOf === undefined ? `the session store (${sessionStoreLabel}) has no by-id occupant read (incarnationOf)`
150
+ : seats.spaceCommits === undefined ? `this deployment has no store backend to hold the space's commit rows (the session store is ${sessionStoreLabel})`
151
+ : undefined;
152
+ if (missing === undefined)
153
+ return;
154
+ throw new Error(`WORKSPACE_PERSIST=space but ${missing} [workspace-space-unreadable]: ` +
155
+ "the k8s workspace space is partitioned by the session occupant and committed under a run-store row, so here no run would restore or save " +
156
+ '/workspace while GET /v1/capabilities.workspaceSync reports persist "space". Use a store backend (DB_BACKEND=local or DB_BACKEND=mysql|pg; ' +
157
+ "DB_BACKEND=memory has none), or set WORKSPACE_PERSIST=off (the default is space), and restart");
128
158
  return;
129
- throw new Error(`WORKSPACE_PERSIST=space but the session store is ${sessionStoreLabel}, which has no session occupant read (sessionFaceOf) [workspace-space-unreadable]: ` +
130
- "the k8s workspace space is partitioned by the session occupant, so on this store no run would restore or save /workspace while " +
131
- 'GET /v1/capabilities.workspaceSync reports persist "space". Use a session store with that read (DB_BACKEND=local, or DB_BACKEND=mysql|pg ' +
132
- "with SESSION_BACKEND not memory), or drop WORKSPACE_PERSIST=space (the default is off), and restart");
159
+ }
160
+ if (cache.available && (seats.incarnationOf === undefined || seats.spaceCommits === undefined)) {
161
+ const missing = seats.incarnationOf === undefined
162
+ ? `the session store (${sessionStoreLabel}) has no by-id occupant read (incarnationOf)`
163
+ : `this deployment has no store backend to hold the space durably (the session store is ${sessionStoreLabel})`;
164
+ throw new Error(`WORKSPACE_CACHE=volume but ${missing} [workspace-space-unreadable]: ` +
165
+ "the k8s build-cache volume belongs to the workspace space, which is keyed by the session occupant, so here no run would get its volume while " +
166
+ "GET /v1/capabilities.workspaceSync reports cache. Use a store backend (DB_BACKEND=local or DB_BACKEND=mysql|pg; DB_BACKEND=memory has none), or set WORKSPACE_CACHE=off, and restart");
167
+ }
133
168
  }
134
169
  function persistNeedsOccupant(persist) {
135
170
  switch (persist) {
@@ -158,6 +193,7 @@ export function createExecutionEnv(ctx) {
158
193
  const { perSessionShellEnv, setSessionShellEnv } = sessionScoped;
159
194
  const { perSessionInheritEnv, setSessionInheritEnv } = sessionScoped;
160
195
  let executionEnvFactory;
196
+ let spaceReclaimer;
161
197
  let worktreeReap;
162
198
  if (config.remoteExec?.provider === "e2b") {
163
199
  executionEnvFactory = e2bExecutionEnvFactory({
@@ -168,10 +204,7 @@ export function createExecutionEnv(ctx) {
168
204
  ...(config.remoteExec.livenessMs != null ? { livenessMs: config.remoteExec.livenessMs } : {}),
169
205
  ...(config.remoteExec.allowInternetAccess != null ? { allowInternetAccess: config.remoteExec.allowInternetAccess } : {}),
170
206
  ...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
171
- ...(() => {
172
- const merged = { ...derivePkgSourceEnv(config.sandboxPkgSource, customPkgSourceFromEnv()), ...(config.remoteExec.sandboxEnv ?? {}) };
173
- return Object.keys(merged).length > 0 ? { sandboxEnv: merged } : {};
174
- })(),
207
+ ...((env) => (env !== undefined ? { sandboxEnv: env } : {}))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
175
208
  logger,
176
209
  metrics,
177
210
  });
@@ -188,16 +221,18 @@ export function createExecutionEnv(ctx) {
188
221
  ...(config.remoteExec.runtimeClass != null ? { runtimeClass: config.remoteExec.runtimeClass } : {}),
189
222
  ...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
190
223
  ...(config.remoteExec.timeoutMs != null ? { timeoutMs: config.remoteExec.timeoutMs } : {}),
224
+ dataTimeoutMs: workspaceSnapshotTimeoutOf(config.remoteExec),
191
225
  ...(config.remoteExec.memory ? { memory: config.remoteExec.memory } : {}),
192
226
  ...(config.remoteExec.cpu ? { cpu: config.remoteExec.cpu } : {}),
193
- ...(config.remoteExec.s3Snapshot ? { s3Snapshot: { ...config.remoteExec.s3Snapshot, ...((f) => (f.available ? { space: f.policy } : {}))(facts.workspaceSpace(config)) } } : {}),
227
+ ...(config.remoteExec.s3Snapshot ? { s3Snapshot: { ...config.remoteExec.s3Snapshot, ...((f) => (f.available ? { space: { persist: f.policy.persist, keep: f.policy.keep } } : {}))(facts.workspaceSpace(config)) } } : {}),
228
+ ...((f) => (f.available ? { cache: f.cache } : {}))(facts.workspaceCache(config)),
229
+ ...(config.remoteExec.workspaceSnapshotExclude ? { snapshotExclude: config.remoteExec.workspaceSnapshotExclude } : {}),
194
230
  logger,
195
- ...(() => {
196
- const derived = derivePkgSourceEnv(config.sandboxPkgSource, customPkgSourceFromEnv());
197
- return Object.keys(derived).length > 0 ? { podEnv: derived } : {};
198
- })(),
231
+ ...((env) => (env !== undefined ? { podEnv: env } : {}))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
199
232
  };
200
- executionEnvFactory = createK8sRunEnvFactory(k8sCfg, { imageOf: (sid) => perTaskImage.get(sid), incarnationOf: sessionScoped.sessionIncarnationOf });
233
+ executionEnvFactory = createK8sRunEnvFactory(k8sCfg, { imageOf: (sid) => perTaskImage.get(sid), incarnationOf: ctx.incarnationOf, spaceCommits: ctx.spaceCommits ?? (() => undefined) });
234
+ if (k8sCfg.cache !== undefined || k8sCfg.s3Snapshot !== undefined)
235
+ spaceReclaimer = createSpaceReclaimer(k8sCfg);
201
236
  }
202
237
  else if (config.remoteExec?.provider === "ssh") {
203
238
  executionEnvFactory = sshExecutionEnvFactory({
@@ -384,9 +419,7 @@ export function createExecutionEnv(ctx) {
384
419
  perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
385
420
  fenceSessionOwner: sessionScoped.fenceSessionOwner,
386
421
  dropSessionScoped: sessionScoped.dropSession,
387
- noteSessionIncarnation: sessionScoped.noteSessionIncarnation,
388
- sessionIncarnationOf: sessionScoped.sessionIncarnationOf,
389
- executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager,
422
+ executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer,
390
423
  };
391
424
  }
392
425
  //# sourceMappingURL=execution-env.js.map
@@ -75,7 +75,7 @@ import type { MemoryEngineBackend } from "@sema-agent/settings-schema/execution-
75
75
  * 而且它断言的是 per-task 事实,不是本键的车道级语义。
76
76
  *
77
77
  * ## 位置与纪律
78
- * 单源:resolve-spec 的 spec 直通、`shouldDiscloseNoPersistentMemory` 判别式、boot 审计的 declared 位
78
+ * 单源:resolve-spec 的 spec 直通、`noPersistentMemoryNotice` 判别式、boot 审计的 declared 位
79
79
  * 三处都必须读本函数,禁各自重算(operator-knob 律:显式 env 的优先权在这一个函数里执法一次)。
80
80
  * 自动补位在 boot 日志上必须可见(stores.ts 的 info 行),部署方随时可用显式 env 覆盖。
81
81
  */
@@ -72,9 +72,6 @@ export interface ParkedReviveGateDeps {
72
72
  };
73
73
  /** host 腿 cwd 占位的落地根(main.ts 的 `localRoot`;`config.localDataRoot` 优先)。 */
74
74
  readonly localRoot: string;
75
- /** S-133:守卫集 `dataRoot` = boot 已铸的引擎记忆根(`memoryEngine.root`,boot/stores.ts 单一写者),不在本腿重推导
76
- * (重推导 = 第二个写者,7.59.0 重扫 codex 轮抓到 HTTP 腿 fallback 与 stores 不同源正是这一形)。`undefined` = 引擎未接。 */
77
- readonly memoryEngineRoot: string | undefined;
78
75
  /** 沙箱 lane 的真 env 登记簿。缺省 = 进程内唯一那只(工厂装饰器写的同一个);测试注入自建实例。 */
79
76
  readonly slots?: SandboxPathEnvSlots;
80
77
  }
@@ -11,7 +11,7 @@ function hostGateCwd(config, localRoot) {
11
11
  return join(config.localDataRoot ?? localRoot, "parked-revive-unrooted");
12
12
  }
13
13
  export function createParkedReviveInheritedGate(deps) {
14
- const { config, question, approvalExemptionStore, logger, localRoot, approverSeat, memoryEngineRoot } = deps;
14
+ const { config, question, approvalExemptionStore, logger, localRoot, approverSeat } = deps;
15
15
  const slots = deps.slots ?? sandboxPathEnvSlots;
16
16
  return async (row, identity) => {
17
17
  const metadata = await chainMetadataOf(deps, row, identity);
@@ -33,7 +33,7 @@ export function createParkedReviveInheritedGate(deps) {
33
33
  question,
34
34
  ...(approvalExemptionStore !== undefined && rootSessionId ? { exemption: createDeploymentListExemption(approvalExemptionStore, rootSessionId, logger, "parked_revive") } : {}),
35
35
  });
36
- const governed = applyRuntimeGovernance({ objective: "", toolPolicy: baseline }, createDeploymentGovernanceInputs(config, pathAdjudication, { engineDataRoot: memoryEngineRoot }));
36
+ const governed = applyRuntimeGovernance({ objective: "", toolPolicy: baseline }, createDeploymentGovernanceInputs(config, pathAdjudication));
37
37
  return {
38
38
  parentConstraints: [
39
39
  {
@@ -39,8 +39,6 @@ export interface ResolveSpecCtx {
39
39
  /** 两张 per-session 登记簿的**租户围栏**(codex R1-F1):每次提交对拍本会话的主人,换主即清。
40
40
  * 论证(为什么 device lane 把这条从「不需要」变成「必需」)逐字见 `createSessionScopedRegistries` 头注。 */
41
41
  fenceSessionOwner: (sid: string, owner: string | null) => void;
42
- /** S-506([ref] §5):装配期化身读数的写腿(per-session 登记簿,与围栏同一只;记忆座同步从那里读)。 */
43
- noteSessionIncarnation: (sid: string, incarnation: string | undefined) => void;
44
42
  hookLlm: ReturnType<typeof createHookLlm>["hookLlm"];
45
43
  hookAgent: HookLlmCall;
46
44
  fleetBus: FleetEventBus;
@@ -7,7 +7,7 @@ import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilit
7
7
  import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
8
8
  import { gateScenarioRequest, mergeUserSkills, selectScenario } from "../capabilities/scenarios.js";
9
9
  import { stampHandsLane, toolPolicyForHands } from "../capabilities/hands-lane.js";
10
- import { buildNoticeLaneFacts, shouldDiscloseNoPersistentMemory, withNoPersistentMemoryNotice } from "../capabilities/memory-notice.js";
10
+ import { buildNoticeLaneFacts, noPersistentMemoryNotice, withNoPersistentMemoryNotice } from "../capabilities/memory-notice.js";
11
11
  import { applyLongtailDefer } from "../capabilities/tool-defer.js";
12
12
  import { assertGuardPatternsUsable, buildApprovalGrantNotice, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs } from "../deployment-governance.js";
13
13
  import { createDeploymentListExemption, gateIntentUnserviceableReason, singleUserAutoAcceptBaselineApplies } from "../approval.js";
@@ -31,25 +31,23 @@ import { HttpError, createCodedHttpError, encodeCheckpointScope } from "../secur
31
31
  import { memoryProjectKeyFor, memorySpecForRequest } from "../memory-scope.js";
32
32
  import { inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
33
33
  import { a2aForScenario, mcpForScenario } from "../web-admin-client/facade.js";
34
- import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, toolNameListFromBody } from "../spec-fields.js";
34
+ import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, allowToolsSpecFragment, toolNameListFromBody } from "../spec-fields.js";
35
35
  import { isAbsoluteHostPath } from "@sema-agent/settings-schema";
36
36
  import { cwdHonored, deviceCwdHonored, effectiveHostWorkspace, inProcessSingleUserLane, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
37
37
  import { assertRequestA2aUnlocked, resolveRequestA2a } from "../task-a2a.js";
38
38
  import { assertRequestMcpContentOrigin, assertRequestMcpUnlocked, resolveRequestMcp } from "../task-mcp.js";
39
39
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSettings, effectivePermissionMode, effectiveThinking, hasConstitutionAnchors, parseTaskSettings, taskHooksHonored, providerDropsAppend, settingsPermissionBucketIssue, withPermissionMode } from "../task-settings.js";
40
40
  import { skillDenyInertEntryOf } from "../rule-entry-list.js";
41
- import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
41
+ import { TENANCY_WALL_CLOCK_BASE_SEC, enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
42
42
  import { redactSecrets } from "../trace/redact.js";
43
43
  import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
44
44
  import { executionLaneCaps, executionLaneHomeDir, executionLaneOf } from "../execution-lane-caps.js";
45
45
  import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
46
46
  import { withUnattendedLeg } from "../unattended-permission-mode.js";
47
47
  import { memoryFaceMounted } from "../memory-scope.js";
48
- import { createAssemblyIncarnationRead, sessionFaceReaderOf } from "./session-incarnation-seat.js";
49
48
  export function createResolveSpec(ctx) {
50
- const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, noteSessionIncarnation, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
49
+ const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
51
50
  assertGuardPatternsUsable(config);
52
- const assemblyIncarnationRead = createAssemblyIncarnationRead(sessionFaceReaderOf(ownerAware), (e, f) => logger.warn(e, f));
53
51
  const onlySensitiveBaseline = buildOnlySensitiveBaselineWarning(config, {
54
52
  approvalSeat: durableEnabled ? "durable" : "absent",
55
53
  singleUserAutoAcceptBaseline: singleUserAutoAcceptBaselineApplies(config),
@@ -124,8 +122,6 @@ export function createResolveSpec(ctx) {
124
122
  throw new HttpError(422, why, { code: HOST_POSTURE_REFUSED_CODE });
125
123
  }
126
124
  const lane = bindSettingsCwdEnvAndModel(body, auth, gated, opts);
127
- if (auth?.sessionId && assemblyIncarnationRead)
128
- noteSessionIncarnation(auth.sessionId, await assemblyIncarnationRead(auth.sessionId));
129
125
  const anchors = await resolveHistoryAnchors(body, auth);
130
126
  const spec = assembleSpecLiteral(body, auth, req, opts, { ...gated, ...lane, ...anchors }, effMode);
131
127
  const folded = await foldGovernanceAndSettings(body, auth, spec, gated, effMode);
@@ -368,6 +364,7 @@ export function createResolveSpec(ctx) {
368
364
  : {}),
369
365
  ...(body.excludeAllTools === true ? { excludeAllTools: true } : {}),
370
366
  ...(() => { const v = toolNameListFromBody(body.excludeTools); return v ? { excludeTools: v } : {}; })(),
367
+ ...allowToolsSpecFragment(body.allowTools),
371
368
  ...(() => { const v = toolNameListFromBody(body.deferTools); return v ? { deferTools: v } : {}; })(),
372
369
  ...(() => { const v = promptProfileFromBody(body.promptProfile); return v ? { promptProfile: v } : {}; })(),
373
370
  ...(() => { const v = toolMaterializeStrategyFromBody(body.toolMaterializeStrategy); return v ? { toolMaterializeStrategy: v } : {}; })(),
@@ -409,7 +406,7 @@ export function createResolveSpec(ctx) {
409
406
  thinking: effectiveThinking(body.reasoningEffort, parsedSettings.settings?.ultracode === true),
410
407
  getApiKeyAndHeaders: getKeyResolver(),
411
408
  ...(() => {
412
- const limits = resolveTaskLimits(body.limits, taskLimitCaps, taskTimeoutSec, config.requirePrincipal, body.council === true || body.debate === true || scenarioName === "discuss", (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null }));
409
+ const limits = resolveTaskLimits(body.limits, taskLimitCaps, taskTimeoutSec, config.requirePrincipal, (event, fields) => logger.warn(event, { ...fields, sessionId: auth?.sessionId ?? null }));
413
410
  const declarations = [];
414
411
  if (taskLimitCaps.timeoutSec !== undefined)
415
412
  declarations.push({ key: "server.limits.timeoutSecCap", value: String(taskLimitCaps.timeoutSec), reason: "env TASK_TIMEOUT_MAX_SEC (seconds; operator ceiling applied to caller limits.maxWalltimeMs as cap×1000)" });
@@ -422,7 +419,7 @@ export function createResolveSpec(ctx) {
422
419
  if (config.maxTaskTokens > 0)
423
420
  declarations.push({ key: "server.budget.maxTokensCap", value: String(config.maxTaskTokens), reason: "env MAX_TASK_TOKENS_MAX (operator ceiling; becomes the effective value when the caller sends none)" });
424
421
  if (config.requirePrincipal === true)
425
- declarations.push({ key: "server.limits.wallClockBaseSec", value: "2400/3600", reason: "tenancy wall-clock base (multi-tenant; big tasks 3600) — TASK_TIMEOUT_SEC raises, never shrinks" });
422
+ declarations.push({ key: "server.limits.wallClockBaseSec", value: String(TENANCY_WALL_CLOCK_BASE_SEC), reason: "tenancy wall-clock base (multi-tenant) — TASK_TIMEOUT_SEC raises, never shrinks" });
426
423
  const maxCostUsd = cappedCeiling(body.maxCostUsd, config.maxTaskCostUsd);
427
424
  const maxTokens = cappedCeiling(body.maxTokens, config.maxTaskTokens);
428
425
  const degrade = (() => {
@@ -518,7 +515,7 @@ export function createResolveSpec(ctx) {
518
515
  }),
519
516
  ...laneHomeBase(),
520
517
  };
521
- const governedBase = applyRuntimeGovernance(spec, createDeploymentGovernanceInputs(config, pathAdjudication, { engineDataRoot: memoryEngine?.root }));
518
+ const governedBase = applyRuntimeGovernance(spec, createDeploymentGovernanceInputs(config, pathAdjudication));
522
519
  let governed = governedBase;
523
520
  let effectiveSettings = effMode ? withPermissionMode(parsedSettings.settings, effMode) : parsedSettings.settings;
524
521
  if (appendLessPack && effectiveSettings?.outputStyle) {
@@ -600,16 +597,15 @@ export function createResolveSpec(ctx) {
600
597
  throw new HttpError(422, `request settings cannot be applied as written: ${e.message}`);
601
598
  }
602
599
  }
603
- if (shouldDiscloseNoPersistentMemory({
600
+ const notice = noPersistentMemoryNotice({
604
601
  memoryFaceMounted: memoryFaceMounted(governed.memory),
605
602
  declaredCapable: effectiveMemoryPersistenceCapable(config),
606
603
  hands: gated.hands,
607
604
  handsReadOnly: governed.handsReadOnly === true,
608
- excludeAllTools: governed.excludeAllTools === true,
609
- excludeTools: governed.excludeTools,
610
605
  ...buildNoticeLaneFacts(executionLaneOf(config.remoteExec?.provider)),
611
- })) {
612
- governed = { ...governed, promptProvider: withNoPersistentMemoryNotice(governed.promptProvider) };
606
+ });
607
+ if (notice !== "silent") {
608
+ governed = { ...governed, promptProvider: withNoPersistentMemoryNotice(governed.promptProvider, notice) };
613
609
  }
614
610
  return { governed, scratchpadDir, hostSemanticsLane };
615
611
  };
@@ -667,7 +663,6 @@ export function createResolveSpec(ctx) {
667
663
  isolatedExecEnv,
668
664
  hasExecutionEnv: executionLaneCaps(executionLaneOf(config.remoteExec?.provider)).coreRemoteExecutionEnv === "supported",
669
665
  ...(config.autonomy ? { autonomy: config.autonomy } : {}),
670
- explicitTeam: body.council === true || body.debate === true || scenarioName === "discuss",
671
666
  });
672
667
  logger.info("orchestration_routed", { mode: decision.mode, reason: decision.reason, sessionId: auth?.sessionId ?? null });
673
668
  if (decision.mode === "supervisor") {
@@ -31,6 +31,7 @@
31
31
  */
32
32
  import type { RetentionPolicy, RetentionReceipt } from "@sema-agent/core";
33
33
  import type { RetentionMode } from "../config-types.js";
34
+ import type { EndedSpace, SpaceReclaimer } from "../workspace-space.js";
34
35
  /** 同 domain 连续失败多少次算「卡住了」(fail-loud 的阈值)。`5` 与 reaper 家族的
35
36
  * `REAPER_FAILURE_WARN_THRESHOLD` 同值同理由:一次抖动(一次重连、一次超时)正是容错要吸收的,
36
37
  * 只有**连败**才越过「可诊断」这条线。 */
@@ -56,7 +57,7 @@ export interface RetentionExecutor {
56
57
  toolResults: number;
57
58
  }>;
58
59
  expireCheckpoints(input: RetentionDestructiveInput): Promise<RetentionReceipt>;
59
- deleteExpiredSessions(input: RetentionDestructiveInput): Promise<RetentionReceipt>;
60
+ deleteExpiredSessions(input: RetentionDestructiveInput): Promise<RetentionSessionsReceipt>;
60
61
  deleteOrphanToolResults(input: RetentionDestructiveInput): Promise<RetentionReceipt>;
61
62
  }
62
63
  /** 破坏性调用的入参(判定时点的三格随行 —— 理由逐字见车1 `RetentionAuditContext` 的头注)。 */
@@ -127,7 +128,17 @@ export interface RetentionSweepCtx {
127
128
  * `createThrottledReaperCatch` 必须建在 `setInterval` 之外是同一条理由。
128
129
  */
129
130
  streaks?: Map<string, number>;
131
+ /** 结束的空间的回收座(k8s 车道 ∧ 配了缓存卷或快照存储时在场;与会话删除协调器同一只):enforce 删掉会话树之后,把回执里结束了的空间交给它
132
+ * (缓存卷、`space/` 快照、该会话停驻车道的快照一并回收)。它从不抛(失败 = 一行 + fail-open 计数);缺席 = 本部署没有可回收的东西。 */
133
+ spaceReclaimer?: SpaceReclaimer;
130
134
  }
135
+ /**
136
+ * 会话树 sweep 的回执 = core 契约的 `RetentionReceipt` + 本仓一位:`endedSpaces` = 本次删掉的会话树里**结束了的空间**(会话 id + 住客空间键 = 会话化身;
137
+ * 读不出住客的空化身不算),present-iff 非空。lane 把它交给空间的回收座(与会话删除协调器同一只),缓存卷与快照随空间的终结回收。
138
+ */
139
+ export type RetentionSessionsReceipt = RetentionReceipt & {
140
+ readonly endedSpaces?: readonly EndedSpace[];
141
+ };
131
142
  /** 一轮的读数(给日志与判据用;lane 自己不做任何决策依赖它)。 */
132
143
  export interface RetentionSweepOutcome {
133
144
  /** 本轮真正处理完的 domain 数(丢租中止时 = 中止之前那些)。 */
@@ -99,6 +99,8 @@ async function sweepDomainEnforce(ctx, d) {
99
99
  };
100
100
  const checkpoints = await ctx.executor.expireCheckpoints(input);
101
101
  const sessions = await ctx.executor.deleteExpiredSessions(input);
102
+ if (sessions.endedSpaces !== undefined && ctx.spaceReclaimer !== undefined)
103
+ await ctx.spaceReclaimer.reclaim(sessions.endedSpaces);
102
104
  const toolResults = await ctx.executor.deleteOrphanToolResults(input);
103
105
  const touched = checkpoints.deleted + sessions.deleted + toolResults.deleted;
104
106
  if (touched > 0) {
@@ -87,9 +87,6 @@ export interface RunnerDepsCtx {
87
87
  promptSource: RunnerDeps["promptSource"];
88
88
  rosterStore: RunnerDeps["rosterStore"];
89
89
  backgroundAgentStore: RunnerDeps["backgroundAgentStore"];
90
- /** S-506([ref] §5):本副本 per-session 登记簿的**同步**化身读腿(装配期由 resolve-spec 写入)。在场 ⇒ 铸
91
- * `RunnerDeps.memorySessionIncarnation` 座;缺席 ⇒ 座不铸(core 口径:按 id 绑定 = 修前行为)。 */
92
- sessionIncarnationOf: ((sessionId: string) => string | undefined) | undefined;
93
90
  mailboxStore: RunnerDeps["mailboxStore"];
94
91
  /**
95
92
  * [ref]:mailbox 的**收件人生命周期面**(预删除态)。与 `mailboxStore` **成对**(`boot/stores.ts` 同段铸,
@@ -159,6 +156,9 @@ export interface RunnerDepsCtx {
159
156
  backend: MemoryBackend;
160
157
  root: string;
161
158
  } | undefined;
159
+ /** core 7.49.0 [ref]:引擎数据根(boot 事实,`boot/stores.ts` 恒铸 —— 记忆引擎开不开都在)。共享基座把它交成每只 Runner 的
160
+ * `memoryEngineDir`:引擎从它解出每条腿的唯一豁免根(守卫集不再收 `dataRoot`)。开引擎时与 `memoryEngine.root` 同一只。 */
161
+ engineDataRoot: string;
162
162
  /**
163
163
  * S-399(core 7.21.1)—— 记忆整理的**已解析座**(`boot/memory-consolidation.ts` 的
164
164
  * `resolveConsolidationDriverSeat` 产物),在场 **iff** 整理阀门开着(`resolveConsolidationValve` 的判决:
@@ -236,7 +236,7 @@ export interface RunnerDepsCtx {
236
236
  }
237
237
  /** [ref] A10 留档发现②:main runner `RunnerDeps` 与 main.ts subRunner 字面量之间此前手工重复
238
238
  * 的 ~15 个键,类型标注见 {@link createSharedRunnerDeps} 头注。 */
239
- export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "allowedTools" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "checkpointStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
239
+ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "memoryEngineDir" | "allowedTools" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "checkpointStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
240
240
  /**
241
241
  * [ref] A10 留档发现②(review 2026-07-29,[ref]§三族A 同源修补的延续):main runner 的
242
242
  * `RunnerDeps` 字面量(下方 `createRunnerDeps`)与 `main.ts` 里 subRunner 的 `new Runner({...})`
@@ -263,7 +263,7 @@ export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDeny
263
263
  * 属性,比再抽一层共享基座更强的同源保证),不重复收纳进这里。
264
264
  */
265
265
  /** 基座真实消费的窄面(Pick)——subRunner 调用点(main.ts)只需凑这 13 个字段,不必造全量 ctx。 */
266
- export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "checkpointStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations" | "metrics">;
266
+ export type SharedRunnerDepsCtx = Pick<RunnerDepsCtx, "config" | "brain" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "deploymentHooks" | "toolResultStore" | "checkpointStore" | "sessionPolicyStore" | "hands" | "usageWindowStore" | "orgMemoryAdmission" | "sharedMemoryStores" | "logger" | "governanceSeams" | "mcpRevocations" | "metrics" | "engineDataRoot">;
267
267
  /** 转发席只需要 `inc` —— 刻意窄到这一个动词,免得把整只 Metrics 拖进这条纯转发腿。 */
268
268
  export interface EngineNoticeMetricsSeat {
269
269
  inc: (name: string, labels?: Record<string, string>) => void;
@@ -1,16 +1,14 @@
1
1
  import { createFileWorkflowScriptStore } from "@sema-agent/core";
2
2
  import { join } from "node:path";
3
- import { listCollabWorkflows, resolveCollabWorkflow } from "../capabilities/collab-workflows.js";
4
3
  import { lkgReadDenyRows } from "../config-lkg.js";
5
4
  import { fleetBackgroundChildPublisher, FleetEventBus } from "../fleet/fleet-bus.js";
6
5
  import { createMemoryEngineIncidentSink } from "../memory-incident-sink.js";
7
- import { createMemorySessionIncarnationSeat, sessionFaceReaderOf } from "./session-incarnation-seat.js";
8
6
  import { assertAutoConsolidationWirable } from "./memory-consolidation.js";
9
7
  import { createHardenedVmRunner } from "../orchestration/hardened-vm-runner.js";
10
8
  import { createWorkerHardenedVmRunner } from "../orchestration/hardened-vm-worker-runner.js";
11
9
  import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
12
10
  import { resolveServedSession } from "../orchestration/workflow-completion-inbox.js";
13
- import { defaultEngineNoticeRouter, redactNoticeRuleText } from "../trace/engine-notice-wire.js";
11
+ import { defaultEngineNoticeRouter, redactNoticeMessage, redactNoticeRuleText } from "../trace/engine-notice-wire.js";
14
12
  import { WorkflowNotifyGate } from "../orchestration/workflow-notify-journal.js";
15
13
  import { makeLoadProjectMemory, makeProbeInstructionSources } from "../project-memory.js";
16
14
  import { createInstructionSourceSeat } from "../instruction-source-notice.js";
@@ -43,7 +41,8 @@ const CEILING_ORIGINS = new Set(["walltime", "turns"]);
43
41
  export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter, metrics) {
44
42
  return (notice) => {
45
43
  const detail = redactNoticeRuleText(notice.code, notice.detail);
46
- logger.warn("engine_notice", { code: notice.code, message: notice.message, ...(detail !== undefined ? { detail } : {}) });
44
+ const message = redactNoticeMessage(notice.message);
45
+ logger.warn("engine_notice", { code: notice.code, message, ...(detail !== undefined ? { detail } : {}) });
47
46
  if (metrics !== undefined && notice.code === "task.interrupt_unconsumed") {
48
47
  const origin = notice.detail?.origin;
49
48
  if (typeof origin === "string" && CEILING_ORIGINS.has(origin))
@@ -103,6 +102,7 @@ export function createSharedRunnerDeps(ctx) {
103
102
  usageWindows: ctx.config.usageWindows ? ctx.config.usageWindows : undefined,
104
103
  usageWindowStore: ctx.usageWindowStore ? ctx.usageWindowStore : undefined,
105
104
  sharedMemoryStores: ctx.sharedMemoryStores ? ctx.sharedMemoryStores : undefined,
105
+ memoryEngineDir: ctx.engineDataRoot,
106
106
  compliancePostureResolver: ctx.governanceSeams.compliancePostureResolver,
107
107
  lockedConfig: ctx.governanceSeams.lockedConfig,
108
108
  retentionPolicy: ctx.governanceSeams.retentionPolicy,
@@ -123,16 +123,7 @@ export function createRunnerDeps(ctx) {
123
123
  : createHardenedVmRunner(),
124
124
  workflowRunStore: workflowRunStore ? workflowRunStore : undefined,
125
125
  workflowJournalStore: workflowJournalStore ? workflowJournalStore : undefined,
126
- workflowScriptStore: (() => {
127
- const fileStore = createFileWorkflowScriptStore(join(config.localDataRoot ?? localRoot, "workflow-scripts"));
128
- return {
129
- scopePartitioned: true,
130
- persist: fileStore.persist.bind(fileStore),
131
- load: fileStore.load.bind(fileStore),
132
- resolveName: (name) => resolveCollabWorkflow(name) ?? fileStore.resolveName?.(name),
133
- list: () => listCollabWorkflows(),
134
- };
135
- })(),
126
+ workflowScriptStore: createFileWorkflowScriptStore(join(config.localDataRoot ?? localRoot, "workflow-scripts")),
136
127
  onWorkflowAgentSpawn: workflowAgentRegistry
137
128
  ? (handle) => {
138
129
  const unregister = workflowAgentRegistry.register(handle);
@@ -192,9 +183,7 @@ export function createRunnerDeps(ctx) {
192
183
  },
193
184
  sessionStore,
194
185
  memoryBackend: memoryEngine ? memoryEngine.backend : undefined,
195
- memoryEngineDir: memoryEngine ? memoryEngine.root : undefined,
196
186
  memoryCaptureRecordStore: sessionCaptureRecords !== undefined ? (plane) => sessionCaptureRecords(plane) : undefined,
197
- memorySessionIncarnation: ctx.sessionIncarnationOf !== undefined && sessionFaceReaderOf(sessionStore) !== undefined ? createMemorySessionIncarnationSeat(ctx.sessionIncarnationOf, (e, f) => logger.warn(e, f)) : undefined,
198
187
  onMemoryHarvestReport: memoryEngine
199
188
  ? (report, info) => {
200
189
  metrics.inc("memory_harvest_total", { ok: String(report.ok), phase: info.phase, incident: report.incident?.kind ?? "none" });
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * ⚠️ **位置即契约**:
8
8
  * 1. `sessionWatchRegistry` 必须在 `purgeSession` **之前**构造 —— purge 尾部的 `dropSession` 是
9
- * 同副本租户轮转围栏(delete 即终结旧 owner 的 SSE 订阅),两者是一对。
9
+ * 同副本租户轮转围栏(delete 即终结旧 owner 的 SSE 订阅),两者是一对;空间回收座在它**之后**起、且不在应答路径里。
10
10
  * 2. `setLeafAdvanceListener(...)` 是**进程级单点**装载(leaf-bus 只有一个监听位),必须与
11
11
  * registry 的构造同段发生,晚于它、早于任何会 append 的路由。
12
12
  */
@@ -26,6 +26,7 @@ import type { TaskListLane } from "./task-list-lane.js";
26
26
  import { type RunnerDeps } from "@sema-agent/core";
27
27
  import type { MailboxRecipientLifecycle } from "../plugins/mailbox-recipient-lifecycle.js";
28
28
  import type { UserInputLedgerStore } from "../user-input-ledger.js";
29
+ import type { SpaceReclaimer } from "../workspace-space.js";
29
30
  export interface SessionFacesCtx {
30
31
  config: ServiceConfig;
31
32
  logger: Logger;
@@ -60,6 +61,9 @@ export interface SessionFacesCtx {
60
61
  /** S-965:用户输入受理账本的**店**(E21 级联的一条腿:该会话的受理行随会话删)。与受理 / 收敛 / 告知读的是同一只(stage-07 取一次)。
61
62
  * 缺席 = 本部署没装账本(env-only:没有 backend / run 店)⇒ 无行可删。 */
62
63
  userInputLedgerStore: UserInputLedgerStore | undefined;
64
+ /** k8s 车道工作区空间的回收座(配了构建缓存卷或快照存储时在场;与托管留存 sweep 同一只)—— E21 级联的一条腿:会话删了 =
65
+ * 这位住客的空间结束了,它的缓存卷、`space/` 快照、该会话停驻车道的快照随之回收。缺席 = 本部署没有可回收的东西。 */
66
+ spaceReclaimer?: SpaceReclaimer;
63
67
  }
64
68
  export declare function createSessionFaces(ctx: SessionFacesCtx): {
65
69
  ownerAware: OwnerAwareSessionStore;
@@ -7,8 +7,10 @@ import { SessionWatchRegistry, posIntEnv } from "../session-watch.js";
7
7
  import { defaultTaskRegistry, peerSessionBoxHandle } from "@sema-agent/core";
8
8
  import { reapSessionBackgroundHard } from "../background-session-reap.js";
9
9
  import { probeSessionChildren, retireSessionChildren, SessionChildrenStopIncompleteError } from "../session-children-retire.js";
10
+ import { SessionIncarnationMissingError, redactIncarnations, sessionFaceReadOf } from "../session-incarnation.js";
11
+ import { recordFailOpen } from "../observability/fail-open.js";
10
12
  export function createSessionFaces(ctx) {
11
- const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, userInputLedgerStore, } = ctx;
13
+ const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, backgroundAgentStore, userInputLedgerStore, spaceReclaimer, } = ctx;
12
14
  const mysqlPool = backend?.mysqlPool();
13
15
  const auditPgPool = backend?.pgPool();
14
16
  const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
@@ -34,6 +36,28 @@ export function createSessionFaces(ctx) {
34
36
  })
35
37
  : undefined;
36
38
  setLeafAdvanceListener(sessionWatchRegistry ? (sid, leaf, owner, seq) => sessionWatchRegistry.notifyLocal(sid, leaf, owner, seq) : undefined);
39
+ const readSessionFace = sessionFaceReadOf(sessionStore);
40
+ const occupantSpaceKeyOf = async (sessionId, owner) => {
41
+ if (readSessionFace === undefined)
42
+ return undefined;
43
+ try {
44
+ const face = await readSessionFace(sessionId);
45
+ return face !== undefined && face.owner === owner ? face.incarnation : undefined;
46
+ }
47
+ catch (err) {
48
+ if (err instanceof SessionIncarnationMissingError)
49
+ return undefined;
50
+ const reason = "occupant_unreadable";
51
+ recordFailOpen("server.workspace-cache.reclaim-failed", `reason=${reason}`);
52
+ logger.warn("workspace_cache_reclaim_failed", {
53
+ sessionId,
54
+ reason,
55
+ error: redactIncarnations(String(err)),
56
+ effect: "the deleted session's space could not be read, so its build-cache volume and snapshot objects are not reclaimed (the session delete went ahead) — delete the volume by label sema.space; the objects fall to the bucket's lifecycle rule",
57
+ });
58
+ return undefined;
59
+ }
60
+ };
37
61
  const purgeSession = ownerAware.deleteSession
38
62
  ? async (sessionId, owner) => {
39
63
  if (ownerAware.ownerOf) {
@@ -41,6 +65,7 @@ export function createSessionFaces(ctx) {
41
65
  if (stillOwner !== owner)
42
66
  return { deleted: false };
43
67
  }
68
+ const spaceKey = spaceReclaimer !== undefined ? await occupantSpaceKeyOf(sessionId, owner) : undefined;
44
69
  const childScope = owner ?? "default";
45
70
  const reach = await probeSessionChildren({ agentStore: backgroundAgentStore, checkpointStore, registry: defaultTaskRegistry }, sessionId, childScope);
46
71
  if (!reach.reachable) {
@@ -116,6 +141,17 @@ export function createSessionFaces(ctx) {
116
141
  await peerBox.store.drop(peerBox.scope, peerBox.handle);
117
142
  const historyDeleted = await ownerAware.deleteSession(sessionId, owner);
118
143
  sessionWatchRegistry?.dropSession(sessionId);
144
+ if (historyDeleted && spaceReclaimer !== undefined && spaceKey !== undefined) {
145
+ void spaceReclaimer.reclaim([{ sessionId, spaceKey }]).catch((err) => {
146
+ recordFailOpen("server.workspace-space.reclaim-failed", "stage=seat_threw");
147
+ logger.warn("workspace_space_reclaim_failed", {
148
+ sessionId,
149
+ stage: "seat_threw",
150
+ error: redactIncarnations(String(err)),
151
+ effect: "the ended space's cache volume and snapshot objects may stay (the reclaim seat threw, which its contract forbids; the session delete went ahead) — the bucket's lifecycle rule is the fallback for objects, the volume is deleted by label sema.space",
152
+ });
153
+ });
154
+ }
119
155
  return { deleted: historyDeleted || runs.removed > 0 };
120
156
  }
121
157
  : undefined;
@@ -3,6 +3,7 @@ export declare function runStage03(ctx: Awaited<ReturnType<typeof runStage02>>):
3
3
  backend: import("../index.js").StoreBackend | undefined;
4
4
  backgroundAgentStore: import("@sema-agent/core").BackgroundAgentStore | undefined;
5
5
  breakerState: import("../plugins/breaker-state-sql.js").TiDBBreakerState | import("../plugins/breaker-state-sql.js").PgBreakerState | undefined;
6
+ engineDataRoot: string;
6
7
  instanceId: string;
7
8
  mailboxRecipientLifecycle: import("../plugins/mailbox-recipient-lifecycle.js").MailboxRecipientLifecycle | undefined;
8
9
  mailboxStore: import("@sema-agent/core").MailboxStore | undefined;