@sema-agent/server 7.137.0 → 7.138.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 (209) hide show
  1. package/MIGRATION.md +328 -139
  2. package/README.md +1 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +67 -20
  5. package/dist/approval-card.d.ts +21 -14
  6. package/dist/approval-card.js +1 -0
  7. package/dist/audit.js +1 -0
  8. package/dist/background-agent-name.d.ts +5 -0
  9. package/dist/background-agent-name.js +5 -0
  10. package/dist/boot/budget-tracing.d.ts +1 -12
  11. package/dist/boot/deployment-mods.d.ts +46 -16
  12. package/dist/boot/deployment-mods.js +82 -6
  13. package/dist/boot/governance-seams.d.ts +10 -17
  14. package/dist/boot/governance-seams.js +5 -17
  15. package/dist/boot/leader.d.ts +3 -0
  16. package/dist/boot/leader.js +1 -0
  17. package/dist/boot/org-delivery.js +15 -2
  18. package/dist/boot/resolve-spec.d.ts +3 -0
  19. package/dist/boot/resolve-spec.js +23 -12
  20. package/dist/boot/retention-lane.d.ts +11 -5
  21. package/dist/boot/retention-lane.js +2 -2
  22. package/dist/boot/runner-deps.d.ts +7 -3
  23. package/dist/boot/runner-deps.js +1 -0
  24. package/dist/boot/stage-04-stores.d.ts +1 -12
  25. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  26. package/dist/boot/stage-05-execution-env.js +7 -4
  27. package/dist/boot/stage-06-runners.d.ts +3 -12
  28. package/dist/boot/stage-06-runners.js +5 -2
  29. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  30. package/dist/boot/stage-07-capability-layer.js +2 -2
  31. package/dist/boot/stage-08-reapers.d.ts +3 -12
  32. package/dist/boot/stage-09-leader.d.ts +3 -12
  33. package/dist/boot/stage-09-leader.js +2 -2
  34. package/dist/boot/stage-10-http-server.d.ts +3 -12
  35. package/dist/boot/stage-10-http-server.js +4 -2
  36. package/dist/boot/web-admin.d.ts +5 -2
  37. package/dist/boot/web-admin.js +3 -3
  38. package/dist/brain.js +5 -5
  39. package/dist/budget.d.ts +17 -15
  40. package/dist/budget.js +2 -2
  41. package/dist/capabilities/mcp-probe.d.ts +36 -11
  42. package/dist/capabilities/mcp-probe.js +44 -5
  43. package/dist/capability-facts.d.ts +6 -0
  44. package/dist/capability-facts.js +5 -0
  45. package/dist/checkpoint-identity.d.ts +13 -6
  46. package/dist/config-catalog.js +7 -1
  47. package/dist/config-types.d.ts +15 -7
  48. package/dist/config.js +8 -5
  49. package/dist/deployment-shape.d.ts +6 -6
  50. package/dist/gate-settlement.d.ts +71 -0
  51. package/dist/gate-settlement.js +40 -0
  52. package/dist/hooks/classic-stations.d.ts +1 -0
  53. package/dist/hooks/classic-stations.js +1 -0
  54. package/dist/hooks/command-spawn.d.ts +2 -2
  55. package/dist/hooks/hook-runner.js +14 -14
  56. package/dist/http/admission.js +4 -2
  57. package/dist/http/dispatch.d.ts +4 -0
  58. package/dist/http/dispatch.js +3 -0
  59. package/dist/http/durable-run-leg.js +4 -2
  60. package/dist/http/memory-compliance-failure-reply.js +3 -0
  61. package/dist/http/resume-legs.d.ts +5 -11
  62. package/dist/http/resume-legs.js +136 -91
  63. package/dist/http/route-ctx.d.ts +30 -51
  64. package/dist/http/route-ctx.js +2 -5
  65. package/dist/http/routes/a2a-serve.js +10 -4
  66. package/dist/http/routes/agents-roster.d.ts +1 -1
  67. package/dist/http/routes/agents-roster.js +5 -1
  68. package/dist/http/routes/approvals-assistant.js +6 -6
  69. package/dist/http/routes/capabilities.js +12 -10
  70. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  71. package/dist/http/routes/mcp-reconnect.js +7 -5
  72. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  73. package/dist/http/routes/memory-origin.d.ts +1 -1
  74. package/dist/http/routes/rules.js +3 -3
  75. package/dist/http/routes/runs.js +11 -7
  76. package/dist/http/routes/sessions.js +6 -4
  77. package/dist/http/routes/side-query.js +2 -3
  78. package/dist/http/routes/tasks.js +792 -780
  79. package/dist/http/server.d.ts +31 -25
  80. package/dist/http/wire-types.d.ts +14 -6
  81. package/dist/identity-source.d.ts +1 -1
  82. package/dist/index.d.ts +2 -0
  83. package/dist/index.js +1 -0
  84. package/dist/leader/wire.d.ts +7 -0
  85. package/dist/leader/wire.js +5 -4
  86. package/dist/mcp-assertion-header.d.ts +51 -40
  87. package/dist/mcp-assertion-header.js +10 -11
  88. package/dist/mcp-authorization-face.d.ts +27 -0
  89. package/dist/mcp-authorization-face.js +15 -0
  90. package/dist/memory-operator-faces.d.ts +13 -4
  91. package/dist/memory-operator-faces.js +1 -17
  92. package/dist/memory-sync-client.js +72 -57
  93. package/dist/memory-sync.d.ts +1 -1
  94. package/dist/model-route-gap.d.ts +6 -22
  95. package/dist/model-route-gap.js +7 -5
  96. package/dist/mods-declaration.d.ts +110 -0
  97. package/dist/mods-declaration.js +109 -0
  98. package/dist/observability/err-text.d.ts +5 -0
  99. package/dist/observability/err-text.js +9 -0
  100. package/dist/observability/fail-open.d.ts +5 -1
  101. package/dist/observability/fail-open.js +5 -1
  102. package/dist/observability/secret-env-scrub.d.ts +1 -1
  103. package/dist/observability/secret-env-scrub.js +1 -1
  104. package/dist/parked-decide.d.ts +15 -2
  105. package/dist/parked-decide.js +14 -8
  106. package/dist/peer-seat.d.ts +150 -0
  107. package/dist/peer-seat.js +239 -0
  108. package/dist/permission-rule-vocab.d.ts +7 -7
  109. package/dist/permission-rule-vocab.js +3 -3
  110. package/dist/plain-string-record.d.ts +38 -0
  111. package/dist/plain-string-record.js +38 -0
  112. package/dist/plugins/background-agent-store-sql.js +6 -3
  113. package/dist/plugins/caching-session-store.d.ts +5 -0
  114. package/dist/plugins/caching-session-store.js +6 -0
  115. package/dist/plugins/checkpoint-store-sql.d.ts +15 -20
  116. package/dist/plugins/checkpoint-store-sql.js +8 -8
  117. package/dist/plugins/file-history-store-sql.js +3 -2
  118. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  119. package/dist/plugins/k8s-pod-reaper.js +3 -3
  120. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  121. package/dist/plugins/local-checkpoint-store.js +0 -1
  122. package/dist/plugins/local-session-store.d.ts +66 -27
  123. package/dist/plugins/local-session-store.js +60 -28
  124. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  125. package/dist/plugins/mailbox-store-sql.js +103 -15
  126. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  127. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  128. package/dist/plugins/one-time-migrations.d.ts +6 -3
  129. package/dist/plugins/one-time-migrations.js +3 -4
  130. package/dist/plugins/permission-rule-store-sql.js +21 -13
  131. package/dist/plugins/pg-session-storage.d.ts +10 -2
  132. package/dist/plugins/pg-session-storage.js +18 -5
  133. package/dist/plugins/remote-env-adb.d.ts +3 -1
  134. package/dist/plugins/remote-env-adb.js +1 -1
  135. package/dist/plugins/remote-env-device.d.ts +3 -1
  136. package/dist/plugins/remote-env-device.js +1 -1
  137. package/dist/plugins/remote-env-e2b.d.ts +2 -0
  138. package/dist/plugins/remote-env-e2b.js +1 -1
  139. package/dist/plugins/remote-env-host.d.ts +3 -1
  140. package/dist/plugins/remote-env-host.js +1 -1
  141. package/dist/plugins/remote-env-k8s.d.ts +3 -1
  142. package/dist/plugins/remote-env-k8s.js +1 -1
  143. package/dist/plugins/remote-env-local-docker.d.ts +2 -0
  144. package/dist/plugins/remote-env-local-docker.js +1 -1
  145. package/dist/plugins/remote-env-ssh.d.ts +3 -1
  146. package/dist/plugins/remote-env-ssh.js +1 -1
  147. package/dist/plugins/retention-store-sql.d.ts +14 -2
  148. package/dist/plugins/retention-store-sql.js +6 -0
  149. package/dist/plugins/session-fork.d.ts +10 -13
  150. package/dist/plugins/session-fork.js +5 -25
  151. package/dist/plugins/session-ownership.d.ts +7 -0
  152. package/dist/plugins/session-ownership.js +5 -0
  153. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  154. package/dist/plugins/session-workspace-cell.js +38 -0
  155. package/dist/plugins/store-backend.js +7 -7
  156. package/dist/plugins/tidb-pool.js +6 -0
  157. package/dist/plugins/tidb-session-store.d.ts +17 -2
  158. package/dist/plugins/tidb-session-store.js +16 -5
  159. package/dist/plugins/tool-result-store-sql.d.ts +13 -6
  160. package/dist/plugins/tool-result-store-sql.js +8 -7
  161. package/dist/plugins/worktree-isolation.d.ts +3 -2
  162. package/dist/plugins/worktree-isolation.js +1 -7
  163. package/dist/project-identity.d.ts +1 -14
  164. package/dist/project-identity.js +0 -4
  165. package/dist/rules-consent.js +6 -4
  166. package/dist/run-local.js +12 -6
  167. package/dist/runs.d.ts +7 -1
  168. package/dist/runs.js +6 -1
  169. package/dist/sandbox-git-plane.d.ts +2 -2
  170. package/dist/server-secret-env.d.ts +6 -3
  171. package/dist/server-secret-env.js +4 -2
  172. package/dist/session-incarnation.d.ts +4 -5
  173. package/dist/session-incarnation.js +2 -2
  174. package/dist/session-workspace.d.ts +130 -0
  175. package/dist/session-workspace.js +83 -0
  176. package/dist/spec-fields.d.ts +1 -1
  177. package/dist/standing-answer-grant.d.ts +2 -13
  178. package/dist/standing-answer-grant.js +0 -7
  179. package/dist/task-a2a.d.ts +44 -14
  180. package/dist/task-a2a.js +46 -20
  181. package/dist/task-mcp.d.ts +33 -20
  182. package/dist/task-mcp.js +46 -22
  183. package/dist/tool-approval.d.ts +32 -8
  184. package/dist/tool-approval.js +49 -12
  185. package/dist/trace/core-keyset-guard.d.ts +33 -18
  186. package/dist/trace/engine-notice-wire.d.ts +1 -1
  187. package/dist/trace/engine-notice-wire.js +2 -0
  188. package/dist/trace/exit-seat.d.ts +20 -1
  189. package/dist/trace/exit-seat.js +3 -0
  190. package/dist/trace/extension-call-face.d.ts +33 -0
  191. package/dist/trace/extension-call-face.js +39 -0
  192. package/dist/trace/project.d.ts +25 -7
  193. package/dist/trace/project.js +29 -3
  194. package/dist/trace/redact.js +42 -13
  195. package/dist/trace/self-minted-wire.d.ts +129 -10
  196. package/dist/trace/self-minted-wire.js +62 -3
  197. package/dist/trace/wire-projection-faces.d.ts +6 -6
  198. package/dist/trace/wire-projection-faces.js +6 -5
  199. package/dist/user-authored-text.d.ts +2 -2
  200. package/dist/user-authored-text.js +16 -1
  201. package/dist/web-admin-client/center-request.d.ts +3 -2
  202. package/dist/web-admin-client/center-request.js +2 -3
  203. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  204. package/dist/web-admin-client/skills-mcp.js +31 -18
  205. package/dist/web-admin-client/types.d.ts +5 -0
  206. package/package.json +3 -3
  207. package/wire-changes/server@7.138.0.json +446 -0
  208. package/dist/http/mcp-remote-text.d.ts +0 -5
  209. package/dist/http/mcp-remote-text.js +0 -7
