@sema-agent/server 7.149.0 → 7.150.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 (194) hide show
  1. package/MIGRATION.md +122 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +39 -15
  5. package/dist/approval-card.d.ts +1 -1
  6. package/dist/approval-gate-currency.d.ts +1 -1
  7. package/dist/audit.d.ts +1 -1
  8. package/dist/audit.js +2 -2
  9. package/dist/background-session-reap.d.ts +1 -1
  10. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  11. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  12. package/dist/boot/parked-revive-gate.d.ts +5 -5
  13. package/dist/boot/parked-revive-gate.js +2 -3
  14. package/dist/boot/resolve-spec.d.ts +4 -5
  15. package/dist/boot/resolve-spec.js +61 -25
  16. package/dist/boot/retention-lane.d.ts +1 -1
  17. package/dist/boot/session-faces.js +21 -21
  18. package/dist/boot/stage-03-store-backend.js +2 -2
  19. package/dist/boot/stage-06-runners.js +1 -2
  20. package/dist/boot/stage-07-capability-layer.js +1 -1
  21. package/dist/boot/stage-10-http-server.d.ts +33 -1
  22. package/dist/boot/stage-10-http-server.js +9 -4
  23. package/dist/boot/web-admin.d.ts +1 -1
  24. package/dist/capability-facts.d.ts +65 -56
  25. package/dist/capability-facts.js +42 -35
  26. package/dist/config-catalog.js +1 -1
  27. package/dist/config-types.d.ts +12 -11
  28. package/dist/deployment-governance.d.ts +7 -28
  29. package/dist/deployment-governance.js +1 -14
  30. package/dist/device-ws-hub.d.ts +1 -0
  31. package/dist/device-ws-hub.js +64 -28
  32. package/dist/device-ws-protocol.d.ts +20 -6
  33. package/dist/device-ws-protocol.js +9 -2
  34. package/dist/engine-identity.d.ts +7 -1
  35. package/dist/engine-identity.js +5 -0
  36. package/dist/field-conflict.d.ts +2 -2
  37. package/dist/fleet/engine-signal.d.ts +14 -1
  38. package/dist/fleet/engine-signal.js +5 -1
  39. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  40. package/dist/fleet/serving-lease.d.ts +18 -3
  41. package/dist/fleet/serving-lease.js +81 -35
  42. package/dist/fleet-client.d.ts +13 -11
  43. package/dist/fleet-client.js +53 -33
  44. package/dist/history-anchor.js +1 -1
  45. package/dist/hitl-delivery.d.ts +1 -3
  46. package/dist/hitl-delivery.js +1 -1
  47. package/dist/http/active-run-conflict.d.ts +3 -7
  48. package/dist/http/active-run-conflict.js +2 -2
  49. package/dist/http/admission.js +2 -2
  50. package/dist/http/approval-sweeps.js +2 -1
  51. package/dist/http/caller-trust.d.ts +2 -2
  52. package/dist/http/caller-trust.js +3 -3
  53. package/dist/http/checkpoint-ctx.d.ts +18 -63
  54. package/dist/http/checkpoint-ctx.js +1 -24
  55. package/dist/http/claim-heartbeat.d.ts +4 -4
  56. package/dist/http/claim-heartbeat.js +3 -3
  57. package/dist/http/dispatch.js +2 -2
  58. package/dist/http/durable-run-leg.js +4 -4
  59. package/dist/http/owner-access.d.ts +243 -19
  60. package/dist/http/owner-access.js +184 -5
  61. package/dist/http/readiness.d.ts +4 -2
  62. package/dist/http/resume-ctx-read.js +16 -1
  63. package/dist/http/resume-legs.js +18 -14
  64. package/dist/http/route-ctx.d.ts +23 -11
  65. package/dist/http/routes/a2a-serve.js +15 -14
  66. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  67. package/dist/http/routes/approvals-assistant.js +8 -9
  68. package/dist/http/routes/capabilities.js +4 -3
  69. package/dist/http/routes/config-catalog.d.ts +1 -1
  70. package/dist/http/routes/mcp-reconnect.js +2 -2
  71. package/dist/http/routes/memory-origin.d.ts +1 -1
  72. package/dist/http/routes/notify-wake.js +2 -3
  73. package/dist/http/routes/projects.d.ts +1 -0
  74. package/dist/http/routes/projects.js +5 -3
  75. package/dist/http/routes/retention-ops.d.ts +1 -1
  76. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  77. package/dist/http/routes/run-permission-mode.js +2 -2
  78. package/dist/http/routes/runs.d.ts +1 -1
  79. package/dist/http/routes/runs.js +23 -30
  80. package/dist/http/routes/session-memory-sync.js +4 -5
  81. package/dist/http/routes/session-sync.js +3 -7
  82. package/dist/http/routes/sessions.d.ts +1 -1
  83. package/dist/http/routes/sessions.js +18 -44
  84. package/dist/http/routes/tasks.js +9 -8
  85. package/dist/http/routes/trace-usage.js +14 -14
  86. package/dist/http/routes/workflows.d.ts +1 -0
  87. package/dist/http/routes/workflows.js +20 -38
  88. package/dist/http/server.d.ts +8 -4
  89. package/dist/http/server.js +5 -1
  90. package/dist/http/stored-body-replay.d.ts +3 -24
  91. package/dist/http/stored-body-replay.js +2 -12
  92. package/dist/http/submit-ctx.d.ts +1 -2
  93. package/dist/http/submit-ctx.js +3 -2
  94. package/dist/http/submit-replay.d.ts +15 -2
  95. package/dist/http/submit-replay.js +10 -4
  96. package/dist/http/wire-types.d.ts +6 -3
  97. package/dist/leader/endpoint.js +9 -8
  98. package/dist/leg-facts.d.ts +8 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/mcp-authorization-face.d.ts +18 -14
  101. package/dist/mcp-authorization-face.js +3 -2
  102. package/dist/memory-store-refusal.d.ts +1 -1
  103. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  104. package/dist/observability/fail-open.d.ts +5 -5
  105. package/dist/observability/fail-open.js +5 -5
  106. package/dist/observability/logger.d.ts +7 -0
  107. package/dist/observability/logger.js +3 -1
  108. package/dist/observability/run-terminal.d.ts +1 -1
  109. package/dist/owner-faces.d.ts +27 -3
  110. package/dist/owner-faces.js +2 -0
  111. package/dist/peer-seat.d.ts +1 -1
  112. package/dist/peer-seat.js +52 -59
  113. package/dist/plugins/exec-path-law.d.ts +34 -0
  114. package/dist/plugins/exec-path-law.js +13 -0
  115. package/dist/plugins/file-run-store.js +1 -1
  116. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  117. package/dist/plugins/local-checkpoint-store.js +3 -0
  118. package/dist/plugins/memory-run-store.js +1 -1
  119. package/dist/plugins/one-time-migrations.d.ts +74 -49
  120. package/dist/plugins/one-time-migrations.js +134 -69
  121. package/dist/plugins/pg-pool.js +4 -0
  122. package/dist/plugins/posix-shell-fs.js +4 -2
  123. package/dist/plugins/remote-env-adb.js +3 -2
  124. package/dist/plugins/remote-env-device.js +8 -6
  125. package/dist/plugins/remote-env-e2b.d.ts +3 -1
  126. package/dist/plugins/remote-env-e2b.js +11 -8
  127. package/dist/plugins/remote-env-host.d.ts +1 -1
  128. package/dist/plugins/remote-env-host.js +20 -11
  129. package/dist/plugins/remote-env-k8s.js +3 -2
  130. package/dist/plugins/remote-env-local-docker.js +3 -2
  131. package/dist/plugins/remote-env-ssh.js +4 -3
  132. package/dist/plugins/remote-shell.d.ts +3 -1
  133. package/dist/plugins/remote-shell.js +3 -3
  134. package/dist/plugins/replica-registration.d.ts +35 -0
  135. package/dist/plugins/replica-registration.js +48 -0
  136. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  137. package/dist/plugins/run-store-sql.d.ts +1 -1
  138. package/dist/plugins/run-store-sql.js +2 -2
  139. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  140. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  141. package/dist/plugins/schema-consistency.d.ts +7 -5
  142. package/dist/plugins/schema-consistency.js +29 -16
  143. package/dist/plugins/schema-drift-sentence.js +1 -1
  144. package/dist/plugins/serving-requirement.d.ts +49 -0
  145. package/dist/plugins/serving-requirement.js +53 -0
  146. package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
  147. package/dist/plugins/stop-then-start-sentence.js +44 -0
  148. package/dist/plugins/store-backend.d.ts +1 -1
  149. package/dist/plugins/store-contracts.d.ts +12 -2
  150. package/dist/plugins/store-contracts.js +3 -0
  151. package/dist/plugins/tidb-pool.js +4 -0
  152. package/dist/process-instance.d.ts +3 -0
  153. package/dist/process-instance.js +7 -0
  154. package/dist/project-context.d.ts +66 -0
  155. package/dist/project-context.js +70 -0
  156. package/dist/question.d.ts +9 -21
  157. package/dist/question.js +28 -19
  158. package/dist/run-cancel-card.d.ts +1 -1
  159. package/dist/run-cancel-card.js +2 -1
  160. package/dist/security.d.ts +1 -1
  161. package/dist/session-agent.d.ts +1 -10
  162. package/dist/session-agent.js +0 -6
  163. package/dist/session-incarnation.d.ts +18 -4
  164. package/dist/session-incarnation.js +4 -1
  165. package/dist/session-notify-door.d.ts +4 -4
  166. package/dist/session-notify-door.js +5 -6
  167. package/dist/session-rules-seat.d.ts +4 -3
  168. package/dist/session-rules-seat.js +3 -11
  169. package/dist/session-sync.js +3 -3
  170. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  171. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  172. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  173. package/dist/task-cwd.d.ts +4 -3
  174. package/dist/task-settings.d.ts +13 -0
  175. package/dist/task-settings.js +13 -7
  176. package/dist/tool-approval.d.ts +7 -6
  177. package/dist/tool-approval.js +41 -37
  178. package/dist/trace/engine-notice-wire.d.ts +14 -0
  179. package/dist/trace/engine-notice-wire.js +18 -1
  180. package/dist/trace/project.d.ts +4 -3
  181. package/dist/trace/project.js +3 -2
  182. package/dist/trace/projection-table.d.ts +33 -2
  183. package/dist/trace/self-minted-wire.d.ts +150 -17
  184. package/dist/trace/self-minted-wire.js +54 -16
  185. package/dist/trace/tool-card.d.ts +3 -0
  186. package/dist/trace/tool-card.js +24 -0
  187. package/dist/trace/tool-output-media.d.ts +2 -1
  188. package/dist/unattended-permission-mode.d.ts +1 -1
  189. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  190. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  191. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  192. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.150.0.json +335 -0
