@sema-agent/server 7.148.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 (360) hide show
  1. package/MIGRATION.md +551 -9
  2. package/README.md +17 -2
  3. package/README.zh-CN.md +5 -1
  4. package/USAGE.md +126 -68
  5. package/dist/adoption/wire.d.ts +3 -3
  6. package/dist/approval-card.d.ts +33 -33
  7. package/dist/approval-gate-currency.d.ts +1 -1
  8. package/dist/approval-leg-origin.d.ts +33 -0
  9. package/dist/approval-leg-origin.js +4 -0
  10. package/dist/audit.d.ts +1 -1
  11. package/dist/audit.js +2 -2
  12. package/dist/auth-bridge.d.ts +9 -1
  13. package/dist/auth-bridge.js +4 -4
  14. package/dist/auth-keys.d.ts +10 -1
  15. package/dist/auth-keys.js +8 -0
  16. package/dist/background-session-reap.d.ts +1 -1
  17. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  18. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  19. package/dist/boot/execution-env.js +10 -11
  20. package/dist/boot/execution-lane.d.ts +6 -4
  21. package/dist/boot/execution-lane.js +3 -1
  22. package/dist/boot/org-delivery.d.ts +3 -2
  23. package/dist/boot/parked-revive-gate.d.ts +5 -5
  24. package/dist/boot/parked-revive-gate.js +2 -3
  25. package/dist/boot/resolve-spec.d.ts +4 -5
  26. package/dist/boot/resolve-spec.js +66 -28
  27. package/dist/boot/retention-lane.d.ts +1 -1
  28. package/dist/boot/runner-deps.js +3 -1
  29. package/dist/boot/session-faces.js +21 -21
  30. package/dist/boot/stage-01-config.d.ts +1 -0
  31. package/dist/boot/stage-01-config.js +4 -3
  32. package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
  33. package/dist/boot/stage-02-web-admin-pull.js +10 -2
  34. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  35. package/dist/boot/stage-03-store-backend.js +4 -4
  36. package/dist/boot/stage-04-stores.d.ts +1 -0
  37. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  38. package/dist/boot/stage-06-runners.d.ts +2 -1
  39. package/dist/boot/stage-06-runners.js +1 -2
  40. package/dist/boot/stage-07-capability-layer.d.ts +2 -1
  41. package/dist/boot/stage-07-capability-layer.js +1 -1
  42. package/dist/boot/stage-08-reapers.d.ts +2 -1
  43. package/dist/boot/stage-09-leader.d.ts +2 -1
  44. package/dist/boot/stage-10-http-server.d.ts +35 -2
  45. package/dist/boot/stage-10-http-server.js +9 -4
  46. package/dist/boot/stores.d.ts +3 -0
  47. package/dist/boot/stores.js +22 -19
  48. package/dist/boot/web-admin.d.ts +1 -1
  49. package/dist/capabilities/tool-defer.d.ts +1 -1
  50. package/dist/capability-facts.d.ts +70 -60
  51. package/dist/capability-facts.js +45 -49
  52. package/dist/card-settle-stored-scope.d.ts +56 -0
  53. package/dist/card-settle-stored-scope.js +27 -0
  54. package/dist/card-void.d.ts +7 -12
  55. package/dist/card-void.js +2 -7
  56. package/dist/config-catalog.d.ts +12 -0
  57. package/dist/config-catalog.js +37 -33
  58. package/dist/config-invariants.d.ts +5 -4
  59. package/dist/config-invariants.js +4 -4
  60. package/dist/config-types.d.ts +49 -16
  61. package/dist/config.d.ts +6 -5
  62. package/dist/config.js +35 -24
  63. package/dist/context-window-notice.d.ts +31 -0
  64. package/dist/context-window-notice.js +28 -0
  65. package/dist/deployment-governance.d.ts +7 -28
  66. package/dist/deployment-governance.js +1 -14
  67. package/dist/deployment-shape.d.ts +20 -11
  68. package/dist/deployment-shape.js +1 -0
  69. package/dist/device-ws-hub.d.ts +1 -0
  70. package/dist/device-ws-hub.js +64 -28
  71. package/dist/device-ws-protocol.d.ts +20 -6
  72. package/dist/device-ws-protocol.js +9 -2
  73. package/dist/engine-identity.d.ts +7 -1
  74. package/dist/engine-identity.js +5 -0
  75. package/dist/env-name-allowlist-knobs.d.ts +6 -5
  76. package/dist/env-name-allowlist-knobs.js +5 -0
  77. package/dist/execution-lane-caps.d.ts +20 -5
  78. package/dist/execution-lane-caps.js +18 -4
  79. package/dist/field-conflict.d.ts +2 -2
  80. package/dist/fleet/engine-signal.d.ts +14 -1
  81. package/dist/fleet/engine-signal.js +5 -1
  82. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  83. package/dist/fleet/serving-lease.d.ts +18 -3
  84. package/dist/fleet/serving-lease.js +81 -35
  85. package/dist/fleet-client.d.ts +13 -11
  86. package/dist/fleet-client.js +53 -33
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +1 -3
  89. package/dist/hitl-delivery.js +1 -1
  90. package/dist/hooks/command-spawn.d.ts +7 -2
  91. package/dist/hooks/command-spawn.js +13 -6
  92. package/dist/hooks/hook-runner.d.ts +5 -0
  93. package/dist/hooks/hook-runner.js +11 -7
  94. package/dist/http/active-run-conflict.d.ts +3 -7
  95. package/dist/http/active-run-conflict.js +2 -2
  96. package/dist/http/admission.js +5 -5
  97. package/dist/http/approval-sweeps.js +5 -3
  98. package/dist/http/caller-trust.d.ts +2 -2
  99. package/dist/http/caller-trust.js +3 -3
  100. package/dist/http/checkpoint-ctx.d.ts +18 -63
  101. package/dist/http/checkpoint-ctx.js +1 -24
  102. package/dist/http/claim-heartbeat.d.ts +6 -6
  103. package/dist/http/claim-heartbeat.js +6 -6
  104. package/dist/http/claimed-run.d.ts +16 -9
  105. package/dist/http/claimed-run.js +8 -0
  106. package/dist/http/dispatch.js +7 -5
  107. package/dist/http/durable-run-leg.d.ts +8 -1
  108. package/dist/http/durable-run-leg.js +34 -15
  109. package/dist/http/execution-context.d.ts +115 -14
  110. package/dist/http/execution-context.js +130 -40
  111. package/dist/http/owner-access.d.ts +246 -21
  112. package/dist/http/owner-access.js +186 -8
  113. package/dist/http/principal-gate.d.ts +18 -11
  114. package/dist/http/principal-gate.js +3 -1
  115. package/dist/http/readiness.d.ts +4 -2
  116. package/dist/http/resume-ctx-read.d.ts +14 -3
  117. package/dist/http/resume-ctx-read.js +24 -3
  118. package/dist/http/resume-legs.d.ts +35 -3
  119. package/dist/http/resume-legs.js +262 -106
  120. package/dist/http/route-ctx.d.ts +29 -11
  121. package/dist/http/routes/a2a-serve.js +17 -14
  122. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  123. package/dist/http/routes/approvals-assistant.js +11 -11
  124. package/dist/http/routes/capabilities.js +5 -4
  125. package/dist/http/routes/config-catalog.d.ts +1 -1
  126. package/dist/http/routes/diagnostics.d.ts +3 -1
  127. package/dist/http/routes/diagnostics.js +30 -5
  128. package/dist/http/routes/mcp-reconnect.js +2 -2
  129. package/dist/http/routes/memory-origin.d.ts +1 -1
  130. package/dist/http/routes/notify-wake.js +2 -3
  131. package/dist/http/routes/principal-inbox.d.ts +3 -1
  132. package/dist/http/routes/principal-inbox.js +3 -2
  133. package/dist/http/routes/projects.d.ts +1 -0
  134. package/dist/http/routes/projects.js +5 -3
  135. package/dist/http/routes/retention-ops.d.ts +1 -1
  136. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  137. package/dist/http/routes/run-permission-mode.js +2 -2
  138. package/dist/http/routes/runs.d.ts +1 -1
  139. package/dist/http/routes/runs.js +33 -37
  140. package/dist/http/routes/session-memory-sync.js +4 -5
  141. package/dist/http/routes/session-sync.d.ts +24 -5
  142. package/dist/http/routes/session-sync.js +45 -37
  143. package/dist/http/routes/sessions-list.d.ts +1 -1
  144. package/dist/http/routes/sessions.d.ts +1 -1
  145. package/dist/http/routes/sessions.js +18 -44
  146. package/dist/http/routes/tasks.js +136 -101
  147. package/dist/http/routes/trace-usage.js +14 -14
  148. package/dist/http/routes/workflows.d.ts +1 -0
  149. package/dist/http/routes/workflows.js +20 -38
  150. package/dist/http/server.d.ts +12 -9
  151. package/dist/http/server.js +35 -6
  152. package/dist/http/stored-body-replay.d.ts +3 -24
  153. package/dist/http/stored-body-replay.js +2 -12
  154. package/dist/http/submit-ctx.d.ts +2 -2
  155. package/dist/http/submit-ctx.js +5 -5
  156. package/dist/http/submit-replay.d.ts +15 -2
  157. package/dist/http/submit-replay.js +10 -4
  158. package/dist/http/wire-types.d.ts +6 -3
  159. package/dist/index.d.ts +3 -1
  160. package/dist/index.js +1 -0
  161. package/dist/leader/endpoint.js +9 -8
  162. package/dist/leg-facts.d.ts +13 -2
  163. package/dist/leg-facts.js +2 -0
  164. package/dist/local-store-declaration.d.ts +54 -0
  165. package/dist/local-store-declaration.js +59 -0
  166. package/dist/mcp-assertion-header.d.ts +1 -1
  167. package/dist/mcp-assertion-header.js +1 -1
  168. package/dist/mcp-authorization-face.d.ts +18 -14
  169. package/dist/mcp-authorization-face.js +3 -2
  170. package/dist/memory-project-layer.js +1 -1
  171. package/dist/memory-scope.d.ts +4 -4
  172. package/dist/memory-scope.js +4 -4
  173. package/dist/memory-store-refusal.d.ts +1 -1
  174. package/dist/memory-sync-client.js +2 -2
  175. package/dist/model-context-window.d.ts +50 -11
  176. package/dist/model-context-window.js +20 -1
  177. package/dist/model-entry-refusal.d.ts +5 -4
  178. package/dist/model-entry-refusal.js +17 -3
  179. package/dist/model-reach-scope.d.ts +2 -1
  180. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  181. package/dist/observability/fail-open.d.ts +19 -7
  182. package/dist/observability/fail-open.js +19 -7
  183. package/dist/observability/logger.d.ts +7 -0
  184. package/dist/observability/logger.js +3 -1
  185. package/dist/observability/metrics.js +1 -1
  186. package/dist/observability/run-terminal.d.ts +1 -1
  187. package/dist/observability/run-terminal.js +1 -0
  188. package/dist/observability/secret-env-scrub.d.ts +1 -1
  189. package/dist/observability/secret-env-scrub.js +1 -1
  190. package/dist/org-delivery-gate.d.ts +31 -6
  191. package/dist/org-delivery-gate.js +7 -4
  192. package/dist/owner-faces.d.ts +30 -5
  193. package/dist/owner-faces.js +2 -0
  194. package/dist/park-row-commit.d.ts +67 -0
  195. package/dist/park-row-commit.js +76 -0
  196. package/dist/parked-decide.d.ts +9 -7
  197. package/dist/parked-decide.js +25 -6
  198. package/dist/peer-directory.d.ts +1 -1
  199. package/dist/peer-seat.d.ts +1 -1
  200. package/dist/peer-seat.js +52 -59
  201. package/dist/plugins/adoption-log-sql.d.ts +3 -3
  202. package/dist/plugins/adoption-log-sql.js +19 -19
  203. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  204. package/dist/plugins/approval-ask-store-sql.js +3 -5
  205. package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
  206. package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
  207. package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
  208. package/dist/plugins/approval-exemption-skill-m46.js +6 -10
  209. package/dist/plugins/approval-exemption-store.d.ts +26 -1
  210. package/dist/plugins/approval-exemption-store.js +28 -19
  211. package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
  212. package/dist/plugins/ensure-schema-lock-policy.js +75 -9
  213. package/dist/plugins/exec-path-law.d.ts +34 -0
  214. package/dist/plugins/exec-path-law.js +13 -0
  215. package/dist/plugins/file-run-store.d.ts +22 -9
  216. package/dist/plugins/file-run-store.js +51 -23
  217. package/dist/plugins/local-checkpoint-store.d.ts +8 -1
  218. package/dist/plugins/local-checkpoint-store.js +5 -1
  219. package/dist/plugins/local-data-root-admission.d.ts +52 -0
  220. package/dist/plugins/local-data-root-admission.js +100 -0
  221. package/dist/plugins/local-engine-high-water.d.ts +28 -7
  222. package/dist/plugins/local-engine-high-water.js +27 -22
  223. package/dist/plugins/local-session-store.d.ts +134 -49
  224. package/dist/plugins/local-session-store.js +149 -108
  225. package/dist/plugins/memory-run-store.d.ts +14 -9
  226. package/dist/plugins/memory-run-store.js +48 -22
  227. package/dist/plugins/one-time-migration-marker.d.ts +19 -3
  228. package/dist/plugins/one-time-migration-marker.js +7 -1
  229. package/dist/plugins/one-time-migrations.d.ts +110 -59
  230. package/dist/plugins/one-time-migrations.js +224 -136
  231. package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
  232. package/dist/plugins/org-delivery-quota-store.js +29 -14
  233. package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
  234. package/dist/plugins/permission-rule-store-sql.js +5 -9
  235. package/dist/plugins/pg-pool.d.ts +1 -1
  236. package/dist/plugins/pg-pool.js +14 -12
  237. package/dist/plugins/posix-shell-fs.js +4 -2
  238. package/dist/plugins/remote-env-adb.js +3 -2
  239. package/dist/plugins/remote-env-device.d.ts +3 -2
  240. package/dist/plugins/remote-env-device.js +8 -6
  241. package/dist/plugins/remote-env-e2b.d.ts +4 -8
  242. package/dist/plugins/remote-env-e2b.js +21 -15
  243. package/dist/plugins/remote-env-host.d.ts +51 -10
  244. package/dist/plugins/remote-env-host.js +97 -31
  245. package/dist/plugins/remote-env-k8s.js +7 -4
  246. package/dist/plugins/remote-env-local-docker.d.ts +6 -3
  247. package/dist/plugins/remote-env-local-docker.js +11 -25
  248. package/dist/plugins/remote-env-ssh.js +4 -3
  249. package/dist/plugins/remote-shell.d.ts +3 -1
  250. package/dist/plugins/remote-shell.js +3 -3
  251. package/dist/plugins/replica-registration.d.ts +35 -0
  252. package/dist/plugins/replica-registration.js +48 -0
  253. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  254. package/dist/plugins/run-store-sql.d.ts +27 -8
  255. package/dist/plugins/run-store-sql.js +46 -18
  256. package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
  257. package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
  258. package/dist/plugins/schema-consistency.d.ts +91 -5
  259. package/dist/plugins/schema-consistency.js +189 -7
  260. package/dist/plugins/schema-drift-sentence.d.ts +6 -5
  261. package/dist/plugins/schema-drift-sentence.js +13 -5
  262. package/dist/plugins/serving-requirement.d.ts +49 -0
  263. package/dist/plugins/serving-requirement.js +53 -0
  264. package/dist/plugins/session-placement.d.ts +5 -4
  265. package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
  266. package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
  267. package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
  268. package/dist/plugins/stop-then-start-sentence.js +51 -0
  269. package/dist/plugins/store-backend.d.ts +23 -9
  270. package/dist/plugins/store-backend.js +58 -24
  271. package/dist/plugins/store-contracts.d.ts +45 -5
  272. package/dist/plugins/store-contracts.js +19 -0
  273. package/dist/plugins/tidb-pool.d.ts +11 -6
  274. package/dist/plugins/tidb-pool.js +22 -12
  275. package/dist/plugins/workspace-custody.d.ts +44 -0
  276. package/dist/plugins/workspace-custody.js +47 -0
  277. package/dist/process-instance.d.ts +3 -0
  278. package/dist/process-instance.js +7 -0
  279. package/dist/project-context.d.ts +66 -0
  280. package/dist/project-context.js +70 -0
  281. package/dist/project-identity.d.ts +8 -7
  282. package/dist/project-identity.js +40 -17
  283. package/dist/project-layer-key.d.ts +13 -2
  284. package/dist/project-layer-key.js +9 -1
  285. package/dist/question.d.ts +9 -21
  286. package/dist/question.js +28 -19
  287. package/dist/read-deny-floor-notice.d.ts +36 -0
  288. package/dist/read-deny-floor-notice.js +40 -0
  289. package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
  290. package/dist/request-declaration-disposition.js +39 -0
  291. package/dist/run-cancel-card.d.ts +28 -0
  292. package/dist/run-cancel-card.js +20 -0
  293. package/dist/run-cancel-context.d.ts +10 -0
  294. package/dist/run-cancel-context.js +1 -0
  295. package/dist/run-local.js +28 -13
  296. package/dist/runs.d.ts +31 -7
  297. package/dist/runs.js +34 -16
  298. package/dist/security.d.ts +3 -3
  299. package/dist/server-secret-env.d.ts +34 -20
  300. package/dist/server-secret-env.js +61 -18
  301. package/dist/session-agent.d.ts +4 -12
  302. package/dist/session-agent.js +0 -6
  303. package/dist/session-children-retire.d.ts +6 -3
  304. package/dist/session-children-retire.js +2 -5
  305. package/dist/session-incarnation.d.ts +18 -4
  306. package/dist/session-incarnation.js +4 -1
  307. package/dist/session-notify-door.d.ts +4 -4
  308. package/dist/session-notify-door.js +5 -6
  309. package/dist/session-rules-seat.d.ts +4 -3
  310. package/dist/session-rules-seat.js +3 -11
  311. package/dist/session-sync.js +3 -3
  312. package/dist/session-workspace.d.ts +3 -3
  313. package/dist/store-admission.d.ts +33 -0
  314. package/dist/store-admission.js +49 -0
  315. package/dist/store-posture.d.ts +5 -0
  316. package/dist/store-posture.js +18 -1
  317. package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
  318. package/dist/stores-sql/checkpoint-store-sql.js +109 -23
  319. package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
  320. package/dist/stores-sql/file-history-store-sql.js +3 -5
  321. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  322. package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
  323. package/dist/stores-sql/tool-result-store-sql.js +4 -6
  324. package/dist/task-a2a.d.ts +3 -8
  325. package/dist/task-a2a.js +25 -27
  326. package/dist/task-cwd.d.ts +27 -10
  327. package/dist/task-cwd.js +3 -0
  328. package/dist/task-mcp.d.ts +3 -3
  329. package/dist/task-mcp.js +1 -1
  330. package/dist/task-settings.d.ts +13 -0
  331. package/dist/task-settings.js +14 -7
  332. package/dist/terminal.d.ts +2 -0
  333. package/dist/terminal.js +21 -6
  334. package/dist/tool-approval.d.ts +50 -41
  335. package/dist/tool-approval.js +96 -73
  336. package/dist/trace/engine-notice-wire.d.ts +38 -21
  337. package/dist/trace/engine-notice-wire.js +25 -4
  338. package/dist/trace/ledger-sink.d.ts +3 -1
  339. package/dist/trace/project.d.ts +8 -5
  340. package/dist/trace/project.js +11 -9
  341. package/dist/trace/projection-table.d.ts +33 -2
  342. package/dist/trace/redact.d.ts +12 -0
  343. package/dist/trace/redact.js +5 -1
  344. package/dist/trace/self-minted-wire.d.ts +268 -56
  345. package/dist/trace/self-minted-wire.js +110 -40
  346. package/dist/trace/tool-card.d.ts +3 -0
  347. package/dist/trace/tool-card.js +24 -0
  348. package/dist/trace/tool-output-media.d.ts +2 -1
  349. package/dist/unattended-permission-mode.d.ts +1 -1
  350. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  351. package/dist/web-admin-client/apply-effective.js +22 -9
  352. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  353. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  354. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  355. package/docs/error-text-lexicon.json +317 -0
  356. package/package.json +4 -2
  357. package/scripts/embed-minimal.mjs +183 -0
  358. package/wire-changes/server@7.149.0.json +372 -0
  359. package/wire-changes/server@7.150.0.json +335 -0
  360. package/dist/plain-string-record.js +0 -70