@@ -46,7 +46,7 @@ export class RemoteContainerExecutionEnv {
46
46
  this.homeDir = config.homeDir;
47
47
  this.sandboxApi = deps.sandboxApi ?? Sandbox;
48
48
  }
49
- capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true };
49
+ capabilities = { isolation: true, suspendable: true, canonicalPathAuthoritative: true, sharesProcessTable: false };
50
50
  workspaceHandle() {
51
51
  if (!this.handle) {
52
52
  throw new RemoteExecutionError("connect_failed", "workspaceHandle() called before connect()");
@@ -71,14 +71,16 @@ export declare class RemoteHostExecutionEnv implements RemoteExecutionEnv, Sched
71
71
  * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
72
72
  * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
73
73
  * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
74
- * 布尔字面量零影响(三键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
74
+ * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
75
75
  * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
76
76
  * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
77
77
  */
78
+ /** `sharesProcessTable`(core 7.57.0,必填):命令是本进程 spawn 的子进程(同一张进程表),引擎的模式杀守卫对它照 NodeExecutionEnv 一样问 `pgrep`。 */
78
79
  readonly capabilities: {
79
80
  isolation: boolean;
80
81
  suspendable: boolean;
81
82
  canonicalPathAuthoritative: boolean;
83
+ sharesProcessTable: boolean;
82
84
  };
83
85
  /** R7 自唤醒: structural delegation to the injected scheduler backend. `hasScheduler(env)` checks BOTH the method
84
86
  * presence (always, since we implement the interface) AND `schedulerCapabilities.supported===true` — which is only
@@ -37,7 +37,7 @@ const UNSUPPORTED_SCHEDULER_CAPS = {
37
37
  maxScheduledHorizonSec: 0,
38
38
  };
39
39
  export class RemoteHostExecutionEnv {
40
- capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true };
40
+ capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true, sharesProcessTable: true };
41
41
  get schedulerCapabilities() {
42
42
  return this.cfg.scheduler?.schedulerCapabilities ?? UNSUPPORTED_SCHEDULER_CAPS;
43
43
  }
@@ -296,14 +296,16 @@ export declare class RemoteK8sExecutionEnv implements RemoteExecutionEnv, Backgr
296
296
  * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
297
297
  * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
298
298
  * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
299
- * 布尔字面量零影响(三键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
299
+ * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
300
300
  * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
301
301
  * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
302
302
  */
303
+ /** `sharesProcessTable`(core 7.57.0,必填):命令跑在沙箱 pod 自己的 PID 命名空间里(不开 hostPID),不在本进程所在的进程表。 */
303
304
  readonly capabilities: {
304
305
  isolation: boolean;
305
306
  suspendable: boolean;
306
307
  canonicalPathAuthoritative: true;
308
+ sharesProcessTable: false;
307
309
  };
308
310
  cwd: string;
309
311
  private podName?;
@@ -268,7 +268,7 @@ export class RemoteK8sExecutionEnv {
268
268
  this.logger = config.logger;
269
269
  this.onWorkspaceSnapshot = config.onWorkspaceSnapshot;
270
270
  this.cacheMisses = config.cacheMisses;
271
- this.capabilities = { isolation: true, suspendable: !!this.cfg.s3Snapshot, canonicalPathAuthoritative: true };
271
+ this.capabilities = { isolation: true, suspendable: !!this.cfg.s3Snapshot, canonicalPathAuthoritative: true, sharesProcessTable: false };
272
272
  const bgMax = Math.max(1, Math.min(K8S_BG_MAX_TIMEOUT_SEC, Math.floor(this.cfg.timeoutMs / 1000)));
273
273
  this.backgroundCapabilities = {
274
274
  supported: true,
@@ -62,10 +62,12 @@ export interface LocalDockerEnvDeps {
62
62
  }
63
63
  export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv {
64
64
  /** DUAL-MODE-DESIGN §5: a container is a real isolation boundary; v1 has no managed memory snapshot. */
65
+ /** `sharesProcessTable`(core 7.57.0,必填):命令跑在本机容器自己的 PID 命名空间里(不传 `--pid=host`),不在本进程所在的进程表。 */
65
66
  readonly capabilities: {
66
67
  isolation: boolean;
67
68
  suspendable: boolean;
68
69
  canonicalPathAuthoritative: boolean;
70
+ sharesProcessTable: boolean;
69
71
  };
70
72
  /** Working directory inside the container; relative paths resolve against it (ExecutionEnv contract). */
71
73
  cwd: string;
@@ -21,7 +21,7 @@ const unsupported = (op) => ({
21
21
  error: new RemoteExecutionError("unsupported", `${op} is not supported on a local-docker target (v1: a container is an isolation boundary but not a managed memory snapshot; capabilities.suspendable=false)`),
22
22
  });
23
23
  export class RemoteLocalDockerExecutionEnv {
24
- capabilities = { isolation: true, suspendable: false, canonicalPathAuthoritative: true };
24
+ capabilities = { isolation: true, suspendable: false, canonicalPathAuthoritative: true, sharesProcessTable: false };
25
25
  cwd;
26
26
  get homeDir() {
27
27
  return this.homeSeat.home;
@@ -59,14 +59,16 @@ export declare class RemoteSshExecutionEnv implements RemoteExecutionEnv {
59
59
  * (RPC 断、传输掉),不是「一个它看不见的命名空间」⇒ 读边界的执行期复核**拒**,而不是让词法判决站着。
60
60
  * 缺席是今天每个适配器的读法(整链沉默 ⇒ 词法判决成立、命令照跑),所以这一声明是**收紧**方向。
61
61
  * 严格读法 `=== true`(core `canonicalPathIsAuthoritative`);`isSuspendable` 一族同样严格,对本仓的
62
- * 布尔字面量零影响(三键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
62
+ * 布尔字面量零影响(四键都写死字面 `true`/`false`,不存在「非布尔值被读成缺席」那一形 —— 那一形由
63
63
  * core 的 `config.execution_env_capability_invalid` 普查响亮播报,本仓的受众表已登记)。
64
64
  * 谁该声明由闭集门看着(`test/execution-lane-caps.test.ts` 的逐 lane 对账:新 lane 不表态 = 编译红)。
65
65
  */
66
+ /** `sharesProcessTable`(core 7.57.0,必填):命令跑在 SSH 目标机上;目标恰是本机(或本进程所在容器的宿主)时命令与本进程同表,这一形今天没有配置面可声明 `true`。 */
66
67
  readonly capabilities: {
67
68
  isolation: boolean;
68
69
  suspendable: boolean;
69
70
  canonicalPathAuthoritative: boolean;
71
+ sharesProcessTable: boolean;
70
72
  };
71
73
  /** Working directory; relative paths resolve against it (ExecutionEnv contract). Starts at mountPath. */
72
74
  cwd: string;
@@ -18,7 +18,7 @@ function exitCodeForSignalKill(signal) {
18
18
  return 128 + (signalNumber(`SIG${signal}`) ?? 9);
19
19
  }
20
20
  export class RemoteSshExecutionEnv {
21
- capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true };
21
+ capabilities = { isolation: false, suspendable: false, canonicalPathAuthoritative: true, sharesProcessTable: false };
22
22
  cwd;
23
23
  conn;
24
24
  sftpPromise;
@@ -52,7 +52,11 @@ import { type SqlDriver, type SqlTxConn } from "./sql-driver.js";
52
52
  import { type IndexSpec } from "./ensure-index.js";
53
53
  import type { EndedSpace } from "../workspace-space.js";
54
54
  /**
55
- * 三只**被托管**的 SQL 店(session / checkpoint / tool-result)共用的 `retention` 声明常量。
55
+ * 三只**被托管**的 SQL 店(session / checkpoint / tool-result)在扫车道开着时的 `retention` 声明常量。
56
+ *
57
+ * 🔴 **谁说这个词**(引擎 7.58.0 起:店说 `"managed"` 而部署不跑执行器 ⇒ 锁不锁都拒启):「这只店的行有没有人按期删」不是店类自己知道的事,
58
+ * 是装配它的部署知道的事。唯一事实 = 扫车道开不开({@link retentionSweepRuns});后端铸店时按它给词({@link sqlStoreRetention}),
59
+ * 交给引擎启动检查的执行器读同一个事实(开 ⇒ 执行器,关 ⇒ `null`)。店构造不给词 ⇒ `"none"`(引擎对缺席的同一读法)。
56
60
  *
57
61
  * 🔴 为什么是一个共享常量而不是各写各的字面量:这一格是 core `assertRetentionCapability` 的**唯一**读点,
58
62
  * 三只店的答案必须同进同退 —— 哪天本能力从某只店的行上撤了(比如 tool_result 改由别处清),漏改一处
@@ -66,8 +70,16 @@ import type { EndedSpace } from "../workspace-space.js";
66
70
  * file/in-memory 形没有任何东西会按期删它们的行 ⇒ 显式 `"none"`(缺席也读 none,但显式是文档义务)。
67
71
  */
68
72
  export declare const MANAGED_RETENTION: RetentionDeclaration;
69
- /** file/in-memory 形的诚实声明(见 {@link MANAGED_RETENTION} 的读法说明)。 */
73
+ /** file/in-memory 形、以及扫车道关着的 SQL 店的诚实声明(见 {@link MANAGED_RETENTION} 的读法说明)。 */
70
74
  export declare const UNMANAGED_RETENTION: RetentionDeclaration;
75
+ /** 本部署跑不跑托管留存的扫车道(`RETENTION_SWEEP_INTERVAL_SEC > 0`)—— 三只 SQL 店的声明与交给引擎启动检查的执行器只读这一处。 */
76
+ export declare function retentionSweepRuns(sweep: {
77
+ readonly intervalSec: number;
78
+ }): boolean;
79
+ /** 后端铸三只被托管的 SQL 店时交给店的词:扫车道开 ⇒ `"managed"`,关 ⇒ `"none"`。 */
80
+ export declare function sqlStoreRetention(sweep: {
81
+ readonly intervalSec: number;
82
+ }): RetentionDeclaration;
71
83
  /** 追加式审计表(设计稿 §6)。 */
72
84
  export declare const RETENTION_AUDIT_TABLE = "retention_audit";
73
85
  /** per-domain 法务保留标记(设计稿 §5)——**行在 = 该域整体冻结**。 */
@@ -5,6 +5,12 @@ import { ensureIndexes } from "./ensure-index.js";
5
5
  import { incarnationFromCell } from "../session-incarnation.js";
6
6
  export const MANAGED_RETENTION = "managed";
7
7
  export const UNMANAGED_RETENTION = "none";
8
+ export function retentionSweepRuns(sweep) {
9
+ return sweep.intervalSec > 0;
10
+ }
11
+ export function sqlStoreRetention(sweep) {
12
+ return retentionSweepRuns(sweep) ? MANAGED_RETENTION : UNMANAGED_RETENTION;
13
+ }
8
14
  export const RETENTION_AUDIT_TABLE = "retention_audit";
9
15
  export const RETENTION_HOLD_TABLE = "retention_hold";
10
16
  export const RETENTION_LEASE_TABLE = "retention_lease";
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * ## 规则(一句)
5
5
  * 「fork 拷到哪一条」只有引擎一只判官:`settledForkOptions(源会话)` 答 `{}`(整份日志)或 `{ entryId: <在飞轮的父>, position: "at" }`(拷到那一条为止)。
6
- * 本仓的店不写第二份「什么算在飞」—— local 店把它的答案原样交给引擎自己的 `repo.fork`;两只 SQL 孪生没有 `repo.fork`(它们按行搬字节),所以这里只管**按答案拷**:
6
+ * 本仓的店不写第二份「什么算在飞」—— local 店把它的答案原样交给引擎自己的 `repo.fork`;两只 SQL 孪生没有 `repo.fork`(它们按行搬字节),按答案挑行也是引擎的一只
7
+ * (`selectForkRows`,引擎 7.59.0 包根导出:同一条父链走法、同一组拒词);本文件只给它这两只孪生的行形(`entry_id` / `parent_id` 两列):
7
8
  * · `{}` ⇒ 整份日志(含别的分支),叶 = 源会话的叶(与引擎 `getEntriesToFork` 的无 `entryId` 臂同);
8
9
  * · `at` 点 ⇒ 根到分叉点那一条**路径**上的条目(与引擎 `getPathToRoot(entryId)` 同:路径,不是日志前缀,别的分支不随),叶 = 分叉点。
9
10
  *
@@ -11,24 +12,20 @@
11
12
  * 拷贝要的是**整份**持久日志(压缩地板绕过 —— 唤醒只装 `[地板..叶]`,从唤醒里拷会丢地板之前的全部历史);判官要的只是叶链上最近几条(它是引擎的一份纯函数,
12
13
  * 吃一只 `Session`):唤醒出来的会话 `getEntry` 在地板之下会回读持久日志,所以判官在压缩过的会话上也看得见整条叶链。
13
14
  */
14
- import { settledForkOptions, type SessionStorage } from "@sema-agent/core";
15
- /** 引擎答的分叉点(`{}` = 整份;`entryId` + `position: "at"` = 拷到这一条为止)。 */
16
- export type ForkPoint = Awaited<ReturnType<typeof settledForkOptions>>;
15
+ import { type ForkPoint, type ForkRowShape, type SessionStorage } from "@sema-agent/core";
17
16
  /** 问引擎:这只(已唤醒的)源会话从哪里拷。SQL 孪生用它;local 店手里就是引擎的 `Session`,直接调 `settledForkOptions`。 */
18
17
  export declare function forkPointOf(storage: SessionStorage): Promise<ForkPoint>;
19
- /** 整份日志里一行的父链两键(其余列原样搬,不读内容)。 */
18
+ /** 两只 SQL 孪生整份日志里的一行(两孪生选同一组列):父链两键交引擎挑行,其余三列原样搬进新会话、不读内容。 */
20
19
  export interface ForkLogRow {
21
20
  readonly entry_id: string;
22
21
  readonly parent_id: string | null;
22
+ readonly type: string;
23
+ readonly payload: unknown;
24
+ readonly ts: unknown;
23
25
  }
24
26
  /**
25
- * 按引擎的分叉点从整份日志(oldest-first)里挑出要拷的行,与新会话的叶。行的相对顺序保留(调用方按顺序重铸 seq)。
26
- *
27
- * 分叉点不在日志里 ⇒ `invalid_fork_target`、路径中途的父不在日志里 ⇒ `invalid_session`(引擎 `getEntriesToFork` / `getPathToRoot` 同词同句);分叉点之上的父链成环 ⇒ `invalid_session`、
28
- * 句子与引擎的父链守卫同(守卫类不在包根,走法是同一个意思:走过的条目再踩一次 = 环;沿用它的句子,让同一件事在三种后端上说同一句话);`position` 不是 `at` ⇒ 响亮拒(本函数只认引擎今天答的两形)。
27
+ * 两只 SQL 孪生的行怎么交给引擎的 `selectForkRows`(按答案挑行 + 新会话的叶;分叉点不在日志里 ⇒ `invalid_fork_target`、路径中途悬空父 / 父链成环 ⇒ `invalid_session`,
28
+ * 引擎的原词原句)。本仓不再自写父链走法 —— 那曾是与引擎 `getPathToRoot` 同义的第二份。
29
29
  */
30
- export declare function forkCopyOf<R extends ForkLogRow>(rows: readonly R[], sourceLeafId: string | null, point: ForkPoint): {
31
- rows: R[];
32
- leafId: string | null;
33
- };
30
+ export declare const FORK_LOG_ROW_SHAPE: ForkRowShape<ForkLogRow>;
34
31
  //# sourceMappingURL=session-fork.d.ts.map
@@ -1,29 +1,9 @@
1
- import { SessionError, StoredSession, settledForkOptions } from "@sema-agent/core";
1
+ import { StoredSession, settledForkOptions } from "@sema-agent/core";
2
2
  export async function forkPointOf(storage) {
3
3
  return settledForkOptions(new StoredSession(storage));
4
4
  }
5
- export function forkCopyOf(rows, sourceLeafId, point) {
6
- if (point.entryId === undefined)
7
- return { rows: [...rows], leafId: sourceLeafId };
8
- if (point.position !== "at") {
9
- throw new SessionError("invalid_fork_target", `fork point ${point.entryId} comes with a position this store does not copy (${String(point.position)}) — only the engine's "at" point is read here`);
10
- }
11
- const byId = new Map();
12
- for (const r of rows)
13
- byId.set(r.entry_id, r);
14
- const onPath = new Set();
15
- let cursor = point.entryId;
16
- while (cursor !== null) {
17
- if (onPath.has(cursor)) {
18
- throw new SessionError("invalid_session", `Entry ${cursor} is its own ancestor (parent chain cycles at ${cursor}) — start a new session, or fork from an entry above the loop`);
19
- }
20
- const row = byId.get(cursor);
21
- if (row === undefined) {
22
- throw new SessionError(cursor === point.entryId ? "invalid_fork_target" : "invalid_session", `Entry ${cursor} not found`);
23
- }
24
- onPath.add(cursor);
25
- cursor = row.parent_id;
26
- }
27
- return { rows: rows.filter((r) => onPath.has(r.entry_id)), leafId: point.entryId };
28
- }
5
+ export const FORK_LOG_ROW_SHAPE = {
6
+ idOf: (row) => row.entry_id,
7
+ parentOf: (row) => row.parent_id,
8
+ };
29
9
  //# sourceMappingURL=session-fork.js.map
@@ -22,4 +22,11 @@ export declare function forkOwnerOf(requested: string | null | undefined, source
22
22
  * 「首写」由调用方判(已记下属主的会话上的调用照旧静默不改,不经这里)。判官 = {@link isSessionOwnerValue}(core 7.56.0 [ref] `principalNameOf`)。
23
23
  */
24
24
  export declare function assertFirstOwnerWritable(sessionId: string, owner: unknown): asserts owner is string | null;
25
+ /**
26
+ * 导入者的属主 —— 换装的写腿(公开 `importEntries` / `replaceEntries` 与暂存提交)把调用方给的属主重戳进会话行之前的唯一读法:
27
+ * 主体名或 `null`(匿名)⇒ 原样交回;别的一切(空串、空白串、带控制 / 分隔 / 非连接符格式字符的串、非串)⇒ `TypeError` 响亮拒 ——
28
+ * 与引擎导入口拒同一类值时同一类错、同一句开头(引擎那一判只作用于交给它的属主,而换装交给引擎的是存档自己的戳,所以这一判在本仓)。
29
+ * 调用方在读写任何东西之前判,拒了就一个字节没写。判官 = {@link isSessionOwnerValue}(core `principalNameOf`),不在本仓另写规则。
30
+ */
31
+ export declare function importerOwnerOf(owner: unknown): string | null;
25
32
  //# sourceMappingURL=session-ownership.d.ts.map
@@ -22,4 +22,9 @@ export function assertFirstOwnerWritable(sessionId, owner) {
22
22
  throw new SessionError("storage", `register(${sessionId}): a first ownership write must carry a principal name or null (anonymous), got ${ownerValueFault(owner)} — ` +
23
23
  "a value that names nobody would leave the session claimable by anyone or ownable by no one; refusing, nothing written");
24
24
  }
25
+ export function importerOwnerOf(owner) {
26
+ if (isSessionOwnerValue(owner))
27
+ return owner;
28
+ throw new TypeError(`session owner must be a principal name or null (anonymous), got ${ownerValueFault(owner)} — an import stamps it as the session's owner; refusing, nothing written`);
29
+ }
25
30
  //# sourceMappingURL=session-ownership.js.map
@@ -0,0 +1,17 @@
1
+ import type { SessionWorkspace } from "../session-workspace.js";
2
+ import { type JsonColumnReader } from "./sql-json-column.js";
3
+ /** 列定义的唯一属主(两方言 DDL 内插它;存量库缺这一列 ⇒ 起服列册对账拒启,拒句里的 ALTER 由建表语句生成)。 */
4
+ export declare const SESSION_META_WORKSPACE_COLUMN: {
5
+ readonly name: "workspace";
6
+ readonly tidb: "workspace JSON NULL";
7
+ readonly pg: "workspace JSONB";
8
+ };
9
+ /** 这一格的形读者(三只店与帧投影同一只;写前也过它 —— 写得进的就是读得出的)。 */
10
+ export declare const readSessionWorkspaceCell: JsonColumnReader<SessionWorkspace>;
11
+ /** 这一格的失败留痕 tag(F 类;登记行在 `observability/fail-open.ts`)。记录座(写 / 帧)与本文件(读)同一只。 */
12
+ export declare const SESSION_WORKSPACE_DEGRADED: "server.session-workspace.record-degraded";
13
+ /** 值 → 这一格的文本。`encode` 是该承载的编码器(MySQL 协议与 local:`JSON.stringify`;PG:`pgSafeJsonStringify`)。写前过形读者,不合形响亮抛、什么都不写。 */
14
+ export declare function encodeSessionWorkspaceCell(workspace: SessionWorkspace, encode: (value: unknown) => string, sessionId: string): string;
15
+ /** 一格(已按文本投影;SQL NULL / local 缺键 ⇒ 调用方传 `null`)→ 值。坏体 ⇒ 读作没有记录并计一次(见文件头);读腿漏投影 ⇒ 抛。 */
16
+ export declare function decodeSessionWorkspaceCell(cell: unknown, sessionId: string): SessionWorkspace | undefined;
17
+ //# sourceMappingURL=session-workspace-cell.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { PROJECT_ID_REGEX } from "@sema-agent/core";
2
+ import { z } from "zod";
3
+ import { recordFailOpen } from "../observability/fail-open.js";
4
+ import { assertJsonColumnWritable, createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem } from "./sql-json-column.js";
5
+ export const SESSION_META_WORKSPACE_COLUMN = {
6
+ name: "workspace",
7
+ tidb: "workspace JSON NULL",
8
+ pg: "workspace JSONB",
9
+ };
10
+ const SESSION_WORKSPACE_SHAPE = z.object({
11
+ cwd: z.string().min(1),
12
+ root: z.string().min(1),
13
+ entered: z.object({ dir: z.string().min(1), byPath: z.literal(true).optional() }).optional(),
14
+ project: z.object({ id: z.string().regex(PROJECT_ID_REGEX), source: z.enum(["marker", "declared"]) }).optional(),
15
+ remote: z.boolean(),
16
+ });
17
+ export const readSessionWorkspaceCell = createZodColumnReader(SESSION_WORKSPACE_SHAPE);
18
+ export const SESSION_WORKSPACE_DEGRADED = "server.session-workspace.record-degraded";
19
+ const where = (sessionId) => `session_meta.workspace for session ${sessionId}`;
20
+ export function encodeSessionWorkspaceCell(workspace, encode, sessionId) {
21
+ const text = encode(workspace);
22
+ assertJsonColumnWritable(text, readSessionWorkspaceCell, where(sessionId));
23
+ return text;
24
+ }
25
+ export function decodeSessionWorkspaceCell(cell, sessionId) {
26
+ if (cell === null)
27
+ return undefined;
28
+ try {
29
+ return decodeJsonColumn(cell, readSessionWorkspaceCell, where(sessionId));
30
+ }
31
+ catch (e) {
32
+ if (!isJsonColumnBodyProblem(e))
33
+ throw e;
34
+ recordFailOpen(SESSION_WORKSPACE_DEGRADED, `op=read ${e.where} problem=${e.problem}`);
35
+ return undefined;
36
+ }
37
+ }
38
+ //# sourceMappingURL=session-workspace-cell.js.map
@@ -53,7 +53,7 @@ import { TiDBSessionStore } from "./tidb-session-store.js";
53
53
  import { PgSessionStore } from "./pg-session-storage.js";
54
54
  import { TiDBSharedMemoryStore, PgSharedMemoryStore } from "./shared-memory-store-sql.js";
55
55
  import { TiDBAdoptionLogStore, PgAdoptionLogStore } from "./adoption-log-sql.js";
56
- import { TiDBRetentionStore, PgRetentionStore } from "./retention-store-sql.js";
56
+ import { TiDBRetentionStore, PgRetentionStore, sqlStoreRetention } from "./retention-store-sql.js";
57
57
  import { TiDBRetentionLaneStore, PgRetentionLaneStore } from "./retention-lane-store-sql.js";
58
58
  import { createSqlPermissionRuleStores } from "./permission-rule-store-sql.js";
59
59
  import { createFilePermissionRuleStores } from "./permission-rule-store-file.js";
@@ -149,9 +149,9 @@ class TiDBBackend {
149
149
  workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
150
150
  parkDecisionsInst;
151
151
  sharedMemoryStore(authorizer) { return new TiDBSharedMemoryStore(this.pool, authorizer); }
152
- checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger); }
152
+ checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
153
153
  checkpointReadView() { return this.checkpoint(); }
154
- toolResult() { return new TiDBToolResultStore(this.pool); }
154
+ toolResult() { return new TiDBToolResultStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
155
155
  imageIndex() { return new TiDBImageIndex(this.pool); }
156
156
  imageBake() { return new TiDBImageBake(this.pool); }
157
157
  leaderRun() { return new TiDBLeaderRunStore(this.pool); }
@@ -161,7 +161,7 @@ class TiDBBackend {
161
161
  breaker(onWriteFail) { return new TiDBBreakerState(this.pool, onWriteFail); }
162
162
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new TiDBCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
163
163
  rateLimiter(limit, onDegraded) { return new TiDBRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
164
- session() { return new TiDBSessionStore(this.pool, { log: createLogger(this.config.logLevel) }); }
164
+ session() { return new TiDBSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
165
165
  adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
166
166
  mysqlPool() { return this.pool; }
167
167
  pgPool() { return undefined; }
@@ -203,9 +203,9 @@ class PgBackend {
203
203
  workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
204
204
  parkDecisionsInst;
205
205
  sharedMemoryStore(authorizer) { return new PgSharedMemoryStore(this.pool, authorizer); }
206
- checkpoint(logger) { return new PgCheckpointStore(this.pool, logger); }
206
+ checkpoint(logger) { return new PgCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
207
207
  checkpointReadView() { return this.checkpoint(); }
208
- toolResult() { return new PgToolResultStore(this.pool); }
208
+ toolResult() { return new PgToolResultStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
209
209
  imageIndex() { return new PgImageIndex(this.pool); }
210
210
  imageBake() { return new PgImageBake(this.pool); }
211
211
  leaderRun() { return new PgLeaderRunStore(this.pool); }
@@ -215,7 +215,7 @@ class PgBackend {
215
215
  breaker(onWriteFail) { return new PgBreakerState(this.pool, onWriteFail); }
216
216
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new PgCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
217
217
  rateLimiter(limit, onDegraded) { return new PgRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
218
- session() { return new PgSessionStore(this.pool, { log: createLogger(this.config.logLevel) }); }
218
+ session() { return new PgSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
219
219
  adoptionLog() { return new PgAdoptionLogStore(this.pool); }
220
220
  mysqlPool() { return undefined; }
221
221
  pgPool() { return this.pool; }
@@ -7,6 +7,7 @@ import { TIDB_ADOPTION_LOG_STATEMENTS } from "./adoption-log-sql.js";
7
7
  import { TIDB_PERMISSION_RULE_STATEMENTS } from "./permission-rule-store-sql.js";
8
8
  import { TIDB_RETENTION_STATEMENTS } from "./retention-store-sql.js";
9
9
  import { SESSION_META_INCARNATION_COLUMN } from "../session-incarnation.js";
10
+ import { SESSION_META_WORKSPACE_COLUMN } from "./session-workspace-cell.js";
10
11
  import { RUN_IDENTITY_COLUMNS } from "../identity-source.js";
11
12
  import { SESSION_RULES_COLUMN } from "./session-rules-column.js";
12
13
  import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
@@ -73,6 +74,11 @@ export const SCHEMA_STATEMENTS = [
73
74
  -- (CLAUDE.md / .claude/CLAUDE.md / AGENTS.md / .claude/AGENTS.md)。服务端元数据:不进模型上下文、不进 wire
74
75
  -- 会话面;住在会话行上 ⇒ 随会话删除 / 留存清扫一起消失,fork 出的新行为 NULL(新会话首 run 同样只写不发)。
75
76
  instruction_source VARCHAR(64) NULL,
77
+ -- workspace:本会话上一条腿**自己的**最后一帧 workspace_changed(引擎 7.58.0 的整份快照:cwd / root / entered / project / remote)。
78
+ -- NULL = 还没有任何腿记过;唯一写者 = 腿的记录座(src/session-workspace.ts),列属主 = src/plugins/session-workspace-cell.ts
79
+ -- (列定义、形读者、编解码;存量库缺这一列 ⇒ 起服列册对账拒启,拒句里给出 ALTER)。下一条腿的 TaskSpec.projectId 读它的 project.id。
80
+ -- 服务端元数据:不进 wire 会话面、不进模型上下文;住在会话行上 ⇒ 随会话删除 / 留存清扫一起消失,fork 出的新行为 NULL。
81
+ ${SESSION_META_WORKSPACE_COLUMN.tidb},
76
82
  -- #292 C18(core 5.48.0 design/323)subagent 转录持久化 —— placement 元组,**创建时一次落行、
77
83
  -- first-write 不可变**(register 的 ON DUPLICATE 臂永不重写这六列)。判别位 = placement_kind:
78
84
  -- 在场 ⇒ 该会话住在 placement 分区里(不上宿主枚举、claim 形 acquire 恒拒、release=真删除、
@@ -13,6 +13,8 @@
13
13
  import { type AcquiredSession, type PlacedSessionRow, type SessionHead, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
14
14
  import type { Pool } from "mysql2/promise";
15
15
  import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
16
+ import type { SessionWorkspace } from "../session-workspace.js";
17
+ import type { RetentionDeclaration } from "@sema-agent/core";
16
18
  import { type SessionFace } from "../session-incarnation.js";
17
19
  import type { SessionSummary } from "./store-contracts.js";
18
20
  import type { SessionRowPatch } from "../session-row.js";
@@ -21,9 +23,9 @@ import { type PlacementAcquireOpts } from "./session-placement.js";
21
23
  import { type StagingHandle } from "../session-sync-kernel.js";
22
24
  export declare class TiDBSessionStore implements SessionStore {
23
25
  private readonly pool;
24
- /** [ref] 车1:托管留存声明。读法与「为什么 SQL 店答 managed 而实现体在别处」逐字见
26
+ /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法逐字见
25
27
  * `retention-store-sql.ts` 的 {@link MANAGED_RETENTION}。 */
26
- readonly retention: import("@sema-agent/core").RetentionDeclaration;
28
+ readonly retention: RetentionDeclaration;
27
29
  /** [ref] C18(core 5.48.0 [ref]):placement **声明**。六条义务的实装点逐条见
28
30
  * {@link SQL_SESSION_PLACEMENTS} 顶注与本类的 acquire / listSessions / release / listPlaced /
29
31
  * placementOf 各处的 `[ref]` 注。 */
@@ -38,6 +40,7 @@ export declare class TiDBSessionStore implements SessionStore {
38
40
  private readonly log;
39
41
  constructor(pool: Pool, opts?: {
40
42
  readonly log?: OrphanOwnerLog;
43
+ readonly retention?: RetentionDeclaration;
41
44
  });
42
45
  acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
43
46
  private load;
@@ -165,6 +168,18 @@ export declare class TiDBSessionStore implements SessionStore {
165
168
  * 在期限内拒。⚠️ mysql2 的超时是客户端侧的 —— 拒之后那条连接仍被挂着的命令占到服务端结束(S-9 同形,如实披露)。
166
169
  */
167
170
  recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
171
+ /**
172
+ * 会话行的位置与项目那一格(`session_meta.workspace`;域律 `src/session-workspace.ts`,列属主 `session-workspace-cell.ts`)。
173
+ * 一行 PK 读,按文本投影、恰好解一次;行不在 / 格为 NULL ⇒ `undefined`;格读不出 ⇒ 读作没有记录并计一次(F 类,见列属主头注)。
174
+ */
175
+ readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
176
+ /**
177
+ * 整格覆写(最后一帧胜;唯一调用方 = 腿的记录座,它在腿的流循环里一笔一笔等写完)。**住客围栏**:只写化身仍是 `incarnation` 的那一行 ——
178
+ * 行不在、或同 id 已换了住客 ⇒ 0 行、不抛(那一行不是这条腿的)。**不**碰 `updated_at`(那是会话活动时刻,留存腿按它判龄;记下站在哪里不是一次会话活动)。
179
+ * 走店的一般查询面(与腿循环里的账本追加同一形,不另设每查询的客户端超时):mysql2 的客户端超时只是不再等,服务端照把那条 UPDATE 跑完 ——
180
+ * 一笔「放弃等待却仍会落地」的写正是这条腿离开之后还能盖掉下一条腿的那一形。卡住的上界归服务端(锁等待超时)与部署的池策略。
181
+ */
182
+ recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
168
183
  /**
169
184
  * E17 — fork a session: copy its WHOLE durable history to a NEW uuidv7 session owned by `owner`, mirroring
170
185
  * core's `PgSessionRepo.fork`. Entry ids / parent links are per-session and copy VERBATIM; only `seq` is
@@ -4,7 +4,8 @@ import { decodeSessionEventPayload } from "./session-event-payload.js";
4
4
  import { jsonColumnText } from "./sql-json-column.js";
5
5
  import { escapeLike } from "./sql-escape.js";
6
6
  import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
7
- import { MANAGED_RETENTION } from "./retention-store-sql.js";
7
+ import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
8
+ import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
8
9
  import { carriedIncarnation, headFromRow, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
9
10
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
10
11
  import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRow } from "../delegated-session-owner.js";
@@ -12,16 +13,18 @@ import { createLogger } from "../observability/logger.js";
12
13
  import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
13
14
  import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
14
15
  import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
15
- import { forkCopyOf, forkPointOf } from "./session-fork.js";
16
+ import { selectForkRows } from "@sema-agent/core";
17
+ import { FORK_LOG_ROW_SHAPE, forkPointOf } from "./session-fork.js";
16
18
  export class TiDBSessionStore {
17
19
  pool;
18
- retention = MANAGED_RETENTION;
20
+ retention;
19
21
  placements = SQL_SESSION_PLACEMENTS;
20
22
  pending = new Map();
21
23
  log;
22
24
  constructor(pool, opts = {}) {
23
25
  this.pool = pool;
24
26
  this.log = opts.log ?? createLogger();
27
+ this.retention = opts.retention ?? UNMANAGED_RETENTION;
25
28
  }
26
29
  async acquire(sessionId, opts) {
27
30
  if (!sessionId) {
@@ -247,6 +250,14 @@ export class TiDBSessionStore {
247
250
  await this.pool.query({ sql: "UPDATE session_meta SET instruction_source = ? WHERE session_id = ? AND instruction_source IS NULL", timeout: BASELINE_IO_DEADLINE_MS }, [next, sessionId]);
248
251
  }
249
252
  }
253
+ async readSessionWorkspace(sessionId) {
254
+ const [rows] = await this.pool.query(`SELECT ${jsonColumnText("tidb", "workspace")} FROM session_meta WHERE session_id = ?`, [sessionId]);
255
+ const row = rows[0];
256
+ return row === undefined ? undefined : decodeSessionWorkspaceCell(row.workspace, sessionId);
257
+ }
258
+ async recordSessionWorkspace(sessionId, incarnation, workspace) {
259
+ await this.pool.query("UPDATE session_meta SET workspace = ? WHERE session_id = ? AND incarnation = ?", [encodeSessionWorkspaceCell(workspace, JSON.stringify, sessionId), sessionId, incarnation]);
260
+ }
250
261
  async fork(sourceId, owner) {
251
262
  const [metaRows] = await this.pool.query("SELECT leaf_id, owner FROM session_meta WHERE session_id = ?", [sourceId]);
252
263
  if (!metaRows[0])
@@ -258,7 +269,7 @@ export class TiDBSessionStore {
258
269
  return null;
259
270
  const point = await forkPointOf(storage);
260
271
  const [allRows] = await this.pool.query(`SELECT entry_id, parent_id, type, ${jsonColumnText("tidb", "payload")}, ts FROM session_event WHERE session_id = ? ORDER BY seq ASC`, [sourceId]);
261
- const { rows: eventRows, leafId: forkLeafId } = forkCopyOf(allRows, leafId, point);
272
+ const { rows: eventRows, leafId: forkLeafId } = selectForkRows(allRows, leafId, point, FORK_LOG_ROW_SHAPE);
262
273
  const newId = uuidv7();
263
274
  const now = new Date();
264
275
  const conn = await this.pool.getConnection();
@@ -353,7 +364,7 @@ export class TiDBSessionStore {
353
364
  }
354
365
  await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
355
366
  await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ? AND placement_kind IS NULL", [sessionId, owner]);
356
- await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(sessionId, placedRows[0] === undefined ? undefined : { owner: placedRows[0].owner, incarnation: placedRows[0].incarnation }, owner));
367
+ await this.insertValidatedLog(conn, sessionId, owner, validated, carriedIncarnation(sessionId, placedRows[0] === undefined ? undefined : { incarnation: placedRows[0].incarnation }));
357
368
  await conn.commit();
358
369
  }
359
370
  catch (err) {
@@ -2,6 +2,7 @@ import type { ToolResultProvenance, ToolResultStore, ToolResultSlice, ToolResult
2
2
  import type { Pool as MySqlPool } from "mysql2/promise";
3
3
  import type { Pool as PgPool, PoolClient as PgPoolClient } from "pg";
4
4
  import { type SqlDriver } from "./sql-driver.js";
5
+ import type { RetentionDeclaration } from "@sema-agent/core";
5
6
  import { type IndexSpec } from "./ensure-index.js";
6
7
  /** PG schema for the tool_result table — the PG translation of tidb-pool.ts's tool_result DDL
7
8
  * (LONGTEXT→TEXT, DATETIME(3)→TIMESTAMPTZ(3), inline KEY→separate CREATE INDEX). Disjoint from other stores'
@@ -29,10 +30,12 @@ export declare function assertToolResultProvenanceSchema(query: (sql: string) =>
29
30
  /** Dual-dialect durable ToolResultStore. See the file header for the dialect-delta ledger. */
30
31
  export declare class SqlToolResultStore implements ToolResultStore {
31
32
  protected readonly db: SqlDriver;
32
- /** [ref] 车1:托管留存声明。读法与「为什么 SQL 店答 managed 而三方法的实现体在 `retention-store-sql.ts`」
33
- * 逐字见 {@link MANAGED_RETENTION}(本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
34
- readonly retention: import("@sema-agent/core").RetentionDeclaration;
35
- constructor(db: SqlDriver);
33
+ /** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法与「为什么 SQL 店答 managed 而三方法的实现体在
34
+ * `retention-store-sql.ts`」逐字见 {@link MANAGED_RETENTION}(扫车道开着时本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
35
+ readonly retention: RetentionDeclaration;
36
+ constructor(db: SqlDriver, opts?: {
37
+ readonly retention?: RetentionDeclaration;
38
+ });
36
39
  /** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
37
40
  private q;
38
41
  /** D1(试剂盒揪出,[ref] 语义):ref 是主键,而 `deleteBySession` 只认**记录下来的出处**与单射的
@@ -97,10 +100,14 @@ export declare class SqlToolResultStore implements ToolResultStore {
97
100
  }
98
101
  /** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
99
102
  export declare class TiDBToolResultStore extends SqlToolResultStore {
100
- constructor(pool: MySqlPool);
103
+ constructor(pool: MySqlPool, opts?: {
104
+ readonly retention?: RetentionDeclaration;
105
+ });
101
106
  }
102
107
  /** PostgreSQL binding — historical class name + ctor shape preserved. */
103
108
  export declare class PgToolResultStore extends SqlToolResultStore {
104
- constructor(pool: PgPool);
109
+ constructor(pool: PgPool, opts?: {
110
+ readonly retention?: RetentionDeclaration;
111
+ });
105
112
  }
106
113
  //# sourceMappingURL=tool-result-store-sql.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { MAX_MINTED_TOOL_RESULT_REF_CHARS, assertSafeToolResultRef, assertToolResultProvenanceMatch, normalizeToolResultProvenance } from "@sema-agent/core";
2
2
  import { pgSanitizeText } from "./pg-safe-json.js";
3
3
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
4
- import { MANAGED_RETENTION } from "./retention-store-sql.js";
4
+ import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
5
5
  import { ensureIndexes } from "./ensure-index.js";
6
6
  export const PG_TOOL_RESULT_SCHEMA = [
7
7
  `CREATE TABLE IF NOT EXISTS tool_result (
@@ -49,9 +49,10 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
49
49
  }
50
50
  export class SqlToolResultStore {
51
51
  db;
52
- retention = MANAGED_RETENTION;
53
- constructor(db) {
52
+ retention;
53
+ constructor(db, opts = {}) {
54
54
  this.db = db;
55
+ this.retention = opts.retention ?? UNMANAGED_RETENTION;
55
56
  }
56
57
  q(tidb, pg) {
57
58
  return this.db.dialect === "tidb" ? tidb : pg;
@@ -128,13 +129,13 @@ export class SqlToolResultStore {
128
129
  }
129
130
  }
130
131
  export class TiDBToolResultStore extends SqlToolResultStore {
131
- constructor(pool) {
132
- super(mysqlDriver(pool));
132
+ constructor(pool, opts) {
133
+ super(mysqlDriver(pool), opts);
133
134
  }
134
135
  }
135
136
  export class PgToolResultStore extends SqlToolResultStore {
136
- constructor(pool) {
137
- super(pgDriver(pool));
137
+ constructor(pool, opts) {
138
+ super(pgDriver(pool), opts);
138
139
  }
139
140
  }
140
141
  //# sourceMappingURL=tool-result-store-sql.js.map
@@ -54,8 +54,9 @@ export declare function withWorktreeIsolation(baseFactory: ExecutionEnvFactory,
54
54
  * post-kill Runner hook). NEVER throws (a prune failure is logged, never fatal).
55
55
  *
56
56
  * NOTE: `git worktree prune` only deregisters worktrees whose DIRECTORY is missing. A crashed run that left
57
- * the dir intact is NOT reaped by prune alone — that is acceptable for v1 (the worktree lives under the known
58
- * `.sema-worktrees/` parent and is human-/script-reapable; a stricter age-based sweep is a follow-on).
57
+ * the dir intact is NOT reaped by prune alone — that is acceptable for v1 (the worktree lives under core's known
58
+ * managed parent `<repoRoot>/<WORKTREE_PARENT>/` on its own `sema/<name>` branch and is human-/script-reapable;
59
+ * a stricter age-based sweep is a follow-on).
59
60
  */
60
61
  export declare function reapOrphanWorktrees(baseEnvForGit: ExecutionEnv, repoRoot: string, logger?: WorktreeIsolationLogger): Promise<void>;
61
62
  //# sourceMappingURL=worktree-isolation.d.ts.map