@@ -129,14 +129,28 @@ export declare function sessionFaceOfHead(sessionId: string, head: SessionHead):
129
129
  export declare function sessionFaceOfHead(sessionId: string, head: SessionHead | undefined): SessionFace | undefined;
130
130
  /**
131
131
  * 「这只会话店有没有按 id 读(`headOf`)」—— 全仓**只判这一处**:授权口、各路由的属主门、能力位、起服门、k8s 工作区空间的每 run 读与
132
- * 一次性迁移 M23(窗内)的回填读的都是这一只返回值。缺席 = 店没有实装 `headOf`(读不出属主 / 住客 / 叶子,调用方各按自己的 fail-closed 臂处置)。
132
+ * 一次性迁移 M23(窗内)的回填读的都是这一只判({@link headReadOf} 的在不在 = 它)。缺席 = 店没有实装 `headOf`(读不出属主 / 住客 / 叶子,调用方各按自己的 fail-closed 臂处置)。
133
+ * 收的是任意店形(路由层的封住视图也行:在不在照判,读口在型上调不了 —— 带属主的行只经属主存取器读,`http/owner-access.ts`)。
133
134
  */
134
- export declare function headReadOf(store: object | undefined): HeadRead | undefined;
135
+ export declare function headReadPresent(store: {
136
+ readonly headOf?: unknown;
137
+ } | undefined): boolean;
138
+ /** 会话店的按 id 读(绑好宿主的那一只;店没有 ⇒ `undefined`)。路由层的封住视图在这里读不出读口(封住的那一格型是 `undefined`,类型墙,`route-ctx.ts` 的 `RouteDeps`)。 */
139
+ export declare function headReadOf(store: {
140
+ readonly headOf?: HeadRead;
141
+ } | undefined): HeadRead | undefined;
135
142
  /**
136
143
  * 删除门(`DELETE /v1/sessions/:id` 的属主门 + 协调器的化身围栏)的头读 —— **只判这一处**:店交了 `deletionHeadOf`(删除已开始、还没收完的那一行也答得出属主与住客;local)⇒ 它;
137
- * 否则 ⇒ {@link headReadOf}(删除是一个事务的店,没有删到一半的行)。缺席 = 店没有按 id 读。
144
+ * 否则 ⇒ {@link headReadOf}(删除是一个事务的店,没有删到一半的行)。缺席 = 店没有按 id 读。同 {@link headReadOf}:只收值上交读口的店形。
138
145
  */