@@ -6,6 +6,7 @@ import { idemKeyOf } from "../http/idempotency.js";
6
6
  import { refusalBody } from "../http/refusal-body.js";
7
7
  import { LIVENESS_HEARTBEAT_MS } from "../config-invariants.js";
8
8
  import { seatText } from "../trace/exit-seat.js";
9
+ import { readVisibleLeaderRow } from "../http/owner-access.js";
9
10
  function statusForLeaderResult(result) {
10
11
  if (result.conflict)
11
12
  return "needs_human";
@@ -62,7 +63,7 @@ export function createLeaderEndpoint(runLeader, opts) {
62
63
  ? setInterval(() => {
63
64
  if (ac.signal.aborted)
64
65
  return;
65
- void readRow(id).then((row) => {
66
+ void readDrivenRow(id).then((row) => {
66
67
  if (row === undefined || row.status !== "running")
67
68
  abortLocal(id, "stall_verdict");
68
69
  else if (row.cancelRequested)
@@ -139,13 +140,13 @@ export function createLeaderEndpoint(runLeader, opts) {
139
140
  }
140
141
  })();
141
142
  }
142
- function readRow(id) {
143
+ function readDrivenRow(id) {
143
144
  return opts.store.getRun(id);
144
145
  }
145
146
  const opErr = (e) => seatText("operator-log-error", e instanceof Error ? e.message : String(e));
146
147
  async function cancelFlagAfterSettle(id) {
147
148
  try {
148
- return (await readRow(id))?.cancelRequested === true;
149
+ return (await readDrivenRow(id))?.cancelRequested === true;
149
150
  }
150
151
  catch (e) {
151
152
  opts.logger?.error?.("leader_cancel_flag_reread_failed", { id, err: opErr(e), effect: "if a cancel landed on this run just before its terminal write, its worker parks are expired only when the cancel is re-sent" });
@@ -305,8 +306,8 @@ export function createLeaderEndpoint(runLeader, opts) {
305
306
  if (m) {
306
307
  const id = m[1];
307
308
  if (opts.store) {
308
- const run = await readRow(id);
309
- if (!run || !ownerOk(run.owner))
309
+ const run = await readVisibleLeaderRow(opts.store, id, ownerOk);
310
+ if (!run)
310
311
  return NOT_FOUND;
311
312
  return getBody(run);
312
313
  }
@@ -327,15 +328,15 @@ export function createLeaderEndpoint(runLeader, opts) {
327
328
  }
328
329
  async function cancel(id, ownerOk) {
329
330
  if (opts.store) {
330
- const row = await readRow(id);
331
- if (!row || !ownerOk(row.owner))
331
+ const row = await readVisibleLeaderRow(opts.store, id, ownerOk);
332
+ if (!row)
332
333
  return NOT_FOUND;
333
334
  if (row.status === "running") {
334
335
  if (await opts.store.requestCancel(id)) {
335
336
  abortLocal(id, "cancel_verb");
336
337
  return { status: 202, body: { leaderRunId: id, status: "cancelling", cancelRequested: true } };
337
338
  }
338
- const now = await readRow(id);
339
+ const now = await readVisibleLeaderRow(opts.store, id, ownerOk);
339
340
  if (!now)
340
341
  return NOT_FOUND;
341
342
  return answerTerminal(id, now);
@@ -1,7 +1,7 @@
1
1
  /**
2
- * 一条腿的**部署事实**(执行车道 · run 技能名册 · 受理见证)—— server 自有的载体,**不搭引擎 spec 的车**。
2
+ * 一条腿的**部署事实**(执行车道 · run 技能名册 · 受理见证 · 登记的工作目录 · 继承的项目上下文)—— server 自有的载体,**不搭引擎 spec 的车**。
3
3
  *
4
- * 规则:`TaskSpec` 是引擎的类型面,上面只放引擎读的成员。本仓装配器(`boot/resolve-spec.ts` 的 `createResolveSpec`)同时还判出三件
4
+ * 规则:`TaskSpec` 是引擎的类型面,上面只放引擎读的成员。本仓装配器(`boot/resolve-spec.ts` 的 `createResolveSpec`)同时还判出五件
5
5
  * 只有 server 自己读的事实 —— 它们以装配器**自己的输出对象**为键登记在这一张表里(单一铸点 {@link mintLegFacts}),由两只漏斗
6
6
  * (受理 `http/admission.ts` 的 `prepareSpec` · 续跑 `http/resume-legs.ts` 的 `resolveResumeSpec`)在拿到装配输出的**同一只对象**时
7
7
  * 读一次(唯一读口 {@link legFactsOf},读在 `stampSpecRoot` 复制之前),此后作普通值随 `PreparedTaskSubmission.legFacts` /
@@ -18,6 +18,7 @@
18
18
  import type { ScenarioHands } from "./capabilities/hands-lane.js";
19
19
  import type { RunSkillEntry } from "./capabilities/skills.js";
20
20
  import type { DelegationRootWitness } from "./delegated-session-owner.js";
21
+ import type { ProjectContext } from "./project-context.js";
21
22
  /** 一条腿的部署事实(冻结记录)。 */
22
23
  export interface LegFacts {
23
24
  /** 执行车道:`none` ⇒ 无手孪生 Runner。读者 = 生产选路器。 */
@@ -26,6 +27,16 @@ export interface LegFacts {
26
27
  readonly skills: readonly RunSkillEntry[];
27
28
  /** 受理时读根会话行的那一读(会话 id + 住客)。读者 = 腿的会话行记录座(`session-workspace.ts`)。 */
28
29
  readonly sessionWitness: DelegationRootWitness;
30
+ /**
31
+ * 这次提交的根会话登记的工作目录(提交体 `cwd` 经唯一写口 `setSessionCwd` 登记的那一枚;装配器在登记之后读一次)。缺席 = 没登记。
32
+ * 读者 = 三只组合入口(同步 / 后台 / 续跑腿的梯子与验证门):经 `compositionLegRoot`(`task-cwd.ts`)交给它们驱动的每一条腿当交活方的工作目录。
33
+ */
34
+ readonly registeredCwd?: string;
35
+ /**
36
+ * 这条 run 继承到的项目上下文(`project-context.ts`):新提交腿在装配器头部判出的那一份,续跑腿是从续跑重建输入重放的那一份。缺席 = 没继承。
37
+ * 读者 = 提交腿写续跑重建输入的那一笔(`submit-ctx.ts` `persistSubmissionCtx` → `CheckpointCtx.projectContext`):判一次、随 run 冻结。
38
+ */
39
+ readonly projectContext?: ProjectContext;
29
40
  }
30
41
  /**
31
42
  * 铸事实(**单一属主**:只允许 `createResolveSpec` 的出口调用)。同一只对象被第二次铸 ⇒ 当场抛 —— 两个写者是漂移的成因,
package/dist/leg-facts.js CHANGED
@@ -6,6 +6,8 @@ export function mintLegFacts(resolved, facts) {
6
6
  hands: facts.hands,
7
7
  skills: Object.freeze(facts.skills.map((e) => Object.freeze({ ...e }))),
8
8
  sessionWitness: Object.freeze({ ...facts.sessionWitness }),
9
+ ...(facts.registeredCwd !== undefined ? { registeredCwd: facts.registeredCwd } : {}),
10
+ ...(facts.projectContext !== undefined ? { projectContext: Object.freeze({ ...facts.projectContext }) } : {}),
9
11
  }));
10
12
  return resolved;
11
13
  }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * **local 店对引擎的声明**(会话店顶层 · 检查点店「两半」读同一份;委派分区的声明是子代转录档位的判据,不随两半降)—— 「这台部署向谁承诺店活过替换」的唯一读者,与运维那一个词的闭集。
3
+ *
4
+ * ## 规则(一句)
5
+ * local 文件店的设计声明是「活过进程重启」(`SESSION_DURABILITY`);起服的店持久性准入(`store-admission.ts`,第 2 段、第一笔写之前)拿这句声明与数据根介质对表
6
+ * (声明 `durable` ∧ 介质 `ephemeral` ⇒ 拒启 `config.store_not_durable`;第 9 段布线审计对引擎读到的声明同判)。声明降为引擎的 `process-local` 只有两个来源,都由 {@link localStoreDeclarationOf} 一处答:
7
+ * · 运维写了 `LOCAL_STORE_DURABILITY=ephemeral`(闭集只有这一个词;缺席 = 设计声明);
8
+ * · 店落在**调用方自己的盘**上(部署形表行 `callerOwnsWorkerDisk`:host 车道 ∧ 单主)且数据根介质判 `ephemeral` —— 调用方的工作区、工具与会话史同在那块盘上,
9
+ * 部署不替他承诺他自己那块易失盘活过替换(参照实现在无卷容器里同形)。
10
+ * 两半一起降:只降会话店,引擎的 durable-park 自检读出缺口(`durableParkGapOf`:检查点活过、会话不活);只降检查点店同理。
11
+ *
12
+ * 叶模块:值导入只有部署形表(叶)与店型常量的属主;`store-posture.ts` / `store-admission.ts` / `config.ts` / local 后端各读这里,不各写一份。
13
+ */
14
+ import type { StoreDurability as EngineStoreDurability } from "@sema-agent/core";
15
+ import { type DeploymentShapeInput } from "./deployment-shape.js";
16
+ import type { StoreDurability } from "./store-posture.js";
17
+ /** `LOCAL_STORE_DURABILITY` 的词(闭集,只有「易失」一个词;缺席 = 店型的设计声明)。不给第二个放宽词 —— 声明只有「我就是易失」这一句。 */
18
+ export declare const LOCAL_STORE_DECLARATION_WORDS: readonly ["ephemeral"];
19
+ export type LocalStoreDeclarationWord = (typeof LOCAL_STORE_DECLARATION_WORDS)[number];
20
+ /**
21
+ * local 店声明的依据词(闭集;引擎声明词由 {@link engineDurabilityOfDeclarationRule} 的穷举 switch 派生 —— 加词 ⇒ 编译红):
22
+ * · `declared_ephemeral` —— 运维写了 `LOCAL_STORE_DURABILITY=ephemeral` ⇒ `process-local`(不论车道与介质:这是部署者对自己的店说的话);
23
+ * · `callers_own_volatile_disk` —— 部署形表行 `callerOwnsWorkerDisk` 成立(host 车道 ∧ 单主:店所在的盘就是调用方自己的盘 —— 他的工作区、工具与会话史同在一块盘上,
24
+ * CC 在容器里同形)且数据根介质判 `ephemeral` ⇒ `process-local`:部署不替调用方承诺他自己那块易失盘活过替换;
25
+ * · `store_design` —— 其余:local 文件店的设计声明(活过进程重启,`SESSION_DURABILITY`)。店落在调用方看不见的 worker 盘上而介质易失 ⇒ 审计拒启;
26
+ * 调用方自己的盘判 `durable` / `unknown` ⇒ 声明照旧(开发机、挂了卷的容器、非 Linux)。
27
+ */
28
+ export declare const LOCAL_STORE_DECLARATION_RULES: readonly ["declared_ephemeral", "callers_own_volatile_disk", "store_design"];
29
+ export type LocalStoreDeclarationRule = (typeof LOCAL_STORE_DECLARATION_RULES)[number];
30
+ /** 依据词 → 引擎声明词(穷举 switch)。 */
31
+ export declare function engineDurabilityOfDeclarationRule(rule: LocalStoreDeclarationRule): EngineStoreDurability;
32
+ /** local 店的声明(依据词 + 派生的引擎声明词;两半店读同一份)。 */
33
+ export interface LocalStoreDeclaration {
34
+ readonly rule: LocalStoreDeclarationRule;
35
+ readonly durability: EngineStoreDurability;
36
+ }
37
+ /** 声明读的部署事实:部署形表行 `callerOwnsWorkerDisk` 的读入形(车道 × 租户)+ 运维的词 `LOCAL_STORE_DURABILITY`(`ServiceConfig` 结构上满足)。 */
38
+ export type LocalStoreDeclarationDeployment = DeploymentShapeInput<"lane" | "principal"> & {
39
+ readonly localStoreDurability: LocalStoreDeclarationWord | undefined;
40
+ };
41
+ /**
42
+ * 🔴 「local 店对引擎声明自己活过什么」的**唯一读者**(两个调用点读同一组输入:local 后端的构造 —— 两半店的声明都从它的返回值来;起服第 2 段的店持久性准入 `store-admission.ts` —— 拒启判读同一份)。
43
+ * `dataRootMedium` = 起服第 2 段对数据根介质的判定;没有起服证据的入口(`run-local`、嵌入方直调 `createStoreBackend`)递 `unknown` —— 与装配值同一条缺证据律:
44
+ * 判不出的介质不放宽声明,只有运维的词能。
45
+ */
46
+ export declare function localStoreDeclarationOf(deployment: LocalStoreDeclarationDeployment, dataRootMedium: StoreDurability): LocalStoreDeclaration;
47
+ /** 依据词的人读形(布线审计的 warn 行与店的持久性准入的拒句用)。 */
48
+ export declare function describeLocalStoreDeclaration(d: LocalStoreDeclaration): string;
49
+ /**
50
+ * `LOCAL_STORE_DURABILITY` 的读法(配置装载调这一只):缺席 / 空串 ⇒ `undefined`(设计声明);恰为闭集里的词(去首尾空白后逐字)⇒ 那个词;
51
+ * 别的任何值 ⇒ 抛(拒启)—— 一个读不懂的声明不许被折成「持久」或「易失」中的任何一边。
52
+ */
53
+ export declare function parseLocalStoreDurabilityWord(raw: string | undefined): LocalStoreDeclarationWord | undefined;
54
+ //# sourceMappingURL=local-store-declaration.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
2
+ import { SESSION_DURABILITY } from "./plugins/session-placement.js";
3
+ export const LOCAL_STORE_DECLARATION_WORDS = ["ephemeral"];
4
+ export const LOCAL_STORE_DECLARATION_RULES = ["declared_ephemeral", "callers_own_volatile_disk", "store_design"];
5
+ export function engineDurabilityOfDeclarationRule(rule) {
6
+ switch (rule) {
7
+ case "declared_ephemeral":
8
+ case "callers_own_volatile_disk":
9
+ return "process-local";
10
+ case "store_design":
11
+ return SESSION_DURABILITY;
12
+ default: {
13
+ const unhandled = rule;
14
+ throw new Error(`store posture: no ruling for local store declaration rule ${String(unhandled)}`);
15
+ }
16
+ }
17
+ }
18
+ export function localStoreDeclarationOf(deployment, dataRootMedium) {
19
+ const of = (rule) => ({ rule, durability: engineDurabilityOfDeclarationRule(rule) });
20
+ const word = deployment.localStoreDurability;
21
+ switch (word) {
22
+ case "ephemeral":
23
+ return of("declared_ephemeral");
24
+ case undefined:
25
+ if (dataRootMedium === "ephemeral" && DEPLOYMENT_SHAPE_PREDICATES.callerOwnsWorkerDisk.holds(deployment))
26
+ return of("callers_own_volatile_disk");
27
+ return of("store_design");
28
+ default: {
29
+ const unhandled = word;
30
+ throw new Error(`unhandled LOCAL_STORE_DURABILITY word ${JSON.stringify(unhandled)}`);
31
+ }
32
+ }
33
+ }
34
+ export function describeLocalStoreDeclaration(d) {
35
+ switch (d.rule) {
36
+ case "declared_ephemeral":
37
+ return "the operator declared LOCAL_STORE_DURABILITY=ephemeral";
38
+ case "callers_own_volatile_disk":
39
+ return "the store sits on the caller's own disk (host execution lane, single owner), and that disk does not survive a container / Pod replacement";
40
+ case "store_design":
41
+ return "the local file store's own declaration";
42
+ default: {
43
+ const unhandled = d.rule;
44
+ throw new Error(`store posture: no wording for local store declaration rule ${String(unhandled)}`);
45
+ }
46
+ }
47
+ }
48
+ export function parseLocalStoreDurabilityWord(raw) {
49
+ const w = raw?.trim() ?? "";
50
+ if (w === "")
51
+ return undefined;
52
+ const word = LOCAL_STORE_DECLARATION_WORDS.find((x) => x === w);
53
+ if (word !== undefined)
54
+ return word;
55
+ throw new Error(`LOCAL_STORE_DURABILITY=${JSON.stringify(raw)} is not a recognized declaration — the only word is ${LOCAL_STORE_DECLARATION_WORDS.map((x) => `"${x}"`).join(", ")} ` +
56
+ `(the local store declares itself process-local: sessions, parked approvals and memory do not survive a container / Pod replacement). ` +
57
+ `Leave it unset for the store's own durable declaration. Refusing to boot rather than reading a declaration nobody can read as either one.`);
58
+ }
59
+ //# sourceMappingURL=local-store-declaration.js.map
@@ -5,7 +5,7 @@
5
5
  * 请求面条目的形 = 契约包的请求传输形({@link RequestMcpServerSpec}:stdio 带 `env`、http 带 `headers` / `principalHeader` / `assertionHeader`),wire 上的形由它说。
6
6
  * 交给引擎的声明不带件值:引擎的声明收据对带 `headers` / `env` 的声明整条拒,件由部署的对端座在发送时作答。所以每条条目按它那一臂的处置表
7
7
  * ({@link MCP_STDIO_TRANSPORT_DISPOSITION} / {@link MCP_HTTP_TRANSPORT_DISPOSITION})拆成草稿:`env` / `headers` 进草稿的件,`assertionHeader` 承载不发送,其余进声明。
8
- * 拆法只有 `plain-string-record.ts` 的 `splitDeclaration` 一只、键的去处只有表一处说;两张表各带三道编译格(请求臂加 / 减键、引擎臂加键、引擎臂删键都编译红)。
8
+ * 拆法只有 `request-declaration-disposition.ts` 的 `splitDeclaration` 一只、键的去处只有表一处说;两张表各带三道编译格(请求臂加 / 减键、引擎臂加键、引擎臂删键都编译红)。
9
9
  * 草稿只有胜出者(部署同名者赢,`task-mcp.ts` 的 `mergeRequestMcp`)才经对端座的唯一铸点(`peer-seat.ts` 的 `mintDeclaration`)登记,登记换来的引用放进声明的传输臂。
10
10
  *
11
11
  * ## `assertionHeader`:一条律,一个键一种读法
@@ -1,4 +1,4 @@
1
- import { splitDeclaration } from "./plain-string-record.js";
1
+ import { splitDeclaration } from "./request-declaration-disposition.js";
2
2
  export const MCP_STDIO_TRANSPORT_DISPOSITION = { args: "engine", command: "engine", env: "seat", kind: "engine" };
3
3
  export const MCP_HTTP_TRANSPORT_DISPOSITION = { assertionHeader: "withheld", headers: "seat", kind: "engine", principalHeader: "engine", url: "engine" };
4
4
  const MCP_REQUEST_TRANSPORT_ARMS = {
@@ -1,14 +1,19 @@
1
1
  /**
2
- * MCP 授权事实(core 7.59.0 `McpAuthorization`:`{ reason, authorizeUrl?, resourceMetadataUrl? }`,与 `errorCode: "unauthorized"` 同行)的**出门投影** ——
3
- * 本仓三张面(`wiring_manifest.mcp[]` 行、重拨回执的 `status`、`//mcp` 面板的 `servers[]`)同读这一只。
2
+ * MCP 授权记录(core 7.59.0 `McpAuthorization`:`{ reason, authorizeUrl?, resourceMetadataUrl? }`,与 `errorCode: "unauthorized"` 同在)的**出门投影** ——
3
+ * 一条记录无论坐在哪个投影载体里,离开本服务只经这一张处置表、这一只投影。五个投影点同读它:状态行投影(`wiring_manifest.mcp[]` 行;账本帧、面板的
4
+ * `lastLegMcp.mcp[]`、能力面两口读的都是它的产物)、重拨回执的 `status`、会话 MCP 面板的 `servers[]`、通告 `mcp.authorization_required` 的 `detail`
5
+ * (登记 `NOTICE_DETAIL_RECORDS`,`trace/engine-notice-wire.ts`)、会话历史读面里工具结果卡的 `details.authorization`(登记 `TOOL_CARD_RECORDS`,`trace/tool-card.ts`)。
6
+ * (工具卡的读者 `trace/tool-card.ts` 是后者的那一只:活面 `tool_end.structured` 上挂着授权记录的自注册卡同读。)记录成员是**结构位**:只经本表出门,
7
+ * 不过自由文本的脱敏、深扫与截断。会话同步拉取口不是投影:它把存档逐字复制给属主,存档里带不带远端的地址由引擎的写入决定。
4
8
  *
5
- * 只投闭集词 `reason`(`rejected` = 端点答 401 且授权座的重问没给出能过的凭据 / `scope` = 403 `insufficient_scope` / `none` = 授权座答「未授权」):
6
- * 判据直读引擎的 `MCP_AUTHORIZATION_REASONS`(不抄词表);词出闭集、非串、整只不是对象 ⇒ 整格缺席(最弱断言,不铸词)。
7
- * 两只地址**不出门**:`authorizeUrl` 只来自部署的授权座,本部署今天没有授权座;`resourceMetadataUrl` 是远端在挑战头里自报的地址,不可信 —— 两只地址的去向
8
- * (授权座 + 出口座)是授权记录面的设计,不在本投影里放行。用户面的「去授权」由会话通告 `mcp.authorization_required` 承载。
9
+ * `reason` 是闭集词(`rejected` = 端点答 401 且对端座的重问没给出能过的凭据 / `scope` = 403 `insufficient_scope` / `none` = 对端座答「未授权」):
10
+ * 判据直读引擎的 `MCP_AUTHORIZATION_REASONS`(不抄词表);词出闭集、非串、整只不是对象 ⇒ 整条记录缺席(最弱断言,不铸词)。
11
+ * 两只地址今天不出门:`authorizeUrl` 只来自对端座 —— 座在,但今天没有会答需授权的凭据来源(地址的唯一入口与它的判法随那一臂立);
12
+ * `resourceMetadataUrl` 是远端在挑战头里自报的地址(终态 `strip`:没有消费方需要它 —— 客户端不跑授权发现;摆在「去授权」旁边的远端地址是钓鱼面)。
9
13
  */
10
- import { type McpAuthorizationReason } from "@sema-agent/core";
11
- /** 授权记录的处置表(三面同读;`core-keyset-guard.ts` 读表 —— 门与投影一个写者):`reason` 认得才出门(闭集词;在场而认不得 ⇒ 丢并计一次),两只地址本面不出现。 */
14
+ import { type McpAuthorization, type McpAuthorizationReason } from "@sema-agent/core";
15
+ import { type FaceOf } from "./trace/projection-table.js";
16
+ /** 授权记录的处置表(五个投影点同读;`core-keyset-guard.ts` 读表 —— 门与投影一个写者):`reason` 认得才出门(闭集词;在场而认不得 ⇒ 丢并计一次),两只地址不出门。 */
12
17
  export declare const MCP_AUTHORIZATION_DISPOSITIONS: {
13
18
  readonly reason: {
14
19
  readonly forward: (w: unknown) => w is McpAuthorizationReason;
@@ -16,12 +21,11 @@ export declare const MCP_AUTHORIZATION_DISPOSITIONS: {
16
21
  readonly authorizeUrl: "strip";
17
22
  readonly resourceMetadataUrl: "strip";
18
23
  };
19
- /** 处置表一格的放行谓词所窄化到的型(`forward: (w: unknown) => w is T` ⇒ `T`):表放行什么,出门值就是什么型。 */
20
- type ForwardedAs<F> = F extends (w: unknown) => w is infer T ? T : never;
21
- /** 本投影的产物 = 表里 `reason` 一格放行后的型。词的型只有表这一个写者,出口不再判第二遍。 */
22
- export type McpAuthorizationFace = {
23
- readonly reason: ForwardedAs<(typeof MCP_AUTHORIZATION_DISPOSITIONS)["reason"]["forward"]>;
24
- };
24
+ /** 记录的成员名 = 表的键(冻结拷贝;成员集只有表一处写)。平铺载体(通告 `detail`)按它认哪几位是这条记录。 */
25
+ export declare const MCP_AUTHORIZATION_MEMBERS: readonly string[];
26
+ type TableFace = FaceOf<McpAuthorization, typeof MCP_AUTHORIZATION_DISPOSITIONS>;
27
+ /** 本投影的产物 = 表的出门型({@link FaceOf}:表翻一格,型随之变);记录级规则「`reason` 在 ⇔ 记录在」在出口上,所以 `reason` 必在。 */
28
+ export type McpAuthorizationFace = TableFace & Required<Pick<TableFace, "reason">>;
25
29
  export declare function projectMcpAuthorization(v: unknown): McpAuthorizationFace | undefined;
26
30
  export {};
27
31
  //# sourceMappingURL=mcp-authorization-face.d.ts.map
@@ -6,10 +6,11 @@ export const MCP_AUTHORIZATION_DISPOSITIONS = {
6
6
  authorizeUrl: "strip",
7
7
  resourceMetadataUrl: "strip",
8
8
  };
9
+ export const MCP_AUTHORIZATION_MEMBERS = Object.freeze(Object.keys(MCP_AUTHORIZATION_DISPOSITIONS));
9
10
  export function projectMcpAuthorization(v) {
10
11
  if (typeof v !== "object" || v === null || Array.isArray(v))
11
12
  return undefined;
12
- const { reason } = projectByTable("mcp.authorization", MCP_AUTHORIZATION_DISPOSITIONS, v, undefined);
13
- return reason !== undefined ? { reason } : undefined;
13
+ const face = projectByTable("mcp.authorization", MCP_AUTHORIZATION_DISPOSITIONS, v, undefined);
14
+ return face.reason === undefined ? undefined : { ...face, reason: face.reason };
14
15
  }
15
16
  //# sourceMappingURL=mcp-authorization-face.js.map
@@ -29,7 +29,7 @@ export function createMemoryProjectLayers(deps) {
29
29
  };
30
30
  return {
31
31
  async prepare(config, scope, taskRoot) {
32
- const layer = memoryProjectLayerFor(config, scope, taskRoot);
32
+ const layer = await memoryProjectLayerFor(config, scope, taskRoot);
33
33
  const current = layer?.key.scope;
34
34
  if (layer === undefined || current === undefined)
35
35
  return layer?.key;
@@ -62,8 +62,8 @@ export declare function memoryScopeFor(config: ServiceConfig, principal?: string
62
62
  * 读不到(根在沙箱/设备盘上,或 host 车道会话没登记 cwd、spec 期还不知道根)⇒ 调用方传 `undefined` ⇒ `{ absent: "unreadable" }`,
63
63
  * core 铸 operator 通知 `memory.project_scope_absent`,写照常落全局层 —— **响亮**,不是静默落 `local`。
64
64
  *
65
- * 🔴 [ref] 键形:项目层 = **全局层的属主 × 项目身份**,`userrepo:<属主段>/<档>.<摘要>`(文法 `project-layer-key.ts`,身份 `project-identity.ts`);
66
- * 属主 = `projectLayerOwnerOf(scope)`。
65
+ * 🔴 [ref] 键形:项目层 = **全局层的属主 × 项目身份**,`userrepo:<属主段>/<档>.<摘要>`(文法 `project-layer-key.ts`,身份 `project-identity.ts`
66
+ * —— 检出带 `.sema/project` 记号 ⇒ 记号档,读法与引擎判官同一只;没有记号 ⇒ git 根回落);属主 = `projectLayerOwnerOf(scope)`。
67
67
  *
68
68
  * 🔴 **写家只落复制路径覆盖得到的层**(7.98.0 合并复审 F1,主会话裁「统一规则,不做特判」):写家 = 读列表里、本部署
69
69
  * **每一条已配置的复制路径都覆盖到**的最具体的一层。项目层不在覆盖面里 ⇒ 本任务**不按项目键控**,写家落全局层 —— 否则开了
@@ -77,9 +77,9 @@ export declare function memoryScopeFor(config: ServiceConfig, principal?: string
77
77
  * · 记忆面暗(`scope` 缺席)—— 没有 spec 可铸,也就不读盘;
78
78
  * · 本任务的项目层不在复制覆盖面里(见上)—— 让位给全局层。
79
79
  */
80
- export declare function memoryProjectKeyFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): MemoryProjectKey | undefined;
80
+ export declare function memoryProjectKeyFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): Promise<MemoryProjectKey | undefined>;
81
81
  /** {@link memoryProjectKeyFor} 的同一条规则,带上本 checkout 要折进当前层的旧层(`memory-project-layer.ts` 读它)。 */
82
- export declare function memoryProjectLayerFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): ProjectLayerOfTask | undefined;
82
+ export declare function memoryProjectLayerFor(config: Pick<ServiceConfig, "requirePrincipal" | "memorySync">, scope: string | undefined, taskRoot: string | undefined): Promise<ProjectLayerOfTask | undefined>;
83
83
  /**
84
84
  * 本部署记忆的**复制覆盖面** —— 唯一属主(7.98.0 合并复审 F1)。「复制路径」= 把某一层记忆搬出本机的腿;今天只有一条:
85
85
  * TOC 同步客户端腿(`MEMORY_SYNC_*`,`memory-sync-client.ts`)。🔴 [ref]:一条复制路径复制**它那一层**(`config.memorySync.scope`)
@@ -34,13 +34,13 @@ export function memoryScopeFor(config, principal, projectId) {
34
34
  }
35
35
  return config.memoryScope;
36
36
  }
37
- export function memoryProjectKeyFor(config, scope, taskRoot) {
38
- return memoryProjectLayerFor(config, scope, taskRoot)?.key;
37
+ export async function memoryProjectKeyFor(config, scope, taskRoot) {
38
+ return (await memoryProjectLayerFor(config, scope, taskRoot))?.key;
39
39
  }
40
- export function memoryProjectLayerFor(config, scope, taskRoot) {
40
+ export async function memoryProjectLayerFor(config, scope, taskRoot) {
41
41
  if (config.requirePrincipal === true || scope === undefined)
42
42
  return undefined;
43
- const layer = projectLayerForTaskRoot(taskRoot, projectLayerOwnerOf(scope));
43
+ const layer = await projectLayerForTaskRoot(taskRoot, projectLayerOwnerOf(scope));
44
44
  if (layer.key.scope !== undefined && !replicationCovers(memoryReplicationCoverage(config), layer.key.scope))
45
45
  return undefined;
46
46
  return layer;
@@ -30,7 +30,7 @@
30
30
  * 找码;这一半候 core 的结构化码位(登记在案,codex 一审 C3)。
31
31
  *
32
32
  * ## 不是这一族的形(core 同表「different surface」,本仓**不**自拼码,如实走各自既有臂;今两形 + 一只已退的形)
33
- * · journal 重放目标目录成链 ⇒ `ControlPlaneCorruptError`(无 `.code`,KL-1094-b)⇒ 路由 typed 500 `internal.memory_control_plane_corrupt`
33
+ * · journal 重放目标目录成链 ⇒ `ControlPlaneCorruptError`(无 `.code`,[ref])⇒ 路由 typed 500 `internal.memory_control_plane_corrupt`
34
34
  * (core 7.44.0 起 scope home 非目录**不再**走这一类 —— 它有了自己的码,见上;`ControlPlaneCorruptError` 只剩真控制面损坏);
35
35
  * · (已不存在的一形)引擎原地写撞硬链(nlink>1):≤ 引擎 7.55.x 是无码文本 `has N names (a hard link) … not written`、走该写点既有的失败车道;
36
36
  * 引擎 7.56.0 起那只写给**这个名字**另建一个新文件(原子改名落位,其余名字保留原文件),拒绝与那句文本一并消失 —— 本仓从未读过那句文本,零码;
@@ -3,7 +3,7 @@ import { readFile, readdir, rename, rmdir, stat } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { carriesCredential, ensureDir, syncMemoryScope, } from "@sema-agent/core";
5
5
  import { replicationCovers } from "./memory-scope.js";
6
- import { parseProjectLayerKey } from "./project-layer-key.js";
6
+ import { compareProjectLayersForSync, parseProjectLayerKey } from "./project-layer-key.js";
7
7
  import { atomicWriteFileAsync } from "./plugins/owner-only-file-mode.js";
8
8
  import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
9
9
  import { buildMemorySyncIdentityBootRefusal, readMemorySyncIdentity } from "./memory-sync-identity.js";
@@ -272,7 +272,7 @@ export function createMemorySyncRunner(opts) {
272
272
  opts.log.warn("memory_sync_layer_enumeration_unsupported", { scope: opts.scope, reason: local.reason, note: "only the sync layer and tracked project layers are synced" });
273
273
  }
274
274
  const projectLayers = new Set([...(local.supported ? local.scopes : []), ...tracked].filter(isCoveredProjectLayer));
275
- return [opts.scope, ...[...projectLayers].sort()];
275
+ return [opts.scope, ...[...projectLayers].sort(compareProjectLayersForSync)];
276
276
  };
277
277
  const syncLayer = async (scope, reason, calls) => {
278
278
  const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
@@ -1,18 +1,57 @@
1
1
  /**
2
- * The window this server ASSUMES for a model whose context window nobody declared — ONE spelling of the number.
2
+ * A model's context window: ONE resolver, ONE spelling of the assumed number, ONE word for where the window came from.
3
3
  *
4
- * Three readers fall back to it and nothing else may carry a copy: the env leg (`config.ts`: `MODEL_CONTEXT_WINDOW` unset ⇒ this value,
5
- * and the `MODEL_MAX_TOKENS` default `min(32000, window)` reads the SAME resolved window), the center leg (`web-admin-client/apply-effective.ts`:
6
- * `entry.contextWindow ?? env model's window ?? this value`) and the config catalog's default note (`config-catalog.ts`). A literal copied
7
- * into any of them is a second source that drifts the day the assumption changes — a center roster would then assume one window while
8
- * the env model assumes another, and the operator-facing default note would name a third.
4
+ * ## The rule
5
+ * A window is either **declared** — someone along the model's inheritance chain wrote a number: the catalog entry's `contextWindow`,
6
+ * the deployment's `MODEL_CONTEXT_WINDOW` (inherited by every catalog entry that writes none, BL-8), or `MODEL_CHEAP_CONTEXT_WINDOW`
7
+ * (the cheap slot; unset ⇒ it inherits the main slot's resolution) — or **assumed**: nobody along the chain wrote one, and this server
8
+ * fills {@link ASSUMED_CONTEXT_WINDOW}. {@link resolveContextWindow} is the only place that decides; it hands back the number AND its
9
+ * source as one value, and an inheriting reader passes the parent's resolution down whole — so the source travels with the number and
10
+ * no reader re-derives "was it declared?" on its own.
9
11
  *
10
- * This is a conservative CEILING GUESS, not a measurement: the engine needs a window to size its compaction trigger and the tool-schema
11
- * defer threshold, and an undeclared window must never be read optimistically — so every model that reaches the engine carries SOME window,
12
- * declared or this one (a catalog entry may omit `contextWindow`; the readers above fill it). Whether the value is conservative for a
13
- * given model is a per-model question this constant does not answer.
12
+ * Readers: the env leg (`config.ts`: main slot, cheap slot inheriting the main resolution), the center leg (`web-admin-client/apply-effective.ts`:
13
+ * entry → env main resolution), and the config catalog's default note (`config-catalog.ts`, which describes the assumed arm and so names the constant).
14
+ * The source is disclosed twice and read from the same per-model table both times (`ServiceConfig.contextWindowSources`): the `GET /v1/models`
15
+ * row key `contextWindowSource`, and the operator line {@link buildContextWindowAssumedNotice} (once per served model whose window is assumed; the one
16
+ * emitter is `context-window-notice.ts`, run after the served table settles).
17
+ *
18
+ * ## The assumed number
19
+ * A conservative guess, not a measurement: the engine needs a window to size its compaction trigger and the tool-schema defer
20
+ * threshold, and an undeclared window must never be read optimistically — so every model that reaches the engine carries SOME window,
21
+ * declared or this one. "Conservative" has one meaning here: the number is no larger than the smallest real window any model family in
22
+ * this stack's own deployments is known to declare, so an undeclared window errs toward "compaction and tool-schema deferral start
23
+ * earlier than the model needs", not toward "the model reaches its limit before compaction starts". It is ONE number, not a table keyed
24
+ * by model family — a per-family table would be a second source for the same fact. A model whose real window is larger declares it
25
+ * (catalog entry `contextWindow` / `MODEL_CONTEXT_WINDOW`); the disclosure above is how an operator / client learns which rows still
26
+ * ride on this number.
14
27
  *
15
28
  * Pure leaf: no imports.
16
29
  */
17
- export declare const ASSUMED_CONTEXT_WINDOW = 262144;
30
+ export declare const ASSUMED_CONTEXT_WINDOW = 131072;
31
+ /** Where a model's window came from — closed set; consumers switch exhaustively. */
32
+ export declare const CONTEXT_WINDOW_SOURCES: readonly ["declared", "assumed"];
33
+ export type ContextWindowSource = (typeof CONTEXT_WINDOW_SOURCES)[number];
34
+ /** A resolved window: the number the engine gets and where it came from (one value — never split across two reads). */
35
+ export interface ResolvedContextWindow {
36
+ readonly window: number;
37
+ readonly source: ContextWindowSource;
38
+ }
39
+ /**
40
+ * The one resolver. `declared` = the number this level of the chain wrote (`undefined` = wrote none); `inherited` = the parent level's
41
+ * resolution (omitted at the root ⇒ the assumed arm). Declared ⇒ that number, `declared`; otherwise the parent's resolution unchanged —
42
+ * a catalog entry that writes no window under a deployment that declared `MODEL_CONTEXT_WINDOW` is declared, under one that did not it is assumed.
43
+ */
44
+ export declare function resolveContextWindow(declared: number | undefined, inherited?: ResolvedContextWindow): ResolvedContextWindow;
45
+ /**
46
+ * The operator line for one served model whose window is assumed (plain data; both legs emit this shape, the event name lives here only).
47
+ * `declareAt` names where the operator writes the number to make it declared (the env knob for an env slot, the catalog entry otherwise).
48
+ */
49
+ export declare function buildContextWindowAssumedNotice(row: {
50
+ readonly model: string;
51
+ readonly window: number;
52
+ readonly declareAt: string;
53
+ }): {
54
+ readonly event: "model_context_window_assumed";
55
+ readonly fields: Record<string, string>;
56
+ };
18
57
  //# sourceMappingURL=model-context-window.d.ts.map
@@ -1,2 +1,21 @@
1
- export const ASSUMED_CONTEXT_WINDOW = 262144;
1
+ export const ASSUMED_CONTEXT_WINDOW = 131072;
2
+ export const CONTEXT_WINDOW_SOURCES = ["declared", "assumed"];
3
+ const ASSUMED = Object.freeze({ window: ASSUMED_CONTEXT_WINDOW, source: "assumed" });
4
+ export function resolveContextWindow(declared, inherited = ASSUMED) {
5
+ return declared !== undefined ? { window: declared, source: "declared" } : inherited;
6
+ }
7
+ export function buildContextWindowAssumedNotice(row) {
8
+ return {
9
+ event: "model_context_window_assumed",
10
+ fields: {
11
+ model: row.model,
12
+ contextWindow: String(row.window),
13
+ declareAt: row.declareAt,
14
+ note: `no context window is declared for model "${row.model}" — this server assumes ${row.window} tokens (a conservative guess, not a measurement): ` +
15
+ `the engine sizes its compaction trigger on it, so a model whose real window is smaller reaches its limit before compaction starts, ` +
16
+ `and a model whose real window is larger compacts earlier than it needs to. ` +
17
+ `GET /v1/models marks this row contextWindowSource:"assumed". Declare the real window at ${row.declareAt} — said once per model.`,
18
+ },
19
+ };
20
+ }
2
21
  //# sourceMappingURL=model-context-window.js.map
@@ -58,7 +58,7 @@ export type CatalogEntryVerdict = {
58
58
  };
59
59
  /**
60
60
  * **逐条判据**:`ok` = 本部署能完整服务它(连同判出它的那一份读数)。判序即语义:先形(不合形的条目谈不上走哪条腿),再腿。
61
- * 形分两段、同一个码(`config.model_entry_invalid`):settings-schema 的 `ModelEntry`,再是 [ref] 的 `headers`(白名单 + 线路 + 值形,
61
+ * 形分三段、同一个码(`config.model_entry_invalid`):settings-schema 的 `ModelEntry`,再是 [ref] 的名字(凭据形的名不收,见 {@link credentialNameRefusal}),再是 [ref] 的 `headers`(白名单 + 线路 + 值形,
62
62
  * 判据单属主 `model-entry-headers.ts`;settings-schema 6.3.0 的 `ModelEntry` 还没有这个键 —— 候裁加字段,届时词表不另抄)。
63
63
  *
64
64
  * 形判的属主是 settings-schema 的 `ModelEntry`(两条读腿用的就是它)—— 本仓不手抄任何一条字段规则;这里在第二道边界(绕过读腿的
@@ -114,9 +114,9 @@ export interface ModelRefusalVerdict {
114
114
  /**
115
115
  * [ref] —— 判决 → `/health.modelRefusals` 与 fleet 心跳 `modelRefusals` 的 wire 形(settings-schema `ModelRefusalsSignal`,纯投影):
116
116
  * 名 → 码,**值只码**(拒因事实里的上游 id / provider 词 / 坏字段键 / 出路句都不上 wire —— `entry_invalid` 行是没人验过的行,id / provider
117
- * 可能就是坏行原样;句子是给本机运维的散文,不是契约)。**名筛在这里、只在这里**:键过 settings-schema `isReportableRefusalName`(唯一判据,
118
- * import 不复制;6.3.0 起 = 标识符白名单 ∧ 无密钥形 ∧ 无 URL userinfo)—— 秘密形 / 空串 / `__proto__` / 不是标识符的串这类出不了门的名不进键、
119
- * 不计数;引擎内部的判决不受影响;本机日志 / 拒因句里的写名同样先问这一只([ref],{@link disclosableRefusalName}:不可披露的名只写码)。
117
+ * 可能就是坏行原样;句子是给本机运维的散文,不是契约)。**名筛在这里、只在这里**:键过 {@link disclosableRefusalName}([ref] 起 wire 出口与日志 / 拒因句同一个口:
118
+ * settings-schema `isReportableRefusalName`〔6.3.0 起 = 标识符白名单 ∧ 无密钥形 ∧ 无 URL userinfo,import 不复制〕∧ 脱敏器的强证据臂不认它是凭据)—— 秘密形 /
119
+ * 空串 / `__proto__` / 不是标识符的串这类出不了门的名不进键、不计数;引擎内部的判决不受影响;本机日志 / 拒因句里的写名同样先问这一个口([ref]:不可披露的名只写码)。
120
120
  * 逐条空且无组拒 ⇒ `undefined`(键缺席:「没有」只有一种拼法)。
121
121
  */
122
122
  export declare function buildModelRefusalsSignal(v: ModelRefusalVerdict | undefined): ModelRefusalsSignal | undefined;
@@ -144,6 +144,7 @@ export declare function echoModelRef(ref: string): string;
144
144
  * hook 选模型的失败文案 —— 各处同一句)。`field` = 调用方的通道名(`model` / `compactionModel` / `agents[0].model` /
145
145
  * `@-mention`),`requested` = 调用方发来的**原形**(name 或 id;本函数回显它时过 {@link echoModelRef},调用方不预先截)。
146
146
  * 别名段(`(catalog entry …)`)的判断读原形:ref 与条目名逐字相同 ⇒ 不带,不因回显截短而误判成「另有条目」。
147
+ * 别名段里的条目名过 {@link nameSlot}([ref]):调用方按上游 id 点中一条名字是凭据形的条目时,名字是**配置里的字**、不是调用方写的字,不可披露的名不原样回给他。
147
148
  */
148
149
  export declare function modelUnavailableMessage(field: string, requested: string, name: string, r: ModelEntryRefusal): string;
149
150
  /**
@@ -4,6 +4,7 @@ import { catalogBrainLane } from "./model-provider.js";
4
4
  import { readCatalogEntryHeaders } from "./model-entry-headers.js";
5
5
  import { DroppedCatalogEntryRow, describeSchemaIssueFields, describeSchemaIssues } from "./web-admin-client/read-warnings.js";
6
6
  import { echoCallerText } from "./caller-text-echo.js";
7
+ import { isCredentialShapedUnit } from "./trace/redact.js";
7
8
  import { DEFAULT_MODEL_REF } from "./model-select.js";
8
9
  export const ENTRY_REFUSAL_REASON_OF = {
9
10
  "config.model_no_brain": "no_brain",
@@ -29,6 +30,8 @@ export function judgeCatalogEntry(m, ctx) {
29
30
  return { ok: false, refusal: entryInvalidRefusal(typeof m.id === "string" ? m.id : "", typeof m.provider === "string" ? m.provider : "", fields, describeSchemaIssues(shape.error) ?? fields.join(", ")) };
30
31
  }
31
32
  const e = shape.data;
33
+ if (isCredentialShapedUnit(e.name))
34
+ return { ok: false, refusal: credentialNameRefusal(e.id, e.provider) };
32
35
  const headers = readCatalogEntryHeaders(e.headers, e.api);
33
36
  if (!headers.ok)
34
37
  return { ok: false, refusal: entryInvalidRefusal(e.id, e.provider, headers.fields, headers.issue) };
@@ -74,6 +77,17 @@ function entryInvalidRefusal(id, provider, fields, issues) {
74
77
  "deployment's catalog on its own (the other entries keep serving) instead of being served with a value nothing validated; fix the entry to bring it back",
75
78
  };
76
79
  }
80
+ function credentialNameRefusal(id, provider) {
81
+ return {
82
+ ...refusalCause("config.model_entry_invalid"),
83
+ id,
84
+ provider,
85
+ fields: ["name"],
86
+ detail: "the catalog entry's name field is credential-shaped (the engine's secret scanner reads it as a credential) — an entry name is returned verbatim by GET /v1/models " +
87
+ "and serves as the @-mention handle, so the entry is not served rather than served under a name that every log line has to mask; rename the entry to a plain identifier " +
88
+ "to bring it back, and treat the credential as exposed if the name was pasted from a real one",
89
+ };
90
+ }
77
91
  function droppedRowEnabled(row) {
78
92
  return row.identity?.enabled !== false;
79
93
  }
@@ -116,13 +130,13 @@ export function judgeCatalogEntries(enabled, ctx, dropped) {
116
130
  export function buildModelRefusalsSignal(v) {
117
131
  if (v === undefined)
118
132
  return undefined;
119
- const entries = Object.fromEntries(Object.entries(v.entries).flatMap(([name, r]) => (isReportableRefusalName(name) ? [[name, r.code]] : [])));
133
+ const entries = Object.fromEntries(Object.entries(v.entries).flatMap(([name, r]) => (disclosableRefusalName(name) !== undefined ? [[name, r.code]] : [])));
120
134
  if (Object.keys(entries).length === 0 && v.plane === undefined)
121
135
  return undefined;
122
136
  return v.plane === undefined ? { entries } : { entries, plane: v.plane };
123
137
  }
124
138
  function disclosableRefusalName(name) {
125
- return isReportableRefusalName(name) ? name : undefined;
139
+ return isReportableRefusalName(name) && !isCredentialShapedUnit(name) ? name : undefined;
126
140
  }
127
141
  export function refusedNameLogField(key, name) {
128
142
  const shown = disclosableRefusalName(name);
@@ -159,7 +173,7 @@ export function echoModelRef(ref) {
159
173
  return echoCallerText(ref, { maxChars: MODEL_REF_ECHO_CHARS });
160
174
  }
161
175
  export function modelUnavailableMessage(field, requested, name, r) {
162
- const alias = requested === name ? "" : ` (catalog entry "${name}")`;
176
+ const alias = requested === name ? "" : ` (catalog entry ${nameSlot(name)})`;
163
177
  return `${field} "${echoModelRef(requested)}"${alias} is published in the catalog but unavailable on this deployment [${r.code}] — ${r.detail}`;
164
178
  }
165
179
  export function modelNotAllowedMessage(field, requested, resolved) {
@@ -4,7 +4,8 @@
4
4
  * 两层,一只 `AsyncLocalStorage`:
5
5
  * · **腿层**(`withModelReachScope`,三条带用户面投递口的 run 腿各包一次:流式 `POST /v1/tasks/stream`、后台 `POST /v1/runs`、续跑):
6
6
  * 带着这条腿的两枚身份 —— `sessionId`(通告归属键)与 `taskId`(这条腿的 run 资源 id,= 本腿交给引擎的 `spec.taskId`;7.121.0)——
7
- * 与本腿已经说过的目标集合(去重单位 = 每腿每目标一次);
7
+ * 与本腿已经说过的目标集合(去重单位 = 每腿每目标一次);腿层的 `sessionId` 同时是读拒字节地板通告的归属键
8
+ * (`read-deny-floor-notice.ts` 的 `presentEngineNotice`:腿派出去的 run —— cascade 的级、验证器、子代 —— 的那一帧落在这条腿的流上);
8
9
  * · **调用层**(`createModelReachBrain` 每次模型调用包一次):一只可变的 {@link ModelCallReach},出网观察者
9
10
  * (`outbound-dispatcher.ts` 的 undici 诊断通道订阅)把这次调用发出的每一条请求的**结局**记在上面 —— 最后一条是
10
11
  * 「连上了、有了回答」还是「建连就失败了」。
@@ -41,7 +41,7 @@ export declare function createCorruptReadFailOpenSeat(tag: FailOpenTag, leg?: (i
41
41
  * 这一座里有安全轴的脸(session-policy),所以每命中一行 warn 保留(降成「每进程一次」是降级,[ref] 的裁定);**在此之上**,认出的脸另记账:
42
42
  * · session-policy 脸 ⇒ R 类 tag(`server.session-policy.corrupt-read-fail-open`)—— 计数逐次 + 探针行 + 一次性 warn。core 7.39.2 起按键读拒;
43
43
  * core 7.41.0 [ref] 起本会话的枚举拒、别的会话的抹除不动它、重绑带走它 —— 但只对**有归属**的坏行(行里的 `__sid`,否则同名高水位标记的 `__sid`);
44
- * 无从归属的坏行仍按「无规则」(core 已知限制 KL-1299-a 接受:无从归属 ≠ 本会话);座上分不出读法 ⇒ 这一脸每一发都计(含已拒的)。
44
+ * 无从归属的坏行仍按「无规则」(core 已知限制 [ref] 接受:无从归属 ≠ 本会话);座上分不出读法 ⇒ 这一脸每一发都计(含已拒的)。
45
45
  * 抹除扫走无归属坏行那一形 core 是否披露、座上数不数得到,未亲核。放行是 core 的裁定、本仓不包层(源头修复纪律),所以**只记账、不改放行**(R 类:有界放行,计数长期保留);
46
46
  * · 其余脸 ⇒ 不计(见 {@link DurableAbsenceFace})。
47
47
  * 记账**先于** warn:记账是总函数,warn 可能抛(core 的 guard 会吞掉那个抛),一次日志故障不许吃掉计数。