@sema-agent/server 7.140.0 → 7.141.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/MIGRATION.md +290 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +75 -13
  5. package/dist/approval-batch-census.d.ts +27 -0
  6. package/dist/approval-batch-census.js +27 -0
  7. package/dist/approval-card.d.ts +31 -16
  8. package/dist/approval-card.js +6 -3
  9. package/dist/approval.d.ts +15 -0
  10. package/dist/approval.js +10 -0
  11. package/dist/audit.d.ts +4 -2
  12. package/dist/audit.js +2 -2
  13. package/dist/boot/device-lane.d.ts +11 -0
  14. package/dist/boot/device-lane.js +16 -0
  15. package/dist/boot/engine-lease.d.ts +24 -3
  16. package/dist/boot/engine-lease.js +9 -2
  17. package/dist/boot/execution-env.js +8 -4
  18. package/dist/boot/memory-boundary.d.ts +2 -2
  19. package/dist/boot/memory-consolidation.d.ts +1 -1
  20. package/dist/boot/memory-consolidation.js +1 -1
  21. package/dist/boot/reapers.d.ts +2 -2
  22. package/dist/boot/resolve-spec.js +7 -4
  23. package/dist/boot/session-faces.d.ts +1 -1
  24. package/dist/boot/session-faces.js +4 -3
  25. package/dist/boot/shutdown.js +5 -0
  26. package/dist/boot/stage-05-execution-env.js +3 -1
  27. package/dist/boot/stage-06-runners.d.ts +0 -2
  28. package/dist/boot/stage-06-runners.js +1 -1
  29. package/dist/boot/stage-07-capability-layer.d.ts +0 -1
  30. package/dist/boot/stage-07-capability-layer.js +8 -10
  31. package/dist/boot/stage-08-reapers.d.ts +0 -1
  32. package/dist/boot/stage-09-leader.d.ts +0 -1
  33. package/dist/boot/stage-10-http-server.d.ts +0 -1
  34. package/dist/boot/stage-10-http-server.js +1 -0
  35. package/dist/boot/stores.js +10 -4
  36. package/dist/boot/web-admin.js +1 -0
  37. package/dist/capabilities/scenarios.d.ts +1 -1
  38. package/dist/capability-facts.d.ts +9 -0
  39. package/dist/capability-facts.js +9 -0
  40. package/dist/card-void.d.ts +38 -0
  41. package/dist/card-void.js +23 -0
  42. package/dist/config-catalog.js +11 -8
  43. package/dist/config-provider.d.ts +13 -13
  44. package/dist/config-provider.js +19 -33
  45. package/dist/config-types.d.ts +26 -8
  46. package/dist/config.js +55 -46
  47. package/dist/delegated-session-owner.d.ts +21 -19
  48. package/dist/delegated-session-owner.js +1 -1
  49. package/dist/device-store.d.ts +7 -0
  50. package/dist/device-store.js +3 -0
  51. package/dist/execution-config-root.d.ts +22 -0
  52. package/dist/execution-config-root.js +35 -0
  53. package/dist/execution-lane-caps.d.ts +49 -2
  54. package/dist/execution-lane-caps.js +90 -13
  55. package/dist/execution-placement.d.ts +3 -0
  56. package/dist/execution-placement.js +7 -0
  57. package/dist/fleet/drain-entry.d.ts +3 -0
  58. package/dist/hosted-posture.d.ts +8 -0
  59. package/dist/hosted-posture.js +4 -0
  60. package/dist/http/admission.js +3 -3
  61. package/dist/http/caller-trust.d.ts +3 -2
  62. package/dist/http/caller-trust.js +2 -1
  63. package/dist/http/dispatch.js +4 -0
  64. package/dist/http/live-frame.d.ts +24 -0
  65. package/dist/http/live-frame.js +16 -0
  66. package/dist/http/resume-legs.js +37 -20
  67. package/dist/http/route-table.js +2 -0
  68. package/dist/http/routes/approvals-assistant.js +1 -0
  69. package/dist/http/routes/capabilities.js +5 -4
  70. package/dist/http/routes/diagnostics.d.ts +0 -19
  71. package/dist/http/routes/diagnostics.js +16 -2
  72. package/dist/http/routes/memory-bundle.d.ts +2 -2
  73. package/dist/http/routes/memory-bundle.js +1 -1
  74. package/dist/http/routes/memory-compliance.js +1 -1
  75. package/dist/http/routes/memory-policy.js +2 -2
  76. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  77. package/dist/http/routes/run-permission-mode.js +170 -0
  78. package/dist/http/routes/session-sync.d.ts +3 -6
  79. package/dist/http/routes/session-sync.js +7 -2
  80. package/dist/http/routes/sessions.js +5 -4
  81. package/dist/http/routes/tasks.js +63 -66
  82. package/dist/http/run-read-face.js +3 -3
  83. package/dist/http/send.d.ts +1 -1
  84. package/dist/http/send.js +2 -2
  85. package/dist/http/server.d.ts +9 -2
  86. package/dist/http/sse-log.d.ts +3 -8
  87. package/dist/http/stored-body-replay.d.ts +3 -3
  88. package/dist/http/stored-body-replay.js +3 -1
  89. package/dist/instruction-source-notice.d.ts +7 -0
  90. package/dist/instruction-source-notice.js +6 -6
  91. package/dist/mcp-assertion-header.d.ts +4 -0
  92. package/dist/mcp-assertion-header.js +5 -0
  93. package/dist/memory-posture.d.ts +1 -1
  94. package/dist/memory-scope.d.ts +4 -4
  95. package/dist/memory-scope.js +1 -1
  96. package/dist/memory-sync-client.d.ts +14 -2
  97. package/dist/memory-sync-client.js +80 -18
  98. package/dist/observability/fail-open.d.ts +30 -2
  99. package/dist/observability/fail-open.js +30 -2
  100. package/dist/observability/metrics.js +0 -1
  101. package/dist/observability/run-terminal.d.ts +6 -2
  102. package/dist/observability/run-terminal.js +25 -26
  103. package/dist/observability/secret-env-scrub.d.ts +20 -1
  104. package/dist/observability/secret-env-scrub.js +14 -0
  105. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  106. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  107. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  108. package/dist/orchestration/workflow-notify-journal.js +2 -1
  109. package/dist/permission-mode-admission.d.ts +68 -0
  110. package/dist/permission-mode-admission.js +23 -0
  111. package/dist/permission-rule-list.d.ts +55 -6
  112. package/dist/permission-rule-list.js +52 -5
  113. package/dist/plugins/caching-session-store.d.ts +6 -0
  114. package/dist/plugins/caching-session-store.js +3 -0
  115. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  116. package/dist/plugins/checkpoint-store-sql.js +74 -3
  117. package/dist/plugins/device-store-sql.d.ts +5 -2
  118. package/dist/plugins/device-store-sql.js +4 -1
  119. package/dist/plugins/execution-placement-sql.d.ts +11 -1
  120. package/dist/plugins/execution-placement-sql.js +12 -1
  121. package/dist/plugins/file-run-store.d.ts +2 -2
  122. package/dist/plugins/file-run-store.js +1 -1
  123. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  124. package/dist/plugins/fork-routing-session-store.js +2 -61
  125. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  126. package/dist/plugins/k8s-exec-protocol.js +6 -1
  127. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  128. package/dist/plugins/local-checkpoint-store.js +22 -0
  129. package/dist/plugins/local-session-store.d.ts +227 -35
  130. package/dist/plugins/local-session-store.js +518 -37
  131. package/dist/plugins/local-session-window.d.ts +17 -3
  132. package/dist/plugins/local-session-window.js +7 -13
  133. package/dist/plugins/memory-run-store.d.ts +2 -2
  134. package/dist/plugins/memory-run-store.js +1 -1
  135. package/dist/plugins/one-time-migrations.d.ts +42 -2
  136. package/dist/plugins/one-time-migrations.js +65 -3
  137. package/dist/plugins/pg-pool.js +3 -2
  138. package/dist/plugins/pg-session-storage.d.ts +2 -0
  139. package/dist/plugins/pg-session-storage.js +3 -2
  140. package/dist/plugins/remote-env-e2b.d.ts +24 -3
  141. package/dist/plugins/remote-env-e2b.js +131 -18
  142. package/dist/plugins/remote-env-k8s.d.ts +20 -1
  143. package/dist/plugins/remote-env-k8s.js +148 -25
  144. package/dist/plugins/remote-env-local-docker.d.ts +25 -11
  145. package/dist/plugins/remote-env-local-docker.js +87 -21
  146. package/dist/plugins/run-store-sql.d.ts +3 -3
  147. package/dist/plugins/run-store-sql.js +4 -3
  148. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  149. package/dist/plugins/sandbox-read-floor.js +608 -0
  150. package/dist/plugins/session-ownership.d.ts +2 -1
  151. package/dist/plugins/session-placement.d.ts +17 -4
  152. package/dist/plugins/session-placement.js +2 -1
  153. package/dist/plugins/session-store.d.ts +18 -20
  154. package/dist/plugins/session-store.js +4 -12
  155. package/dist/plugins/store-backend.d.ts +31 -0
  156. package/dist/plugins/store-backend.js +21 -4
  157. package/dist/plugins/store-contracts.d.ts +9 -2
  158. package/dist/plugins/tidb-pool.js +2 -1
  159. package/dist/plugins/tidb-session-store.d.ts +4 -1
  160. package/dist/plugins/tidb-session-store.js +3 -2
  161. package/dist/run-cancel-context.d.ts +34 -0
  162. package/dist/run-cancel-context.js +7 -0
  163. package/dist/runs.d.ts +39 -12
  164. package/dist/runs.js +68 -53
  165. package/dist/security.d.ts +2 -2
  166. package/dist/server-secret-env.d.ts +2 -1
  167. package/dist/server-secret-env.js +2 -4
  168. package/dist/session-error-wire.d.ts +22 -4
  169. package/dist/session-error-wire.js +16 -0
  170. package/dist/session-incarnation.d.ts +9 -3
  171. package/dist/session-sync-kernel.d.ts +42 -1
  172. package/dist/session-sync-kernel.js +17 -0
  173. package/dist/session-sync.js +16 -4
  174. package/dist/sql-engine-coordinates.d.ts +49 -0
  175. package/dist/sql-engine-coordinates.js +27 -0
  176. package/dist/standing-answer-grant.d.ts +31 -2
  177. package/dist/standing-answer-grant.js +14 -2
  178. package/dist/store-posture.d.ts +1 -1
  179. package/dist/task-a2a.d.ts +1 -1
  180. package/dist/task-mcp.d.ts +1 -1
  181. package/dist/task-settings.d.ts +52 -25
  182. package/dist/task-settings.js +91 -28
  183. package/dist/terminal.d.ts +77 -20
  184. package/dist/terminal.js +66 -20
  185. package/dist/trace/core-keyset-guard.d.ts +79 -2
  186. package/dist/trace/exit-seat.d.ts +7 -0
  187. package/dist/trace/exit-seat.js +1 -0
  188. package/dist/trace/model-segment.d.ts +2 -2
  189. package/dist/trace/redact.js +323 -90
  190. package/dist/trace/self-minted-wire.d.ts +168 -2
  191. package/dist/trace/self-minted-wire.js +63 -0
  192. package/dist/verifier-pause-card.d.ts +8 -0
  193. package/dist/verifier-pause-card.js +19 -0
  194. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  195. package/dist/web-admin-client/apply-effective.js +2 -2
  196. package/dist/web-admin-client/candidate-gate.d.ts +1 -1
  197. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  198. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  199. package/package.json +2 -2
  200. package/wire-changes/server@7.141.0.json +367 -0