139
- export declare function deletionHeadReadOf(store: object | undefined): HeadRead | undefined;
146
+ export declare function deletionHeadReadOf(store: {
147
+ readonly deletionHeadOf?: HeadRead;
148
+ readonly headOf: HeadRead;
149
+ }): HeadRead;
150
+ export declare function deletionHeadReadOf(store: {
151
+ readonly deletionHeadOf?: HeadRead;
152
+ readonly headOf?: HeadRead;
153
+ } | undefined): HeadRead | undefined;
140
154
  /** 换装沿用的行格(化身 + fork 键):{@link carriedRowCells} 的答案。`forkKey: null` = 没有可沿用的键(新建行 / 行上本来没有)。 */
141
155
  export interface CarriedRowCells {
142
156
  readonly incarnation: string;
@@ -66,9 +66,12 @@ export function leafOfAdmittedHead(head) {
66
66
  export function sessionFaceOfHead(sessionId, head) {
67
67
  return head === undefined ? undefined : sessionFaceFromRow(sessionId, head.owner, head.incarnation);
68
68
  }
69
+ export function headReadPresent(store) {
70
+ return typeof store?.headOf === "function";
71
+ }
69
72
  export function headReadOf(store) {
70
73
  const read = store?.headOf;
71
- return typeof read === "function" ? read.bind(store) : undefined;
74
+ return headReadPresent(store) ? read.bind(store) : undefined;
72
75
  }
73
76
  export function deletionHeadReadOf(store) {
74
77
  const read = store?.deletionHeadOf;
@@ -9,7 +9,7 @@
9
9
  * 别的抛 = 活流刚收尾的竞态 ⇒ 落空闲臂。
10
10
  * · **空闲**:这只会话的完成收件箱(`externalNotificationInboxEntry`,external 键域;脱敏 / 限长在入箱口内)⇒ 下次开流排空成 `task_notification` 帧。
11
11
  * 部署没有收件箱 ⇒ 如实交回 `no_inbox`(调用方各自答:HTTP 门 501,引擎完成告警)。
12
- * · **复核住客**:两臂的投递动作(注入 / 入箱)之前**紧邻**一次头读,与目标的住客比 —— 判官是仓里那一只 `sameOccupant`(`execution-placement.ts`:属主 ∧ 化身,
12
+ * · **复核住客**:两臂的投递动作(注入 / 入箱)之前**紧邻**一次头读,与目标的住客比(读与判在属主存取器的 `occupantRecheckOf`)—— 判官是仓里那一只 `sameOccupant`(`execution-placement.ts`:属主 ∧ 化身,
13
13
  * 写口与执行上下文解析器判的也是它):不在 ⇒ `withdrawn: gone`;两样里任一样变了 ⇒ `withdrawn: replaced`。会话店不铸化身(嵌入方自建的店)时两侧都没有令牌,
14
14
  * 判官只剩属主那一半:属主没变 ⇒ 投,变了 ⇒ 撤回。这一读是动作之前的最后一次让出:判活读(及活臂刚收尾时的那次注入)都在它前面 ——
15
15
  * 令牌永不复用,此刻还是那一位 ⇒ 判活读那一刻也是那一位 ⇒ 判出的 run 是它的 run。
@@ -33,7 +33,7 @@
33
33
  import type { ExternalNotificationInput, SessionHead, SubagentToolOptions, TaskStream } from "@sema-agent/core";
34
34
  import { type WorkflowCompletionInbox } from "./orchestration/workflow-completion-inbox.js";
35
35
  import type { DelegationRootWitness } from "./delegated-session-owner.js";
36
- import type { HeadRead } from "./session-incarnation.js";
36
+ import type { OwnerAwareSessionStore } from "./security.js";
37
37
  import type { Logger } from "./observability/logger.js";
38
38
  /** 门的一次投递结局(闭集;调用方 `switch` 穷尽)。 */
39
39
  export type SessionNotifyOutcome = {
@@ -74,8 +74,8 @@ export interface SessionNotifyDoorIo {
74
74
  /** 本副本持活 core 流的腿(taskId → 流)。 */
75
75
  readonly liveRuns: ReadonlyMap<string, Pick<TaskStream, "notify">>;
76
76
  readonly inbox: WorkflowCompletionInbox | undefined;
77
- /** 会话店的按 id 头读(复核住客的那一读)。店没有 ⇒ `undefined`:调用方先判(路由 501;后台完成 relay 照交,门响亮抛 ⇒ relay 告警)。 */
78
- readonly headOf: HeadRead | undefined;
77
+ /** 会话店(复核住客的那一读只经属主存取器)。店没有按 id 头读 ⇒ 调用方先判(路由 501;后台完成 relay 照交,门响亮抛 ⇒ relay 告警)。 */
78
+ readonly sessionStorage: Pick<OwnerAwareSessionStore, "headOf"> | undefined;
79
79
  }
80
80
  export declare function createSessionNotifyDoor(io: SessionNotifyDoorIo): SessionNotifyDoor;
81
81
  /** 委派工具挂载期的完成 sink 的型(取自 core 的选项型,不手抄)。 */
@@ -1,14 +1,13 @@
1
1
  import { externalNotificationInboxEntry } from "./orchestration/workflow-completion-inbox.js";
2
- import { sameOccupant } from "./execution-placement.js";
2
+ import { occupantRecheckOf } from "./http/owner-access.js";
3
3
  import { recordFailOpen } from "./observability/fail-open.js";
4
4
  export function createSessionNotifyDoor(io) {
5
+ const occupantNow = occupantRecheckOf(io.sessionStorage);
5
6
  async function recheck(target) {
6
- if (io.headOf === undefined)
7
+ if (occupantNow === undefined)
7
8
  throw new Error("session notify door: the session store has no head read (headOf); the occupant cannot be re-checked before delivery");
8
- const head = await io.headOf(target.sessionId);
9
- if (head === undefined)
10
- return { delivery: "withdrawn", reason: "gone" };
11
- return sameOccupant(target.occupant, head) ? undefined : { delivery: "withdrawn", reason: "replaced" };
9
+ const verdict = await occupantNow(target.sessionId, target.occupant);
10
+ return verdict === "same" ? undefined : { delivery: "withdrawn", reason: verdict };
12
11
  }
13
12
  return {
14
13
  async deliver(target, event) {
@@ -4,9 +4,10 @@
4
4
  * (引擎交界的身份格,`engine-identity.ts`):带场景的会话,人 ≠ 属主,原样查 = 落到另一只桶(运维在 health 会话上 PUT 的 deny 对引擎不存在 = 安全轴放宽)。
5
5
  *
6
6
  * 律(一句):引擎交来的人恰是这只会话属主背后的人 ⇒ 键换成会话属主;否则原样(与今天逐字节同 —— 没带场景的会话属主与人字节相同,两种读法同一个键)。
7
- * 属主只经会话店的头读一处(`headOf(id)?.owner`,会话行是属主的唯一事实);读不到头(会话不在 / 店不交头读)⇒ 原样。读写两腿与祖先会话的规则读同经这一只席。
7
+ * 属主只经会话店的头读一处(会话行是属主的唯一事实;读与判在属主存取器 `http/owner-access.ts` 的 `engineRulesKeyOf`);读不到头(会话不在 / 店不交头读)⇒ 原样。
8
+ * 读写两腿与祖先会话的规则读同经这一只席。
8
9
  */
9
10
  import type { SessionPolicyStore } from "@sema-agent/core";
10
- import type { HeadRead } from "./session-incarnation.js";
11
- export declare function createEngineSessionRulesSeat(store: SessionPolicyStore, headOf: HeadRead | undefined): SessionPolicyStore;
11
+ import type { OwnerAwareSessionStore } from "./security.js";
12
+ export declare function createEngineSessionRulesSeat(store: SessionPolicyStore, sessions: Pick<OwnerAwareSessionStore, "headOf"> | undefined): SessionPolicyStore;
12
13
  //# sourceMappingURL=session-rules-seat.d.ts.map
@@ -1,14 +1,6 @@
1
- import { personOf } from "./owner-brands.js";
2
- import { recoveredSubject } from "./owner-faces.js";
3
- export function createEngineSessionRulesSeat(store, headOf) {
4
- const keyFor = async (sessionId, principal) => {
5
- if (principal === undefined || headOf === undefined)
6
- return principal;
7
- const owner = (await headOf(sessionId))?.owner;
8
- if (owner === undefined || owner === null)
9
- return principal;
10
- return personOf(recoveredSubject(owner)) === principal ? owner : principal;
11
- };
1
+ import { engineRulesKeyOf } from "./http/owner-access.js";
2
+ export function createEngineSessionRulesSeat(store, sessions) {
3
+ const keyFor = (sessionId, principal) => engineRulesKeyOf(sessions, sessionId, principal);
12
4
  return {
13
5
  getRules: async (sessionId, principal) => store.getRules(sessionId, await keyFor(sessionId, principal)),
14
6
  putRules: async (sessionId, principal, rules, opts) => store.putRules(sessionId, await keyFor(sessionId, principal), rules, opts),
@@ -1,7 +1,7 @@
1
1
  import { anchorEntryDeclared, classifySyncRelationship } from "./session-sync-kernel.js";
2
2
  import { recordFailOpen } from "./observability/fail-open.js";
3
3
  import { loosenReasons, normalizeRules } from "@sema-agent/core";
4
- import { headReadOf } from "./session-incarnation.js";
4
+ import { sessionFactsReadOf } from "./http/owner-access.js";
5
5
  import { fileHistoryBlobExchangeOf, judgeSessionRulesWrite } from "@sema-agent/core";
6
6
  export class FileHistoryUnreadableError extends Error {
7
7
  sessionId;
@@ -75,8 +75,8 @@ export async function exportSessionManifest(sessionId, srcBackend, historyStore)
75
75
  const entryIds = await listEntryIds(sessionId);
76
76
  if (entryIds === null)
77
77
  return null;
78
- const headOf = headReadOf(session);
79
- const leafId = headOf ? ((await headOf(sessionId))?.leafId ?? null) : null;
78
+ const sessionFactsOf = sessionFactsReadOf(session);
79
+ const leafId = sessionFactsOf ? ((await sessionFactsOf(sessionId))?.leafId ?? null) : null;
80
80
  const fs = historyStore;
81
81
  const fileHistory = await exportedFileHistory(fs, sessionId);
82
82
  const policyStore = srcBackend.sessionPolicy();
@@ -1,6 +1,6 @@
1
1
  import type { Pool as MySqlPool } from "mysql2/promise";
2
2
  import type { Pool as PgPool } from "pg";
3
- import { type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
3
+ import { type CheckpointPage, type CheckpointRowRead, type CheckpointSessionCursor, type CheckpointSessionQuery, type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
4
4
  import { type SqlDialect, type SqlDriver } from "../plugins/sql-driver.js";
5
5
  import type { CtxWriteClaim } from "../plugins/store-contracts.js";
6
6
  import type { RetentionDeclaration } from "@sema-agent/core";
@@ -72,7 +72,6 @@ interface SteerColumnRead {
72
72
  * 列有内容却读不出 ⇒ `lossy`(两个调用点对它的处置见 {@link SteerColumnRead})。
73
73
  */
74
74
  export declare function readPendingSteerQueueColumn(queueRaw: unknown): SteerColumnRead;
75
- /** SQL checkpoint 店的装配选项:托管留存声明。(ctx 栅栏写口的认领由调用方每次交进来 —— [ref]:栅栏的来源是认领本身,店不另绑 run 店。) */
76
75
  export interface SqlCheckpointStoreOptions {
77
76
  readonly retention?: RetentionDeclaration;
78
77
  }
@@ -162,7 +161,27 @@ export declare class SqlCheckpointStore implements CheckpointStore {
162
161
  private pendingRowQuery;
163
162
  /** Create-once. core mints the token (`mintCheckpointToken`) and calls this during suspend. */
164
163
  put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
164
+ /** 一行的列组 —— `get` 与按会话列表读同一份投影、交同一只解码({@link readStoredCheckpoint})。 */
165
+ private storedCheckpointColumns;
165
166
  get(token: CheckpointToken): Promise<Checkpoint | null>;
167
+ /**
168
+ * 一行字节 → 检查点 —— **`get` 与按会话列表({@link listBySession})的唯一解码**(列组 = {@link storedCheckpointColumns})。读不成的格交回 `damaged`
169
+ * (`get` 原样抛那只拒绝,列表把这一行答 `unreadable`);非体问题(读腿漏投影 `not-text` / 连接)原样抛 —— 码坏不冒充一行数据坏。
170
+ */
171
+ private readStoredCheckpoint;
172
+ /**
173
+ * core `CheckpointStore.listBySession`(可选动词,按在不在探;core 7.66.0 契约):一只会话的行,任何状态,按查询的成员收窄、按 `createdAt` 再按令牌排、键集分页。
174
+ * **读法只有 core 那一只**:查询经 `readCheckpointSessionQuery` 读(坏查询在任何 SQL 之前带码拒),过滤 / 排序 / 游标 / 截断 / 损坏判 全由 core 的 `selectSessionPage` 答
175
+ * (内存店与文件店同调它);本店只交「这只会话的行」—— 按会话列(`session_id`,本店每一只按会话的读口同读这一列)取行,经 `get` 的同一只解码({@link readStoredCheckpoint})成检查点。
176
+ * SQL 只按会话收窄,别的成员(状态 / scope / 门型 / 工具调用 / 建行时刻)一律由 core 判 —— 列上的比较不是 core 的比较(MySQL 协议的 `utf8mb4_bin` 按补空格比,
177
+ * 带尾空格的集外状态词会被 SQL 当成三词之一排除,而 core 要把它答成 `unreadable`),所以 SQL 不预筛任何一格,预筛不可能排除 core 会留下的行。
178
+ * 读成本:这只会话的每一行读一份字节(检查点体 + 旁格)—— 只在页里的行交出去;会话的行数由留存扫车道界住。
179
+ * 一行读不成(体不是一只检查点 / 决议或执行记录那一格读不出 / 体没说出它归在的这只会话)⇒ 这一行**照列**为 `unreadable`(令牌 / scope / 会话 / 状态 / 建行时刻取本店在字节之外
180
+ * 握着的列组;体读成、归属对而旁格坏 ⇒ 过滤器照读体上的成员),不跳过、不让整页失败;拿它的令牌去 `get` 撞见同一只拒。体是对象而成员形读不动 ⇒ core 自己答 `unreadable`。
181
+ * 页里每一只这样的行计一次 `server.approvals.park-row-blob-unreadable`(本店停驻行读者的那一条律:一行的字节只坏它自己那一行):core 判的经它给的座(句子去掉令牌后作探针),
182
+ * 本店判、core 看着读得动的那几行由本店计。只读:列表不改任何一行;交出的行是这一次解码的新对象,改它不改库。
183
+ */
184
+ listBySession(query: CheckpointSessionQuery): Promise<CheckpointPage<CheckpointRowRead, CheckpointSessionCursor>>;
166
185
  /**
167
186
  * Atomic CAS: pending → resolved, scoped. Returns true for the single winner, false if already
168
187
  * resolved/expired (AlreadyResolved → caller no-ops, never re-executes). `outcome` is persisted atomically
@@ -1,9 +1,9 @@
1
1
  import { parkedApprovalCard } from "../approval-content-kind.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, checkpointSweepAfterSql, hostSessionOf, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
4
- import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, } from "@sema-agent/core";
4
+ import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, readCheckpointSessionQuery, selectSessionPage, } from "@sema-agent/core";
5
5
  import { parkRowFacts, skillCardFacts } from "../approval-card.js";
6
- import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
6
+ import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
7
7
  import { recordFailOpen } from "../observability/fail-open.js";
8
8
  import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
9
9
  import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
@@ -242,6 +242,12 @@ export function readPendingSteerQueueColumn(queueRaw) {
242
242
  }
243
243
  const STEER_CAS_ATTEMPTS = 8;
244
244
  const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
245
+ const STORED_ROW_DAMAGE = {
246
+ body: "its stored body is not a checkpoint object",
247
+ decision: "its stored decision record cannot be read",
248
+ execution: "its stored execution record cannot be read",
249
+ filing: "its stored body does not name the session the row is filed under",
250
+ };
245
251
  export class SqlCheckpointStore {
246
252
  db;
247
253
  logger;
@@ -337,21 +343,62 @@ export class SqlCheckpointStore {
337
343
  throw rowTooLargeOrSelf(this.db.dialect, e, `the checkpoint row for session ${JSON.stringify(cp.sessionId)}`);
338
344
  }
339
345
  }
346
+ storedCheckpointColumns() {
347
+ const d = this.db.dialect;
348
+ return (`status, ${jsonColumnText(d, "checkpoint")}, ${jsonColumnText(d, "outcome")}, reopen_reason, rev, ` +
349
+ `${jsonColumnText(d, "pending_steer_queue")}, ${jsonColumnText(d, "execution_outcome")}, execution_at_ms`);
350
+ }
340
351
  async get(token) {
341
- const { rows } = await this.db.query(this.q(`SELECT status, ${jsonColumnText("mysql", "checkpoint")}, ${jsonColumnText("mysql", "outcome")}, reopen_reason, rev, ` +
342
- `${jsonColumnText("mysql", "pending_steer_queue")}, ${jsonColumnText("mysql", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = ?`, `SELECT status, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnText("pg", "outcome")}, reopen_reason, rev, ` +
343
- `${jsonColumnText("pg", "pending_steer_queue")}, ${jsonColumnText("pg", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = $1`), [token]);
352
+ const { rows } = await this.db.query(`SELECT ${this.storedCheckpointColumns()} FROM checkpoint WHERE token = ${this.ph(1)}`, [token]);
344
353
  const r = rows[0];
345
354
  if (!r)
346
355
  return null;
347
- const cp = readCheckpointBodyOrRefuse(r.checkpoint, `for token ${tokenFingerprint(token)}`);
356
+ const read = this.readStoredCheckpoint(r, token);
357
+ if (read.kind === "damaged")
358
+ throw read.error;
359
+ const cp = read.checkpoint;
360
+ this.logger?.info?.("checkpoint_get", {
361
+ tokenFp: tokenFingerprint(token),
362
+ status: cp.status,
363
+ hasWorkspaceHandle: !!cp.state?.workspaceHandle,
364
+ snapshotId: cp.state?.workspaceHandle?.snapshotId,
365
+ });
366
+ return cp;
367
+ }
368
+ readStoredCheckpoint(r, token) {
369
+ const where = `for token ${tokenFingerprint(token)}`;
370
+ let cp;
371
+ try {
372
+ cp = readCheckpointBodyOrRefuse(r.checkpoint, where);
373
+ }
374
+ catch (e) {
375
+ if (e instanceof StoreCorruptError)
376
+ return { kind: "damaged", member: "body", error: e };
377
+ throw e;
378
+ }
348
379
  cp.status = r.status;
349
- const outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, `for token ${tokenFingerprint(token)}`));
380
+ let outcomeRaw;
381
+ try {
382
+ outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, where));
383
+ }
384
+ catch (e) {
385
+ if (e instanceof StoreCorruptError)
386
+ return { kind: "damaged", member: "decision", error: e, body: cp };
387
+ throw e;
388
+ }
350
389
  if (outcomeRaw)
351
390
  cp.resolvedOutcome = winnerFromOutcome(outcomeRaw);
352
391
  cp.reopenReason = r.reopen_reason ?? undefined;
353
392
  cp.rev = r.rev == null ? 0 : Number(r.rev);
354
- const executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, `for token ${tokenFingerprint(token)}`));
393
+ let executionOutcome;
394
+ try {
395
+ executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, where));
396
+ }
397
+ catch (e) {
398
+ if (e instanceof StoreCorruptError)
399
+ return { kind: "damaged", member: "execution", error: e, body: cp };
400
+ throw e;
401
+ }
355
402
  if (executionOutcome !== undefined) {
356
403
  cp.executionOutcome = executionOutcome;
357
404
  if (r.execution_at_ms != null)
@@ -362,17 +409,58 @@ export class SqlCheckpointStore {
362
409
  recordFailOpen("server.sql-json.checkpoint.pending-steer-queue-lossy");
363
410
  this.logger?.info?.("checkpoint_pending_steer_unreadable", { tokenFp: tokenFingerprint(token) });
364
411
  }
365
- if (cp.state) {
366
- if (steer.state.pendingSteerQueue !== undefined)
367
- cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
368
- }
369
- this.logger?.info?.("checkpoint_get", {
370
- tokenFp: tokenFingerprint(token),
371
- status: cp.status,
372
- hasWorkspaceHandle: !!cp.state?.workspaceHandle,
373
- snapshotId: cp.state?.workspaceHandle?.snapshotId,
412
+ if (isJsonObject(cp.state) && steer.state.pendingSteerQueue !== undefined)
413
+ cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
414
+ return { kind: "row", checkpoint: cp };
415
+ }
416
+ async listBySession(query) {
417
+ const read = readCheckpointSessionQuery(query);
418
+ const { rows } = await this.db.query(`SELECT token, scope, session_id, created_at_ms, ${this.storedCheckpointColumns()} FROM checkpoint WHERE session_id = ${this.ph(1)}`, [read.sessionId]);
419
+ const tokens = rows.map((r) => String(r.token));
420
+ const withoutTokens = (sentence) => tokens.reduce((out, t) => withoutCheckpointToken(out, t), sentence);
421
+ const damaged = new Map();
422
+ const candidates = [];
423
+ rows.forEach((r, i) => {
424
+ const token = tokens[i];
425
+ const got = this.readStoredCheckpoint(r, token);
426
+ const body = got.kind === "row" ? got.checkpoint : got.member === "body" ? undefined : got.body;
427
+ const filedHere = body !== undefined && body.sessionId === String(r.session_id);
428
+ if (got.kind === "row" && filedHere) {
429
+ candidates.push([token, body]);
430
+ return;
431
+ }
432
+ damaged.set(token, { problem: STORED_ROW_DAMAGE[got.kind === "damaged" ? got.member : "filing"], r, counted: filedHere });
433
+ if (filedHere) {
434
+ candidates.push([token, body]);
435
+ return;
436
+ }
437
+ const filing = { token: token, sessionId: String(r.session_id), scope: String(r.scope), status: String(r.status), createdAt: Number(r.created_at_ms) };
438
+ candidates.push([token, filing]);
374
439
  });
375
- return cp;
440
+ const page = selectSessionPage(candidates, read, (cp) => cp, (sentence) => recordFailOpen("server.approvals.park-row-blob-unreadable", withoutTokens(sentence)));
441
+ if (damaged.size === 0)
442
+ return page;
443
+ return {
444
+ ...page,
445
+ rows: page.rows.map((row) => {
446
+ const token = row.kind === "row" ? row.checkpoint.token : row.token;
447
+ const hit = damaged.get(token);
448
+ if (hit === undefined)
449
+ return row;
450
+ if (hit.counted && row.kind === "row")
451
+ recordFailOpen("server.approvals.park-row-blob-unreadable", `session listing, session ${JSON.stringify(String(hit.r.session_id))}: ${hit.problem} — listed as unreadable`);
452
+ const createdAt = Number(hit.r.created_at_ms);
453
+ return {
454
+ kind: "unreadable",
455
+ token: token,
456
+ scope: String(hit.r.scope),
457
+ sessionId: String(hit.r.session_id),
458
+ status: String(hit.r.status),
459
+ createdAt: Number.isFinite(createdAt) ? createdAt : null,
460
+ problem: hit.problem,
461
+ };
462
+ }),
463
+ };
376
464
  }
377
465
  async resolve(token, scope, outcome, expect) {
378
466
  const params = [this.json(outcome, "checkpoint outcome"), Date.now(), token, scope];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 托管留存(managed retention)的**执行器** —— 引擎座表那一半(阶段三逐字搬进 `@sema-agent/core-stores-sql` 的那一半)。
3
3
  * SINGLE-FILE DUAL-DIALECT([ref] A12 定型半场;先例 = `checkpoint-store-sql.ts`)。[ref] 车1,设计稿
4
- * `docs/DESIGN-270-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
4
+ * `docs/[ref]-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
5
5
  * (`ManagedRetentionCapability` / `RetentionReceipt`);事务内钩表的形 = core-stores-sql 的 `SqlTransactionHooks`(本文件的同名型是它的形,见下)。
6
6
  *
7
7
  * ── 一个事务、两个属主:执行器 + 部署钩 ─────────────────────────────────────────────────────────────
@@ -6,9 +6,10 @@ import type { RunInternals } from "@sema-agent/core";
6
6
  * 统一剥掉(根 `/` 除外),让同一目标只有一个 key —— 前缀判域(见 {@link dirCoversPath})靠的就是
7
7
  * key 的唯一性。
8
8
  *
9
- * 🔴 **单点**([ref] 起):此前它的一份私有副本住在 `boot/deferred-sandbox-path-env.ts`(沙箱代理的
10
- * `absolutePath` 词法臂),而写门豁免域与任务根的判域现在读的是同一条律 —— 两份各自归一就是两个 key
11
- * 空间,而「豁免域盖没盖住任务根」这种判断在两个 key 空间里可以同时答是和否。
9
+ * 🔴 **单点**([ref] 起):写门豁免域与任务根的判域读的是同一条律 —— 两份各自归一就是两个 key
10
+ * 空间,而「豁免域盖没盖住任务根」这种判断在两个 key 空间里可以同时答是和否。它只答**配置座**上的判域
11
+ * (env-facts 的 cwd / scratchpad 事实、{@link dirCoversPath});执行环境方法读路径不经它 —— 那一面是执行缝路径律
12
+ * (`plugins/exec-path-law.ts`,只锚不折,沙箱写门代理的 `absolutePath` 也读那一只)。
12
13
  * (core 内部有同形的 `normalizeAbsPathLexically`,但未从包根导出;此处是 Node 标准库的等价运算,
13
14
  * 不是它的抄本——若日后 core 导出,这里应改为直接复用。)
14
15
  */
@@ -463,6 +463,19 @@ export declare function taskHooksHonored(config: {
463
463
  * 在 `server.ts` prepareSpec 里 400 响亮拒 + 逐字列名。双层姿势与 outputStyle / permissionMode 两处逐字相同。
464
464
  */
465
465
  export declare function parseTaskSettings(raw: unknown, seats?: ParseTaskSettingsSeats): ParseTaskSettingsResult;
466
+ /** The one reading of `settings.outputStyle`: a non-empty string within {@link MAX_SETTINGS_OUTPUT_STYLE_CHARS} ⇒ it; anything else ⇒ absent.
467
+ * Readers: the settings parser above and the system-prompt fold judgment at the head of `resolveSpec` (which runs before the parser's stage). */
468
+ export declare function acceptOutputStyle(v: unknown): string | undefined;
469
+ /**
470
+ * **系统提示追加折叠的唯一读法**(一个 spec 字段 `TaskSpec.appendSystemPrompt` 里折进几段):段按序、缺席 / 空段不出、`\n\n` 连接;`chars` = 折出来的字符数(含连接符)。
471
+ * 段序恒为 请求 `appendSystemPrompt` → 项目简报(`project-context.ts`)→ `settings.outputStyle`(字节稳定,前缀缓存靠它);三段共一只上限 16384。
472
+ * 读者四位,同读这一只:受理口的两段合计判(`http/admission.ts`)、装配器头部的三段判(`boot/resolve-spec.ts`,只在简报在场时)、装配合成(本文件 `applyTaskSettings` 接 style)、
473
+ * 续跑镜像(`resolve-spec.ts` 阶段⑤:存量体合计超帽 ⇒ 丢 style + warn)。
474
+ */
475
+ export declare function appendFold(segments: readonly (string | undefined)[]): {
476
+ readonly text: string | undefined;
477
+ readonly chars: number;
478
+ };
466
479
  /** Defensive acceptance of the `appendSystemPrompt` rider ([ref] R2 + codex F2): the HTTP 400 gate (prepareSpec,
467
480
  * MAX_SYSTEM_PROMPT_CHARS) only covers FRESH submits — every resume family re-enters resolveSpec on a STORED body,
468
481
  * and a pre-1.243 store preserved unknown request keys verbatim (the field was INERT then), so an over-cap legacy
@@ -360,9 +360,9 @@ export function parseTaskSettings(raw, seats = {}) {
360
360
  }
361
361
  if (typeof s.model === "string" && s.model.length > 0)
362
362
  out.model = s.model;
363
- if (typeof s.outputStyle === "string" && s.outputStyle.length > 0 && s.outputStyle.length <= MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
364
- out.outputStyle = s.outputStyle;
365
- }
363
+ const outputStyle = acceptOutputStyle(s.outputStyle);
364
+ if (outputStyle !== undefined)
365
+ out.outputStyle = outputStyle;
366
366
  if (shellEnv)
367
367
  out.shellEnv = shellEnv;
368
368
  if (s.ultracode === true)
@@ -388,6 +388,14 @@ export function parseTaskSettings(raw, seats = {}) {
388
388
  out.httpHookAllowlist = httpHookAllowlist;
389
389
  return { settings: Object.keys(out).length > 0 ? out : undefined, deferred, ...(hooksReread !== undefined ? { hooksReread } : {}) };
390
390
  }
391
+ export function acceptOutputStyle(v) {
392
+ return typeof v === "string" && v.length > 0 && v.length <= MAX_SETTINGS_OUTPUT_STYLE_CHARS ? v : undefined;
393
+ }
394
+ export function appendFold(segments) {
395
+ const present = segments.filter((segment) => segment !== undefined && segment.length > 0);
396
+ const text = present.length > 0 ? present.join("\n\n") : undefined;
397
+ return { text, chars: text?.length ?? 0 };
398
+ }
391
399
  export function acceptAppendSystemPrompt(v, warn) {
392
400
  if (v === undefined)
393
401
  return undefined;
@@ -593,10 +601,8 @@ export function applyTaskSettings(base, settings, bases) {
593
601
  const allow = settings.permissions?.allow;
594
602
  if (allow !== undefined)
595
603
  next = { ...next, allowedTools: allow.map((entry) => allowMemberOf(entry, bases?.configRoot)) };
596
- if (settings.outputStyle) {
597
- const appended = next.appendSystemPrompt ? `${next.appendSystemPrompt}\n\n${settings.outputStyle}` : settings.outputStyle;
598
- next = { ...next, appendSystemPrompt: appended };
599
- }
604
+ if (settings.outputStyle)
605
+ next = { ...next, appendSystemPrompt: appendFold([next.appendSystemPrompt, settings.outputStyle]).text };
600
606
  return next;
601
607
  }
602
608
  //# sourceMappingURL=task-settings.js.map
@@ -300,9 +300,9 @@ export type StandingAnswerHome = "none" | "seat" | "server_store";
300
300
  /**
301
301
  * 🔴 **[ref] —— 会话级放行的唯一判据:站着的答案能不能退掉这一问,能的话它记在哪({@link StandingAnswerHome})。** 纯函数,读者:
302
302
  * · **读侧**(`askBroadcast` 的会话授权短路 {@link ToolApprovalCoordinator.standingGrantSettles}):`seat` 才可能由既有授权代答,否则往下走真出卡;
303
- * · **写侧**(`finishRespond` 的 `grantable` 与 `recordDeploymentListAnswer`):`seat` 把 `allow_session` 铸成进程内授权;`server_store` 落 server 的会话豁免店;
303
+ * · **写侧**(`finishRespond` 与 `recordDeploymentListAnswer`,经 {@link liveSessionAnswer}):`seat` 把 `allow_session` 铸成进程内授权;`server_store` 落 server 的会话豁免店;
304
304
  * `none` 只放行这一次、`rememberApplied:false`;
305
- * · **卡面提示位**(活卡帧 / 条目 / `livePending` 行的 `rememberRefused`):在场 ⇔ `none`;
305
+ * · **卡面提示位**(活卡帧 / 条目 / `livePending` 行的 `rememberRefused`):在场 ⇔ 本函数答 `none` 或这条腿记不下(读的是 {@link liveSessionAnswer},它先问本函数);
306
306
  * · **耐久可记性**({@link standingAnswerDurablyRecordable}:`/decide` 的 remember、运维队列行、策略层两只豁免读面)。
307
307
  * 各读者读同一份事实、说同一句话:一问若不许被记住的放行答掉,它的应答也不许变成一条记住的放行 —— 否则
308
308
  * 同一只卡「重跑一遍」就被自己上一次的回答代答(test [ref] U1s-off 的真形)。读者侧没有任何按「问的属主」另判的臂:属主只在本函数里读一次。
@@ -469,11 +469,12 @@ export interface ToolApprovalFrame {
469
469
  mandated?: true;
470
470
  /**
471
471
  * **7.116.0(审计修复批 S4;ADDITIVE,`"tool_approval"` only)—— 这张卡上的「本会话放行」只放行这一次、不会被记住。**
472
- * 在场 ⇔ 同一只判据 {@link standingAnswerHome} 答 `none`(治理强制 / 必须真人 / 强制 / 应答还授予别的东西 / 类集清不掉),回决侧写授权
473
- * (`finishRespond`)读的是同一份事实 ⇒ 帧说不可记 ⇔ 点了 `allow_session` 回执 `rememberApplied:false`。壳读这一位决定渲不渲
472
+ * 在场 ⇔ 同一只判官 {@link liveSessionAnswer} 答 `none`(问本身记不住:治理强制 / 必须真人 / 强制 / 应答还授予别的东西 / 类集清不掉;或这条腿记不下:
473
+ * 没有会话 / 读不出会话住客),回决侧写授权(`finishRespond`)读的是同一只 ⇒ 帧说不可记 ⇔ 点了 `allow_session` 回执 `rememberApplied:false`。壳读这一位决定渲不渲
474
474
  * 「本会话放行」,不再从 `requiresRealApproval` / `mandated` / `governanceForced` 三位自己推断(那一推断漏掉第四个理由:core 7.41.0
475
475
  * 的 `Skill` 卡批准即授予清单,三位全无)。`card_json` 卡面、`livePending` 行、运维队列行同名同义。
476
- * 🔴 **真才带,缺席绝不编 `false`**:缺席 = 可记(有会话时)或老服务端。没有会话的腿上、或读不出会话住客的腿上点了照样只放行一次(回执说明),本位不管那两形。
476
+ * 判官是 {@link liveSessionAnswer}(这一问的事实 + 这条腿记不记得下:没有会话的腿、读不出会话住客的腿同样在场),回决写侧读同一只。
477
+ * 🔴 **真才带,缺席绝不编 `false`**:缺席 = 在这条腿上记得住,或老服务端。
477
478
  */
478
479
  rememberRefused?: true;
479
480
  /**
@@ -528,7 +529,7 @@ export interface ToolApprovalFrame {
528
529
  * **[ref] ②(core 7.37.0 [ref];ADDITIVE,`"tool_approval"` only)** —— 这一问的**类**:`classes` 的首员(core `ASK_CLASSES` 序),
529
530
  * `MODE_ARBITRATION` 的行键(权限模式读的就是它 / 它们)。引擎在每一个铸 `origin` 的站盖章(策略自报的值在盖章处被替换),本仓经
530
531
  * `approval-card.ts` 的 `readAskClassPair(req)` 窄读(判法 = core 屏 `screenAskClassMembers`,[ref] ⑥ / [ref],本仓零自写对判;屏拒 ⇒ 两键都不投 + 计一次
531
- * [ref] 集外 tag)。**与 {@link classes} 同在同缺**;缺席 = 老引擎 / 不盖类的站(共享制品读确认,core KL-1214-a),不是「无类」。
532
+ * [ref] 集外 tag)。**与 {@link classes} 同在同缺**;缺席 = 老引擎 / 不盖类的站(共享制品读确认,core [ref]),不是「无类」。
532
533
  * display:壳按类渲卡(cli 卡面、client-core [ref]),durable 审批席按类答;**本仓不按它裁决**(无人值守腿的按类作答读 core 交来的
533
534
  * `AskRequest.classes` 本身)。同一对的其余面:`card_json` 卡面 / `approval_request` 呈卡帧 / 断连重放帧(同一份素材)、运维队列行
534
535
  * (`parkRowFacts`,读停车行孪生座 `PendingAction.tool_approval.askClass`);inbox 行不带(core `CheckpointSummary` 不回显)。