@@ -1,7 +1,7 @@
1
1
  import { type SessionStore } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import type { StoreBackend } from "./store-backend.js";
4
- import { type DelegationRootWitness, type DelegationTree, type OrphanOwnerLog } from "../delegated-session-owner.js";
4
+ import { type DelegationRootWitness, type OrphanOwnerLog } from "../delegated-session-owner.js";
5
5
  import type { OwnerAwareSessionStore } from "../security.js";
6
6
  /**
7
7
  * Session-store plugin seam.
@@ -37,31 +37,29 @@ export declare function createSessionStore(config: Pick<ServiceConfig, "sessionB
37
37
  * (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
38
38
  * 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
39
39
  * 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
40
- * 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
41
- * `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
40
+ * 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 stage-07 装配区:
41
+ * `ensureChildSessionDurable` 闭包只在部署的子代转录档位是 `full` 时构造(core `resolveSubagentTranscriptTier`:会话店声明了 durable 的委派分区 ——
42
+ * 三只持久会话店;无 repo 的内存店不声明,档位不是 `full`,挂点不装)。
42
43
  */
43
44
  export declare function ensureSessionDurable(hostStore: SessionStore, sessionId: string): Promise<void>;
44
45
  /**
45
- * [ref] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
46
- * 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
47
- * (14ms~2s)+ settle failed,赎回窗口不存在):
48
- * 0. 属主([ref],`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
46
+ * core `SubagentToolOptions.background.ensureChildSessionDurable` 的实现体 —— 停驻挂点的**见证**:这只后台子代的转录此刻是 host 店里一只有真内容、
47
+ * 属主按委派继承律定得下来的会话,才放行停驻;任何一步不成立 ⇒ 抛 = 否决(core 收 veto 即 expire checkpoint + settle failed,pre-153 行为,不放大)。
48
+ *
49
+ * 规则(`fork-routing-session-store.ts` 头注):一只后台子代的 durable 行在,它的转录就是 host 店委派分区里的一只 placed 行。挂点只装在声明了
50
+ * durable 委派分区的部署上(stage-07:core `resolveSubagentTranscriptTier(...) === "full"`),那里 core 给每只新起的后台子代铸 placement、
51
+ * 转录生来就在 host 店 —— 所以这里**只验、不搬**(此前的「晋升」把私店的转录整树拷进 host 店,随这条规则退役)。
52
+ * 0. 属主(`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
49
53
  * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与写口放行的那一位(认领之前解析器那一读);core 的挂点签名只给子代 id,
50
- * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`:不按新住客晋升 / 认领);
54
+ * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`);
51
55
  * 缺席(行上没有根)或根行此刻不在 ⇒ 孤行哨兵。
52
- * 1. 快路:已在 host 店且有真内容 → 通过,**前提是那一行的属主按律定得下来**(`settleDelegatedOwner`:记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次
53
- * (与 M23 同读数),无主 ⇒ 认领(CAS 子行,写判断时的读数)、没落下 ⇒ 抛(只有记录委派树的 SQL 店;local 店的行不记委派树 ⇒ 不认领);此刻属主不是律给的那一只 ⇒ 那是别人的同 id 会话,
54
- * 不是这只子代的转录 ⇒ 抛 = 停驻否决;与晋升建行前那一道同一只函数)。
55
- * 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
56
- * 三步见 ForkRoutingSessionStore.promoteToHost),宿主行在建行那一笔里带第 0 步的属主;写的是哨兵 ⇒ 写落下之后一行 warn。
57
- * 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)+ 同一只属主定夺 —— 晋升产物必须达到与快路完全相同的
58
- * 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
56
+ * 1. 在不在、有没有真内容:{@link ensureSessionDurable}(`requireExisting` 打开 + 叶子非空 + forget 收尾)。任何失败(不在、空壳、读故障)⇒ 原样上抛 = 否决
57
+ * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。这里**不判它是不是 placed 行**:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
58
+ * 它照样是这只子代的转录;判据只问「host 店里有这份真内容」。
59
+ * 2. 属主定夺(`settleDelegatedOwner`):记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次(与 M23 同读数),无主 ⇒ 认领(CAS 子行,写判断时的读数)、没落下 ⇒ 抛;
60
+ * 此刻属主不是律给的那一只 ⇒ 那是别人的同 id 会话,不是这只子代的转录 ⇒ 抛 = 停驻否决。认领写下的是哨兵 ⇒ 写落下之后一行 warn(`site: "park"`)。
59
61
  * 宿主店必须读得出属主(`delegatedOwnerHostOf`:`headOf`;记录委派树的店另交 `placementOf` + `claimOwnerless`,两只同在或同缺;生产的 SQL / local 店与缓存包装层都满足),
60
62
  * 否则响亮拒。
61
- * 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
62
- * (stage-07 的 `config.sessionBackend === "tidb"` 门不变 —— 该词在 local 后端上同样归一成 `tidb`,所以本腿在 local 宿主店上也跑)。
63
63
  */
64
- export declare function ensureChildSessionDurableWithPromotion(hostStore: OwnerAwareSessionStore, routing: {
65
- promoteToHost(sessionId: string, owner: string | null, tree: DelegationTree): Promise<void>;
66
- }, sessionId: string, root: DelegationRootWitness | undefined, log: OrphanOwnerLog): Promise<void>;
64
+ export declare function attestChildSessionDurable(hostStore: OwnerAwareSessionStore, sessionId: string, root: DelegationRootWitness | undefined, log: OrphanOwnerLog): Promise<void>;
67
65
  //# sourceMappingURL=session-store.d.ts.map
@@ -23,24 +23,16 @@ export async function ensureSessionDurable(hostStore, sessionId) {
23
23
  await hostStore.forget?.(acquired.sessionId);
24
24
  }
25
25
  }
26
- export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId, root, log) {
26
+ export async function attestChildSessionDurable(hostStore, sessionId, root, log) {
27
27
  const host = delegatedOwnerHostOf(hostStore);
28
28
  if (host === undefined) {
29
29
  throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (headOf), or records delegation trees without being able to claim under them (placementOf / claimOwnerless), so the delegated transcript's owner cannot be derived or checked`);
30
30
  }
31
31
  const judged = await readDelegatedOwner(sessionId, root === undefined ? {} : { rootSessionId: root.sessionId }, sourceFacesVia(host.headOf));
32
32
  assertRootStillWitnessed(sessionId, root, judged);
33
- let promoted = false;
34
- try {
35
- await ensureSessionDurable(hostStore, sessionId);
36
- }
37
- catch {
38
- await routing.promoteToHost(sessionId, judged.inherited.owner, judged.tree);
39
- await ensureSessionDurable(hostStore, sessionId);
40
- promoted = true;
41
- }
33
+ await ensureSessionDurable(hostStore, sessionId);
42
34
  const settled = await settleDelegatedOwner(host, sessionId, judged.inherited.owner, judged.tree);
43
- if (promoted || settled.claimed)
44
- reportWrittenOwner(log, "promotion", sessionId, settled.judged ?? judged);
35
+ if (settled.claimed)
36
+ reportWrittenOwner(log, "park", sessionId, settled.judged ?? judged);
45
37
  }
46
38
  //# sourceMappingURL=session-store.js.map
@@ -5,6 +5,7 @@ import { type OutcomeSink } from "./file-outcome-sink.js";
5
5
  import { type InboxWarn } from "./workflow-run-store-sql.js";
6
6
  import { FileRunStore } from "./file-run-store.js";
7
7
  import { type OneTimeMigrationReport } from "./one-time-migrations.js";
8
+ import type { LocalSessionRows } from "./local-session-window.js";
8
9
  import type { SessionHeadReader } from "./caching-session-store.js";
9
10
  import { LocalCheckpointStore } from "./local-checkpoint-store.js";
10
11
  import { FileResumeAnchorStore } from "./file-resume-anchor-store.js";
@@ -408,6 +409,36 @@ export interface StoreBackend {
408
409
  * 编辑被拒)。云形缺 MinIO ⇒ fail-loud(启动了才是假安全);`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true` =
409
410
  * 显式接受 SQL 店 + per-blob 帽(单机/测试台形)。local 后端不经此门(file 店,无 SQL 包墙)。 */
410
411
  export declare function assertCloudSnapshotBlobPosture(kind: "mysql" | "pg" | "local", config: Pick<ServiceConfig, "snapshotBlobStore" | "snapshotBlobAllowSql">): void;
412
+ /**
413
+ * LOCAL backend (clay 2026-06-25 seamless local↔cloud; P0.5 variant-2 + variant-1 file-backed 2026-06-26): a DB-less StoreBackend
414
+ * so a locally-run HTTP service serves the SAME contract a cloud worker does (runs + trace + sessions + resume;
415
+ * long-term memory rides the [ref] memory ENGINE, not this backend). Sessions / per-session policy / file snapshots are
416
+ * FILE-BACKED via core's `FileStorageBackend` over `root`, so a session SURVIVES a process restart and the resume
417
+ * picker (GET /v1/sessions → LocalSessionStore.listSessions) is restart-visible — the 2c "first local, then sync"
418
+ * hard prerequisite.
419
+ *
420
+ * The session store stays the §0.5 owner-aware {@link LocalSessionStore} wrapper, but over a fresh `FileSessionRepo`
421
+ * (NOT `fileBackend.sessionStore`, which is core's bare `TtlSessionStore` lacking the §0.5 owner/list/fork/delete
422
+ * ops); policy/snapshot delegate to the fileBackend's own file stores. The fileBackend takes a coarse boot
423
+ * single-instance lock on `root/LOCK` (an `O_EXCL`+`linkSync` PIDFILE, not flock — [ref] F4; same-host only:
424
+ * liveness = `process.kill(pid,0)`, so a foreign PID namespace or NFS defeats it, see DEPLOY-PREREQS) —
425
+ * `close()` MUST `dispose()` it so a graceful restart can
426
+ * re-acquire (a stale lock from a dead PID is pruned by core, but graceful restart relies on the release).
427
+ *
428
+ * P0.5 variant-1 (clay 2026-06-26): `run()`/`resumeAnchor()` are now FILE-backed too (FileRunStore /
429
+ * FileResumeAnchorStore over core 1.138.0's crash-safe fs primitives — atomicWriteFile/writeThenLink/AppendLog), so the
430
+ * runs ledger + trace event log + E18 anchors SURVIVE a process restart, completing variant-2's promised follow-on. The
431
+ * picker still lists from the session abstraction (variant-2), but a restart now also re-reads run history + resume
432
+ * anchors. Session owners are persisted too (7.119.0: in the session-meta sidecar, next to the incarnation — see
433
+ * LocalSessionStore), so an owner survives a restart like the SQL twins' column does. The cloud-only stores
434
+ * (checkpoint/approval/toolResult/imageIndex/imageBake/breaker/costQuota/rateLimiter) are OMITTED → the consumers'
435
+ * `backend?.X?.()` gates degrade them exactly like a no-DB env.
436
+ */
437
+ /**
438
+ * The local session store's rows for the session view's window (`local-session-window.ts`): the local backend's own session store — its row
439
+ * projection and its log export — and `undefined` on any other backend. The one place that answers "is there a local store to read rows from".
440
+ */
441
+ export declare function localSessionRowsOf(backend: StoreBackend | undefined): LocalSessionRows | undefined;
411
442
  /**
412
443
  * 启动日志里「跨副本 or 进程内」那一格的标签。
413
444
  *
@@ -17,6 +17,7 @@ import { NO_SQL_MIGRATIONS, assertExecutionPlacementSwitchAdmissibleOnPool, back
17
17
  import { deployedExecutionLanes } from "../execution-lane-caps.js";
18
18
  import { assertRetiredSchemaAbsentOnPool } from "./schema-consistency.js";
19
19
  import { LocalSessionStore, createLocalSessionRepo } from "./local-session-store.js";
20
+ import { SESSION_DURABILITY, SESSION_PLACEMENTS } from "./session-placement.js";
20
21
  import { LocalCheckpointStore } from "./local-checkpoint-store.js";
21
22
  import { FileResumeAnchorStore } from "./file-resume-anchor-store.js";
22
23
  import { TiDBApprovalExemptionStore, PgApprovalExemptionStore, FileApprovalExemptionStore } from "./approval-exemption-store.js";
@@ -150,7 +151,7 @@ class TiDBBackend {
150
151
  workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
151
152
  parkDecisionsInst;
152
153
  sharedMemoryStore(authorizer) { return new TiDBSharedMemoryStore(this.pool, authorizer); }
153
- checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
154
+ checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep), claims: this.run() }); }
154
155
  checkpointReadView() { return this.checkpoint(); }
155
156
  toolResult() { return new TiDBToolResultStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
156
157
  imageIndex() { return new TiDBImageIndex(this.pool); }
@@ -204,7 +205,7 @@ class PgBackend {
204
205
  workflowParkDecisions() { return (this.parkDecisionsInst ??= new SqlWorkflowParkDecisionStore(this.run())); }
205
206
  parkDecisionsInst;
206
207
  sharedMemoryStore(authorizer) { return new PgSharedMemoryStore(this.pool, authorizer); }
207
- checkpoint(logger) { return new PgCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
208
+ checkpoint(logger) { return new PgCheckpointStore(this.pool, logger, { retention: sqlStoreRetention(this.config.retentionSweep), claims: this.run() }); }
208
209
  checkpointReadView() { return this.checkpoint(); }
209
210
  toolResult() { return new PgToolResultStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
210
211
  imageIndex() { return new PgImageIndex(this.pool); }
@@ -222,6 +223,9 @@ class PgBackend {
222
223
  pgPool() { return this.pool; }
223
224
  async dbNowMs() { const res = await this.pool.query("SELECT ROUND(EXTRACT(EPOCH FROM now())*1000) AS ms"); return Number(res.rows[0]?.ms); }
224
225
  }
226
+ export function localSessionRowsOf(backend) {
227
+ return backend instanceof LocalBackend ? backend.session() : undefined;
228
+ }
225
229
  class LocalBackend {
226
230
  kind = "local";
227
231
  fileBackend;
@@ -265,7 +269,11 @@ class LocalBackend {
265
269
  error: (msg, fields) => void (this.onError ?? this.onWarn)?.(msg, fields),
266
270
  strictHydrate: config.runStoreStrictHydrate,
267
271
  });
268
- this.sessionStore = new LocalSessionStore(createLocalSessionRepo(this.fileBackend.root), this.runStore, this.fileBackend.root, { log: createLogger(config.logLevel) });
272
+ this.sessionStore = new LocalSessionStore(createLocalSessionRepo(this.fileBackend.root), this.runStore, this.fileBackend.root, {
273
+ log: createLogger(config.logLevel),
274
+ durability: SESSION_DURABILITY,
275
+ placements: SESSION_PLACEMENTS,
276
+ });
269
277
  this.parkDecisionLedgerRoot = config.workflowRunStoreBackend === "memory" ? undefined : join(this.fileBackend.root, "workflows");
270
278
  this.resumeAnchorStore = new FileResumeAnchorStore(this.fileBackend.root);
271
279
  this.approvalExemptionStore = new FileApprovalExemptionStore(this.fileBackend.root, { warn: corruptReadWarn });
@@ -283,9 +291,12 @@ class LocalBackend {
283
291
  ? undefined
284
292
  : (plane) => fileSessionCaptureRecordStore(plane?.controlDir ?? ownControlDir);
285
293
  }
286
- async ensureSchema() { }
294
+ async ensureSchema() {
295
+ await this.sessionStore.settleArchiveSnapshots();
296
+ }
287
297
  async runOneTimeMigrations() {
288
298
  const holderDeathCodeRowsBackfilled = this.runStore.migrateLegacyHolderDeathCodes();
299
+ const archives = await this.sessionStore.migrateLocalArchives();
289
300
  const placements = await backfillLocalExecutionPlacements(this.runStore.sessionIdsWithRuns(), this.sessionStore.executionPlacementBackfill, { lanes: this.executionLanes });
290
301
  return {
291
302
  ...NO_SQL_MIGRATIONS,
@@ -293,6 +304,11 @@ class LocalBackend {
293
304
  executionPlacementsBackfilled: placements.backfilled,
294
305
  executionPlacementSessionsUnreadable: placements.unreadable,
295
306
  askExecutionLanesBackfilled: backfillLocalAskExecutionLanes(this.approvalAskStore, { lanes: this.executionLanes }),
307
+ localArchiveRowFactsBackfilled: archives.rowFactsBackfilled,
308
+ localArchivesAnonymized: archives.anonymized,
309
+ localArchivesKeptStamped: archives.keptStamped,
310
+ localArchivesUnreadable: archives.unreadable,
311
+ localArchiveSnapshotsRecovered: archives.snapshotsRecovered,
296
312
  };
297
313
  }
298
314
  async close() {
@@ -336,6 +352,7 @@ class LocalBackend {
336
352
  return (this.checkpointStoreInst ??= new LocalCheckpointStore(this.fileBackend.root, {
337
353
  getActiveTaskId: (sessionId) => this.runStore.getActiveTaskId(sessionId),
338
354
  readSessionOwner: async (sessionId) => (await this.sessionStore.headOf(sessionId))?.owner,
355
+ claims: this.runStore,
339
356
  }));
340
357
  }
341
358
  checkpoint() {
@@ -307,6 +307,13 @@ export type CreateRunOutcome = {
307
307
  activeTaskId?: undefined;
308
308
  sessionAbsent: true;
309
309
  };
310
+ /**
311
+ * 认领栅栏交给 `write` 的那一行 run 此刻的事实(SQL = 锁住那一行读的;进程内 = 同步段里读的)。认领与 `running` 只认 taskId,而续跑腿让同一个 taskId
312
+ * 停驻之后回到 `running`、认领仍在 —— 「接下这笔写的那条腿还是不是这条 run 的腿」要按腿问的写者拿它问(今天:副本 `instanceId`,续跑的 CAS 赢点把它改成接手的副本)。
313
+ */
314
+ export interface FencedRun {
315
+ readonly instanceId: string | null;
316
+ }
310
317
  /** SQL 认领栅栏的结局:`held: false` = 认领此刻不是这条 run 的(或它已不在跑),`write` 一次都没调。 */
311
318
  export type ClaimFenced<T> = {
312
319
  readonly held: true;
@@ -325,13 +332,13 @@ export type FenceLanded = typeof FENCE_LANDED;
325
332
  * **宿主会话的认领当写栅栏用 —— 进程内那一形**(属主 = run 店;Memory / File 两孪生同律)。认领 = 会话 → 持有它的 taskId,
326
333
  * 由 {@link CreateRunOutcome} 的建行(或续跑腿的 markResuming)取得、终局 / 收割放掉 —— 「这次受理还持有会话吗」只问它,不另记身份。
327
334
  *
328
- * `whileHoldingClaim(sessionId, taskId, write)`:**只有** `taskId` 此刻持有 `sessionId` 的认领、且这条 run 是活的(`running`)时才调 `write`、答 `true`,
335
+ * `whileHoldingClaim(sessionId, taskId, write)`:**只有** `taskId` 此刻持有 `sessionId` 的认领、且这条 run 是活的(`running`)时才调 `write`(交给它那一行的 {@link FencedRun})、答 `true`,
329
336
  * 同一拍把这条 run 的活性戳刷新(一次栅栏写也是一次心跳);否则一次都不调、答 `false`。整只是**同步**的 —— `write` 也是:判、刷新、`write` 落定是同一个同步段,
330
337
  * 中间没有让出,所以认领在 `write` 落定之前挪不动(这一条由类型担保 —— `write` 必须同步交回 {@link FENCE_LANDED},不靠「这里恰好没有 await」)。
331
338
  * SQL 孪生的栅栏是一个事务(`run-store-sql.ts` `SqlRunStore.whileHoldingClaim`,`write` 收那只事务连接,结局 {@link ClaimFenced}),不是本型。
332
339
  */
333
340
  export interface InProcessClaimFence {
334
- whileHoldingClaim(sessionId: string, taskId: string, write: () => FenceLanded): boolean;
341
+ whileHoldingClaim(sessionId: string, taskId: string, write: (run: FencedRun) => FenceLanded): boolean;
335
342
  }
336
343
  /**
337
344
  * `RunRecord["status"]`(= `"running" | TaskStatus`)的**唯一分类表** —— 词表的属主 = 判据的属主,
@@ -632,7 +632,8 @@ export const SCHEMA_STATEMENTS = [
632
632
  -- kind === "task_notification" vs everything-else. Do NOT "tidy" this into NOT NULL DEFAULT.
633
633
  kind VARCHAR(24) NULL,
634
634
  -- payload: bounded + redacted JSON extras of the entry's kind. task_notification: task_type / result / …;
635
- -- workflow_complete: at most {"completionId": …} (the engine's own correlation id, verbatim; NULL when the engine minted none).
635
+ -- workflow_complete: at most {"completionId": …, "diagnostics": …} (the engine's own correlation id, verbatim; the engine's diagnostic
636
+ -- teaching text, redacted + bounded at enqueue; each key absent when the engine minted none, NULL when neither).
636
637
  payload TEXT NULL,
637
638
  PRIMARY KEY (session_id, run_id),
638
639
  UNIQUE KEY uq_wfinbox_seq (seq),
@@ -28,13 +28,16 @@ export declare class TiDBSessionStore implements SessionStore {
28
28
  * `retention-store-sql.ts` 的 {@link MANAGED_RETENTION}。 */
29
29
  readonly retention: RetentionDeclaration;
30
30
  /** [ref] C18(core 5.48.0 [ref]):placement **声明**。六条义务的实装点逐条见
31
- * {@link SQL_SESSION_PLACEMENTS} 顶注与本类的 acquire / listSessions / release / listPlaced /
31
+ * {@link SESSION_PLACEMENTS} 顶注与本类的 acquire / listSessions / release / listPlaced /
32
32
  * placementOf 各处的 `[ref]` 注。 */
33
33
  readonly placements: {
34
34
  readonly subagent: {
35
35
  readonly durability: "durable";
36
36
  };
37
37
  };
38
+ /** core `SessionStore.durability`:未 placed 的会话活过进程重启(会话史落在 `session_meta` + `session_event` 两张真表,`acquire(id,{requireExisting:true})` 从事件日志重放)。
39
+ * 与 {@link placements} 的分区耐久是同一只常量({@link SESSION_DURABILITY}),Pg 孪生同格 —— 不声明的话 core 对缺席读 `process_local`,接线清单对真持久部署说谎。 */
40
+ readonly durability: "durable";
38
41
  /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
39
42
  private readonly pending;
40
43
  /** 委派子代孤行那一行 warn 的去处(`delegated-session-owner.ts`;装配给部署的 logger,缺席 ⇒ 缺省 logger,不静默)。 */
@@ -12,7 +12,7 @@ import { writeSessionRowCellsSql } from "./session-row-sql.js";
12
12
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
13
13
  import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRow } from "../delegated-session-owner.js";
14
14
  import { createLogger } from "../observability/logger.js";
15
- import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
15
+ import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "./session-placement.js";
16
16
  import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
17
17
  import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
18
18
  import { selectForkRows } from "@sema-agent/core";
@@ -25,7 +25,8 @@ const rowsOnConnection = (conn) => async (sql, values) => (await conn.query(sql,
25
25
  export class TiDBSessionStore {
26
26
  pool;
27
27
  retention;
28
- placements = SQL_SESSION_PLACEMENTS;
28
+ placements = SESSION_PLACEMENTS;
29
+ durability = SESSION_DURABILITY;
29
30
  pending = new Map();
30
31
  log;
31
32
  executionPlacement;
@@ -1,3 +1,20 @@
1
+ /**
2
+ * [ref] 件3(cli [ref] sema-bug4 (d) / server [ref]):**取消现场快照**的唯一铸点。
3
+ *
4
+ * 语义边界、缺席纪律、「为什么不写模型调用相位」全部成文在类型属主处 —— 见
5
+ * `plugins/store-contracts.ts` 的 `RunCancelContext` 顶注。本文件只做装配。
6
+ *
7
+ * 三条用户取消腿共用它(**判别材料只许有一份口径**):
8
+ * · `POST /v1/runs/:id/cancel` 的 park 取消腿(`http/routes/runs.ts` cancelSuspended);
9
+ * · `/v1/tasks/stream` 的客户端断连腿(`http/routes/tasks.ts`,没读到 done 的那一支);
10
+ * · durable bg 腿的 abort 结算(`runs.ts` —— 同一条 cancel 动词打到 running 行上时的终局形)。
11
+ *
12
+ * [ref] 起,「取消终局整只长什么样」也收在本文件({@link mintCancelledResult})—— 快照与它所在的
13
+ * 那条结果由同一个属主铸,三条腿一份口径。
14
+ *
15
+ * [ref] 起,「这次取消是谁发起的」也收在本文件:中止那一刻经 {@link createRunAbort} 的句柄登记成因,终局铸点从同一只句柄读。
16
+ */
17
+ import type { TerminalCause } from "@sema-agent/core";
1
18
  import type { AbortCause, PersistedTaskResult, RunCancelContext } from "./plugins/store-contracts.js";
2
19
  /**
3
20
  * @param taskId 这条 run 的账本 id(turn 活性登记的键)。
@@ -58,6 +75,23 @@ export declare function mintCancelledResult<R extends PersistedTaskResult>(args:
58
75
  export declare function mintCancelledResult(args: CancelMintArgs & {
59
76
  engineResult?: undefined;
60
77
  }): PersistedTaskResult;
78
+ /**
79
+ * 被取消的续跑腿、且这张卡被本服务 expire 之后,终局上的那句话 —— **全仓单源**([ref])。
80
+ *
81
+ * 引擎对一条「批过的那一步没开跑就结束」的续跑腿,在终局文案上补一句通用的话:「…the checkpoint was REOPENED (pending): the same approval can be resumed again」
82
+ * (它对想再续的宿主是真话:引擎确实把卡放回了 pending)。而本服务按取消语义结算这张卡(取消 = run 死:绝不重新 park,卡 expire),于是写在终局上的那句话与可操作面
83
+ * 不一致 —— 文案说可再续,待办队列里没有这张卡、同一张卡再决断 404。取消不可逆(契约),所以真话是这一句。
84
+ * 引擎那句里「放回 pending」的事实与「同一批准可再续」的结论都是**引擎当时**的,本服务在其后改了卡的状态,最后一个写手的句子才对。
85
+ */
86
+ export declare const CARD_SETTLED_BY_CANCEL_MESSAGE = "Cancelled \u2014 the approved action never started, and the cancel settled this approval: it cannot be resumed (start a new run to continue)";
87
+ /**
88
+ * 一条被取消的续跑腿的失败终局,在本服务亲手 expire 了它那张卡之后的形:**只换文案**,码(引擎补的 `resume.reopened_unstarted` 等)、成因位、统计与其余字段原样
89
+ * (码立住是成文契约;换码是另一条 wire 变更)。非 `failed` 终局原样返回。纯函数,不改入参。
90
+ * 调用点只有 `http/resume-legs.ts` 的 `reopenedAfterLeg`(判据 = 店的事实「本座 expire 了一张 pending 的卡」,不嗅引擎文案)。
91
+ */
92
+ export declare function withCardSettledByCancelMessage<R extends {
93
+ terminal: TerminalCause;
94
+ }>(leg: R): R;
61
95
  /** {@link mintCancelledResult} 的公共实参(`engineResult` 另列:在场与否决定返回型)。 */
62
96
  interface CancelMintArgs {
63
97
  /** 这条 run 的账本 id(活性登记的键;账本不在场时给空串,快照按「证不出」处理)。 */
@@ -36,6 +36,13 @@ export function mintCancelledResult(args) {
36
36
  abortCause: args.cause,
37
37
  };
38
38
  }
39
+ export const CARD_SETTLED_BY_CANCEL_MESSAGE = "Cancelled — the approved action never started, and the cancel settled this approval: it cannot be resumed (start a new run to continue)";
40
+ export function withCardSettledByCancelMessage(leg) {
41
+ const t = leg.terminal;
42
+ if (t.kind !== "failed")
43
+ return leg;
44
+ return { ...leg, terminal: { ...t, message: CARD_SETTLED_BY_CANCEL_MESSAGE } };
45
+ }
39
46
  export function buildCancelledLedgerRow(c) {
40
47
  const t = c.terminal;
41
48
  return {
package/dist/runs.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { defaultTaskRegistry, type CascadeConfig, type CheckpointToken, type RunInternals, type Runner, type SubagentSteerHandle, type TaskResult, type TaskSpec, type TaskStream, type TerminalCause } from "@sema-agent/core";
2
+ import { type CardVoidPort } from "./card-void.js";
2
3
  import { type EntryProblem } from "./entry-problems.js";
3
4
  import type { SendUserFileEmitter } from "./capabilities/send-user-file-tool.js";
4
5
  import type { RunStore } from "./plugins/store-backend.js";
@@ -181,9 +182,24 @@ export declare function releaseKeyIfZeroWorkRefusal(runStore: {
181
182
  export declare function createPromptCommitReader(): (ev: {
182
183
  readonly type: string;
183
184
  }) => string | undefined;
185
+ /** 回退锚写在哪条腿上(探针行的 `leg=`):流式腿 `POST /v1/tasks/stream`、后台腿 `POST /v1/runs`、续跑腿。 */
186
+ export type ResumeAnchorLeg = "stream" | "bg" | "resume";
187
+ /**
188
+ * 回退锚的**唯一**写法(三条腿同一只):**有界** await 写入(单行店操作期限 {@link BASELINE_IO_DEADLINE_MS},与项目指令基线的
189
+ * 读写同一只数、同一只 {@link withinIoDeadline} —— 同为单行 PK 写、同在腿的关键路径上);写失败或到期 ⇒ 不抛(锚是这次提示词能不能
190
+ * 回退的手柄,不是 run 的成败),记一次登记过的 fail-open `server.resume-anchor.capture-failed`(`reason=error|timeout`;计数 + 探针行 + 一次性 warn)。
191
+ * 调用方在本腿的终帧(流式腿的 `done`)**之前** await 它 ⇒ 客户端拿到终帧时锚已落定或已知没落定,没有「锚晚到」的窗;锚店挂住至多让终帧晚一个期限。
192
+ * 到期只放手不取消:那次写照常在飞,迟到落定的锚照样可用(迟到的失败被吞,不重复计数)。
193
+ * 用户面的读数仍只有一处:会话视图用户消息行的 `taskId?`(present-iff 锚在)与 `resumeAt` 的 404 `resume_at.unknown_event`。
194
+ */
195
+ export declare function settleResumeAnchor(write: () => Promise<void>, where: {
196
+ readonly leg: ResumeAnchorLeg;
197
+ readonly kind: "prompt" | "turn";
198
+ }): Promise<void>;
184
199
  export declare class TurnAnchorCapture {
185
200
  private readonly capture;
186
- private readonly onCaptureFail?;
201
+ /** 这只捕获器在哪条腿上(写失败的 fail-open 探针行带它)。 */
202
+ private readonly leg;
187
203
  /** R8 (CC-parity rewind): capture a USER-message anchor straight from the
188
204
  * `message_committed{role:"user"}` event — its `entryId` is the persisted user-message `SessionTreeEntry.id`,
189
205
  * ALWAYS a valid `resumeAt` target (core accepts a user message; it rejects a tool-result / mid-tool-call
@@ -197,7 +213,9 @@ export declare class TurnAnchorCapture {
197
213
  private turnHadTool;
198
214
  /** 用户提交是不是提问的唯一读法({@link createPromptCommitReader};附件帧不是提问)。 */
199
215
  private readonly promptCommit;
200
- constructor(capture: ((eventId: string) => Promise<void>) | undefined, onCaptureFail?: (() => void) | undefined,
216
+ constructor(capture: ((eventId: string) => Promise<void>) | undefined,
217
+ /** 这只捕获器在哪条腿上(写失败的 fail-open 探针行带它)。 */
218
+ leg: Exclude<ResumeAnchorLeg, "stream">,
201
219
  /** R8 (CC-parity rewind): capture a USER-message anchor straight from the
202
220
  * `message_committed{role:"user"}` event — its `entryId` is the persisted user-message `SessionTreeEntry.id`,
203
221
  * ALWAYS a valid `resumeAt` target (core accepts a user message; it rejects a tool-result / mid-tool-call
@@ -247,6 +265,20 @@ export declare function parkToolCallId(result: unknown, cs: ParkCheckpointReader
247
265
  * 不塞进 `checkpointGate`——那是 core 的类型,server 不改其形)。非 park / 无 token / tool-less /
248
266
  * 读失败 ⇒ **同一引用**原样返回(键缺席)。graft 不负责 strip——组合序恒为 graft → strip。 */
249
267
  export declare function graftParkToolCallId<T>(result: T, cs: ParkCheckpointReader | undefined): Promise<T>;
268
+ /**
269
+ * 🔴 [ref] **停驻没落定就以失败收场的腿:卡的命运先于认领释放**(后台腿与续跑腿同读这一只;动词 = `card-void.ts` 的结算作废)。
270
+ *
271
+ * 腿要以失败收场(行 `failed`、认领释放),而手里的结果是宿主层停驻 —— 引擎交回结果之前已把卡落进 checkpoint 店,卡在本 run 的会话上 ——
272
+ * ⇒ 先把那张卡按它自己的 scope 作废,调用方再放认领。不作废 ⇒ 待决面上留一张卡,兑现它的 run 已经 `failed`、认领已放:决定口照样受理,
273
+ * 续跑在没有认领的会话上驱动(`resume-legs.ts` 认领缺席的成因清单)。顺序承重:先放认领、后作废 ⇒ 两步之间(作废没做成时一直)就是那一形。
274
+ * 停驻没落定的成因不在判据里(停驻行 / 用量行 / 结果到手之后的任何一笔账本写抛):判据只读手里那份结果。
275
+ *
276
+ * `held` = 手里那份结果过了宿主判官之后的形(判官提上来的嵌套停驻同样算;不在本 run 会话上的嵌套停驻 —— cascade 级上的卡 —— 判官不提,
277
+ * 不归本腿作废)。不是停驻 ⇒ 零 I/O。不抛:作废没做成 ⇒ 卡回到「留在待决面上、由兜底收割收走」的形,响亮留痕。
278
+ */
279
+ export declare function voidHeldParkCard(held: {
280
+ readonly terminal: TerminalCause;
281
+ } | undefined, hostSessionId: string | undefined, cards: CardVoidPort | undefined, leg: "bg" | "resume"): Promise<void>;
250
282
  /**
251
283
  * Drive a task to completion in the background, persisting its events to the durable run log (S2).
252
284
  *
@@ -339,16 +371,11 @@ approval?: {
339
371
  declaredFamilies: ReadonlySet<ApprovalFrameFamily>; /** 出处带车道:这条执行上下文的车道(装配点 `durable-run-leg.ts` 写 `ctx.lane`)。 */
340
372
  executionLane: import("./execution-lane-caps.js").ExecutionLane;
341
373
  },
342
- /** [ref]:park 投影腿的 checkpoint 读口(`deps.checkpointStore`)。在场时,durable park 的
343
- * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});缺席=旧字节逐字不变。 */
344
- parkCheckpointReader?: {
345
- get(token: CheckpointToken): Promise<{
346
- pendingAction?: {
347
- kind?: unknown;
348
- toolCallId?: unknown;
349
- };
350
- } | null>;
351
- },
374
+ /** 本腿结算用的 checkpoint 店(`deps.checkpointStore`),两件事:①[ref] park 投影腿的读口 —— 在场时 durable park 的
375
+ * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});②验证门结算时作废验证器自己的停驻卡
376
+ * ({@link voidVerifierPauseCard}:本服务兑现不了的卡不留在待决面上);③停驻没落定就以失败收场时作废手里那张卡({@link voidHeldParkCard})。
377
+ * 缺席 ⇒ ①旧字节逐字不变;②③带凭据的停驻响亮留痕、卡由兜底收割。 */
378
+ checkpointCards?: ParkCheckpointReader & CardVoidPort,
352
379
  /** [ref]:装配期(`prepareSpec` → `resolveSpec`)铸下的用户面通告 —— 那一刻本腿的通告口还没开,
353
380
  * 所以由本腿在 `registerEngineNoticeLeg` 那一拍连同 `pending` 一起投(投递单点仍是 `route()`)。
354
381
  * 缺席 ⇒ 一个字节不投(既有调用方逐字不变)。 */