@sema-agent/core 5.19.0 → 5.21.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 (329) hide show
  1. package/CHANGELOG.md +102 -4
  2. package/dist/agents/agent-definition.d.ts +7 -0
  3. package/dist/agents/agent-transcript-tool.d.ts +30 -0
  4. package/dist/agents/builtin-agents.d.ts +60 -0
  5. package/dist/agents/cascade.d.ts +107 -0
  6. package/dist/agents/cumulative-stats.d.ts +56 -0
  7. package/dist/agents/observer.d.ts +162 -0
  8. package/dist/agents/peer-admission.d.ts +95 -0
  9. package/dist/agents/repair-loop.d.ts +226 -0
  10. package/dist/agents/retain-ledger.d.ts +141 -0
  11. package/dist/agents/roster-store.d.ts +105 -0
  12. package/dist/agents/roster-store.js +3 -0
  13. package/dist/agents/send-message-tool.d.ts +92 -0
  14. package/dist/agents/session-util.d.ts +5 -0
  15. package/dist/agents/subagent-steps.d.ts +66 -0
  16. package/dist/agents/subagent.d.ts +600 -0
  17. package/dist/agents/suspend-guard.d.ts +29 -0
  18. package/dist/agents/teacher.d.ts +75 -0
  19. package/dist/agents/team.d.ts +120 -1
  20. package/dist/agents/tool-filter.d.ts +34 -0
  21. package/dist/agents/verify.d.ts +198 -0
  22. package/dist/bench/metrics.d.ts +455 -0
  23. package/dist/brain/anthropic.d.ts +30 -0
  24. package/dist/brain/circuit-breaker.d.ts +33 -0
  25. package/dist/brain/circuit-breaker.js +14 -3
  26. package/dist/brain/context-overflow.d.ts +60 -3
  27. package/dist/brain/degrading.d.ts +67 -0
  28. package/dist/brain/errors.d.ts +42 -0
  29. package/dist/brain/failover.d.ts +15 -0
  30. package/dist/brain/media-degrade.d.ts +39 -0
  31. package/dist/brain/model-presets.d.ts +31 -0
  32. package/dist/brain/open-responses.d.ts +19 -0
  33. package/dist/brain/openai.d.ts +46 -0
  34. package/dist/brain/reasoning.d.ts +106 -1
  35. package/dist/brain/repetition.d.ts +83 -0
  36. package/dist/brain/request-params.d.ts +56 -0
  37. package/dist/brain/retry.d.ts +46 -0
  38. package/dist/brain/routing.d.ts +12 -0
  39. package/dist/brain/status-sink.d.ts +30 -1
  40. package/dist/brain/stream-engine.d.ts +147 -0
  41. package/dist/brain/stream-shared.d.ts +34 -0
  42. package/dist/brain/terminal-cause.d.ts +31 -0
  43. package/dist/brain/timeout.d.ts +108 -0
  44. package/dist/brain/timeout.js +11 -0
  45. package/dist/brain/tool-call-id.d.ts +20 -0
  46. package/dist/brain/tool-call-repair.d.ts +13 -0
  47. package/dist/config/catalog.d.ts +47 -0
  48. package/dist/config/defaults.d.ts +33 -0
  49. package/dist/core/a2a-task-state.d.ts +53 -0
  50. package/dist/core/a2a.d.ts +51 -0
  51. package/dist/core/arg-summary.d.ts +62 -0
  52. package/dist/core/ask-question.d.ts +272 -2
  53. package/dist/core/auto-compaction.d.ts +467 -0
  54. package/dist/core/auto-compaction.js +20 -6
  55. package/dist/core/auto-mode-prompt-assets.d.ts +8 -0
  56. package/dist/core/auto-mode-prompt.d.ts +27 -0
  57. package/dist/core/auto-mode.d.ts +54 -3
  58. package/dist/core/auto-promote.d.ts +100 -0
  59. package/dist/core/background-agent-store.d.ts +293 -0
  60. package/dist/core/background-agent-store.js +5 -0
  61. package/dist/core/background-shell.d.ts +110 -0
  62. package/dist/core/cache-break-detector.d.ts +34 -0
  63. package/dist/core/canonical-json.d.ts +57 -0
  64. package/dist/core/checkpoint-store.d.ts +1574 -16
  65. package/dist/core/compliance.d.ts +30 -0
  66. package/dist/core/consolidate-scope.d.ts +75 -0
  67. package/dist/core/context-edit.d.ts +99 -0
  68. package/dist/core/context-guard.d.ts +46 -0
  69. package/dist/core/exec-gate.d.ts +44 -0
  70. package/dist/core/exec-output-tail.d.ts +61 -0
  71. package/dist/core/file-snapshot-store.d.ts +104 -0
  72. package/dist/core/fs-write-gate-policy.d.ts +36 -0
  73. package/dist/core/git-worktree-env.d.ts +31 -0
  74. package/dist/core/governance-codes.d.ts +19 -0
  75. package/dist/core/hooks.d.ts +609 -1
  76. package/dist/core/human-input-projection.d.ts +37 -0
  77. package/dist/core/human-input-projection.js +13 -0
  78. package/dist/core/image-downsample.d.ts +74 -0
  79. package/dist/core/locked-config.d.ts +37 -0
  80. package/dist/core/lsp-diagnostics.d.ts +77 -0
  81. package/dist/core/lsp-protocol.d.ts +29 -0
  82. package/dist/core/lsp-session.d.ts +60 -1
  83. package/dist/core/lsp.d.ts +150 -1
  84. package/dist/core/mailbox-store.d.ts +57 -0
  85. package/dist/core/mailbox-store.js +2 -0
  86. package/dist/core/mcp.d.ts +385 -0
  87. package/dist/core/mcp.js +58 -11
  88. package/dist/core/media-byte-cap.d.ts +21 -0
  89. package/dist/core/memory-admission.d.ts +71 -0
  90. package/dist/core/memory-engine/content-origin.d.ts +64 -0
  91. package/dist/core/memory-engine/data-plane.d.ts +43 -0
  92. package/dist/core/memory-engine/dual-root.d.ts +24 -0
  93. package/dist/core/memory-engine/engine.d.ts +230 -0
  94. package/dist/core/memory-engine/engine.js +103 -35
  95. package/dist/core/memory-engine/file-backend.d.ts +155 -0
  96. package/dist/core/memory-engine/frontmatter.d.ts +16 -0
  97. package/dist/core/memory-engine/layout.d.ts +217 -0
  98. package/dist/core/memory-engine/memory-backend-contract.d.ts +12 -0
  99. package/dist/core/memory-engine/migrate.d.ts +9 -0
  100. package/dist/core/memory-engine/scan.d.ts +12 -0
  101. package/dist/core/memory-engine/scope-contract.d.ts +61 -1
  102. package/dist/core/memory-engine/sync-client.d.ts +87 -0
  103. package/dist/core/memory-engine/sync.d.ts +60 -0
  104. package/dist/core/memory-engine/tools.d.ts +41 -0
  105. package/dist/core/memory-engine/types.d.ts +188 -0
  106. package/dist/core/memory-recall.d.ts +141 -0
  107. package/dist/core/memory-vector.d.ts +20 -0
  108. package/dist/core/memory.d.ts +458 -0
  109. package/dist/core/message-utils.d.ts +6 -0
  110. package/dist/core/oracle-isolation.d.ts +69 -0
  111. package/dist/core/permission-rule-consent.d.ts +138 -0
  112. package/dist/core/permission-rule-model.d.ts +122 -0
  113. package/dist/core/permission-rule-store.d.ts +119 -3
  114. package/dist/core/permission-rules.d.ts +87 -1
  115. package/dist/core/present-plan-tool.d.ts +20 -0
  116. package/dist/core/pricing.d.ts +26 -0
  117. package/dist/core/property-harness.d.ts +86 -0
  118. package/dist/core/protocol-naming.d.ts +38 -0
  119. package/dist/core/protocol-table.d.ts +61 -0
  120. package/dist/core/push-queue.d.ts +1 -0
  121. package/dist/core/remote-env.d.ts +383 -1
  122. package/dist/core/retention-policy.d.ts +40 -0
  123. package/dist/core/retention-policy.js +21 -0
  124. package/dist/core/retention.d.ts +51 -0
  125. package/dist/core/roles.d.ts +59 -0
  126. package/dist/core/runner/active-skill-scope.d.ts +34 -0
  127. package/dist/core/runner/assemble-result.d.ts +134 -0
  128. package/dist/core/runner/compaction-call-options.d.ts +97 -1
  129. package/dist/core/runner/grounding-signal.d.ts +10 -0
  130. package/dist/core/runner/image.d.ts +17 -0
  131. package/dist/core/runner/image.js +29 -15
  132. package/dist/core/runner/memory-consolidation.d.ts +60 -0
  133. package/dist/core/runner/prepare-memory.d.ts +59 -0
  134. package/dist/core/runner/prepare-task.d.ts +1011 -2
  135. package/dist/core/runner/prepare-task.js +51 -14
  136. package/dist/core/runner/prompt-suggestions.d.ts +7 -0
  137. package/dist/core/runner/runtask.d.ts +304 -3
  138. package/dist/core/runner/runtask.js +17 -2
  139. package/dist/core/runner/session-file-state-replay.d.ts +49 -0
  140. package/dist/core/runner/session-rule-policy.d.ts +58 -0
  141. package/dist/core/runner/strict-output-schema.d.ts +18 -3
  142. package/dist/core/runner/synthetic-tools.d.ts +113 -0
  143. package/dist/core/runner/teardown-bounded.d.ts +34 -0
  144. package/dist/core/runner/tool-disclosure.d.ts +227 -0
  145. package/dist/core/runner/tool-output-projection.d.ts +6 -0
  146. package/dist/core/runner/turn-attachments.d.ts +634 -0
  147. package/dist/core/runner/usage-accounting.d.ts +32 -0
  148. package/dist/core/runtime.d.ts +9 -0
  149. package/dist/core/safe-notify.d.ts +64 -0
  150. package/dist/core/safety-axis-vocab.d.ts +23 -0
  151. package/dist/core/safety-merge-corpus.d.ts +37 -0
  152. package/dist/core/scheduler.d.ts +121 -0
  153. package/dist/core/secret-env.d.ts +32 -0
  154. package/dist/core/select-model.d.ts +15 -0
  155. package/dist/core/sensitive-path-policy.d.ts +42 -0
  156. package/dist/core/session-policy-store.d.ts +94 -0
  157. package/dist/core/session-reconcile.d.ts +80 -0
  158. package/dist/core/session-store.d.ts +85 -0
  159. package/dist/core/session.d.ts +153 -0
  160. package/dist/core/shared-memory/contract.d.ts +22 -0
  161. package/dist/core/shared-memory/normalize.d.ts +123 -2
  162. package/dist/core/shared-memory/tools.d.ts +14 -0
  163. package/dist/core/shared-memory/types.d.ts +105 -0
  164. package/dist/core/shutdown-debug.d.ts +6 -0
  165. package/dist/core/side-query.d.ts +38 -0
  166. package/dist/core/side-query.js +6 -1
  167. package/dist/core/skill-tool-specifier.d.ts +72 -0
  168. package/dist/core/skills-directory.d.ts +100 -1
  169. package/dist/core/spec-contract.d.ts +89 -0
  170. package/dist/core/store-contracts/background-agent-store-contract.d.ts +31 -0
  171. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +26 -0
  172. package/dist/core/store-contracts/contract-harness.d.ts +20 -0
  173. package/dist/core/store-contracts/contract-kit-version.d.ts +27 -0
  174. package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +8 -0
  175. package/dist/core/store-contracts/mailbox-store-contract.d.ts +42 -0
  176. package/dist/core/store-contracts/tool-result-store-contract.d.ts +13 -0
  177. package/dist/core/strategy-store.d.ts +37 -0
  178. package/dist/core/stub-env.d.ts +7 -0
  179. package/dist/core/surrogate-safe-slice.d.ts +35 -0
  180. package/dist/core/task-notification.d.ts +202 -0
  181. package/dist/core/task-outcome.d.ts +53 -0
  182. package/dist/core/task-registry-agent.d.ts +337 -1
  183. package/dist/core/task-registry-agent.js +2 -0
  184. package/dist/core/task-registry-monitor.d.ts +12 -0
  185. package/dist/core/task-registry-shared.d.ts +540 -0
  186. package/dist/core/task-registry.d.ts +343 -0
  187. package/dist/core/task-registry.js +13 -2
  188. package/dist/core/task-tool-shape.d.ts +44 -0
  189. package/dist/core/tighten-task-spec.d.ts +21 -0
  190. package/dist/core/tool-detach.d.ts +21 -0
  191. package/dist/core/tool-errors.d.ts +131 -0
  192. package/dist/core/tool-errors.js +4 -0
  193. package/dist/core/tool-name-aliases.d.ts +27 -0
  194. package/dist/core/tool-policy.d.ts +555 -0
  195. package/dist/core/tool-policy.js +3 -0
  196. package/dist/core/tool-result-budget.d.ts +32 -0
  197. package/dist/core/tool-result-store.d.ts +174 -1
  198. package/dist/core/tools.d.ts +45 -0
  199. package/dist/core/trace.d.ts +323 -0
  200. package/dist/core/types.d.ts +3859 -2
  201. package/dist/core/untrusted-egress.d.ts +8 -0
  202. package/dist/core/untrusted-text.d.ts +156 -0
  203. package/dist/core/usage-window-store.d.ts +95 -0
  204. package/dist/core/version.d.ts +1 -0
  205. package/dist/core/warm-resume.d.ts +17 -0
  206. package/dist/core/wiring-manifest.d.ts +169 -0
  207. package/dist/core/with-retry.d.ts +24 -0
  208. package/dist/core/workflow-journal-store.d.ts +160 -0
  209. package/dist/core/workflow-run-store-contract.d.ts +25 -0
  210. package/dist/core/workflow-run-store.d.ts +119 -0
  211. package/dist/core/workflow-run-store.js +2 -0
  212. package/dist/engine/compaction/compaction.d.ts +256 -1
  213. package/dist/engine/compaction/utils.d.ts +94 -0
  214. package/dist/engine/execution-env/kill-tree.d.ts +29 -0
  215. package/dist/engine/execution-env/node-execution-env.d.ts +94 -0
  216. package/dist/engine/harness/agent-harness.d.ts +116 -0
  217. package/dist/engine/harness/agent-harness.js +3 -14
  218. package/dist/engine/harness/messages.d.ts +15 -0
  219. package/dist/engine/harness/types.d.ts +464 -2
  220. package/dist/engine/llm/diagnostics.d.ts +4 -0
  221. package/dist/engine/llm/event-stream.d.ts +3 -0
  222. package/dist/engine/llm/index.d.ts +7 -0
  223. package/dist/engine/llm/types.d.ts +500 -3
  224. package/dist/engine/llm/validation.d.ts +3 -0
  225. package/dist/engine/loop/agent-loop.d.ts +87 -2
  226. package/dist/engine/loop/runtime-deps.d.ts +7 -0
  227. package/dist/engine/loop/types.d.ts +424 -0
  228. package/dist/engine/lsp/frame-decoder.d.ts +13 -0
  229. package/dist/engine/lsp/node-lsp-manager.d.ts +107 -0
  230. package/dist/engine/lsp/stdio-lsp-transport.d.ts +7 -0
  231. package/dist/engine/session/import-validate.d.ts +27 -0
  232. package/dist/engine/session/log-digest.d.ts +93 -0
  233. package/dist/engine/session/memory-repo.d.ts +6 -0
  234. package/dist/engine/session/memory-storage.d.ts +2 -0
  235. package/dist/engine/session/session.d.ts +75 -0
  236. package/dist/engine/session/storage-base.d.ts +8 -0
  237. package/dist/fixtures/index.d.ts +36 -0
  238. package/dist/index.d.ts +17 -2
  239. package/dist/index.js +1 -2
  240. package/dist/internal/harness-types.d.ts +6 -0
  241. package/dist/internal/harness.d.ts +11 -0
  242. package/dist/internal/llm.d.ts +6 -0
  243. package/dist/orchestration/builtin-workflows.d.ts +53 -0
  244. package/dist/orchestration/dev-vm-script-runner.d.ts +19 -0
  245. package/dist/orchestration/goal.d.ts +57 -0
  246. package/dist/orchestration/goal.js +3 -0
  247. package/dist/orchestration/run-spec.d.ts +42 -0
  248. package/dist/orchestration/run-spec.js +4 -0
  249. package/dist/orchestration/run-workflow-tool.d.ts +169 -0
  250. package/dist/orchestration/workflow-governance.d.ts +61 -0
  251. package/dist/orchestration/workflow-meta.d.ts +28 -0
  252. package/dist/orchestration/workflow-observe.d.ts +60 -0
  253. package/dist/orchestration/workflow-primitives.d.ts +23 -1
  254. package/dist/orchestration/workflow-sandbox-conformance.d.ts +38 -0
  255. package/dist/orchestration/workflow-script-runner.d.ts +88 -0
  256. package/dist/orchestration/workflow-script-store.d.ts +98 -0
  257. package/dist/orchestration/workflow-size-guideline.d.ts +67 -0
  258. package/dist/orchestration/workflow-types.d.ts +169 -2
  259. package/dist/orchestration/workflow.d.ts +358 -0
  260. package/dist/orchestration/workflow.js +13 -2
  261. package/dist/prompt-assembly/artifact-store.d.ts +33 -0
  262. package/dist/prompt-assembly/artifact.d.ts +25 -0
  263. package/dist/prompt-assembly/assemble.d.ts +20 -0
  264. package/dist/prompt-assembly/composer.d.ts +29 -0
  265. package/dist/prompt-assembly/epoch.d.ts +55 -1
  266. package/dist/prompt-assembly/event-registry.d.ts +35 -0
  267. package/dist/prompt-assembly/explain.d.ts +12 -0
  268. package/dist/prompt-assembly/tool-catalog.d.ts +38 -0
  269. package/dist/prompt-assembly/turn-snapshot.d.ts +40 -0
  270. package/dist/prompt-assembly/types.d.ts +115 -0
  271. package/dist/prompts/coordinator.d.ts +27 -0
  272. package/dist/prompts/default.d.ts +539 -0
  273. package/dist/prompts/simple-sections.d.ts +45 -0
  274. package/dist/prompts/supervisor.d.ts +66 -0
  275. package/dist/scenarios/env.d.ts +28 -0
  276. package/dist/scenarios/full-body.d.ts +50 -0
  277. package/dist/scenarios/scenario-registry.d.ts +60 -0
  278. package/dist/scenarios/teacher-quickstart.d.ts +27 -0
  279. package/dist/server/http.d.ts +17 -0
  280. package/dist/stores/cc/lockfile.d.ts +6 -0
  281. package/dist/stores/cc/mailbox-store.d.ts +8 -0
  282. package/dist/stores/cc/roster-adapter.d.ts +4 -0
  283. package/dist/stores/cc/sidecar-transcript.d.ts +66 -0
  284. package/dist/stores/file/background-agent-store.d.ts +24 -0
  285. package/dist/stores/file/background-agent-store.js +2 -1
  286. package/dist/stores/file/checkpoint-store.d.ts +38 -0
  287. package/dist/stores/file/file-snapshot-store.d.ts +25 -0
  288. package/dist/stores/file/fs-atomic.d.ts +155 -0
  289. package/dist/stores/file/index.d.ts +89 -0
  290. package/dist/stores/file/mailbox-store.d.ts +36 -0
  291. package/dist/stores/file/mailbox-store.js +2 -0
  292. package/dist/stores/file/memory-store.d.ts +82 -0
  293. package/dist/stores/file/permission-rule-store.d.ts +79 -0
  294. package/dist/stores/file/session-policy-store.d.ts +28 -0
  295. package/dist/stores/file/session-store.d.ts +40 -0
  296. package/dist/stores/file/shared-ledger.d.ts +83 -0
  297. package/dist/stores/file/tool-result-store.d.ts +11 -0
  298. package/dist/stores/file/usage-window-store.d.ts +18 -0
  299. package/dist/stores/file/workflow-journal-store.d.ts +100 -0
  300. package/dist/stores/file/workflow-run-store.d.ts +11 -0
  301. package/dist/stores/file/workflow-run-store.js +2 -0
  302. package/dist/tools/fs/bash-readonly-classifier.d.ts +282 -0
  303. package/dist/tools/fs/encoding.d.ts +60 -0
  304. package/dist/tools/fs/fs-bash.d.ts +133 -0
  305. package/dist/tools/fs/fs-pdf.d.ts +28 -0
  306. package/dist/tools/fs/fs-search-tools.d.ts +5 -0
  307. package/dist/tools/fs/fs-shared.d.ts +360 -0
  308. package/dist/tools/fs/fs-write.d.ts +16 -0
  309. package/dist/tools/fs/gh-rate-limit.d.ts +22 -0
  310. package/dist/tools/fs/index.d.ts +79 -0
  311. package/dist/tools/fs/notebook.d.ts +56 -0
  312. package/dist/tools/fs/pdf.d.ts +70 -0
  313. package/dist/tools/fs/pdf.js +8 -2
  314. package/dist/tools/fs/repo-map.d.ts +10 -0
  315. package/dist/tools/fs/safety.d.ts +376 -0
  316. package/dist/tools/fs/search.d.ts +131 -0
  317. package/dist/tools/loop-tick.d.ts +24 -0
  318. package/dist/tools/monitor.d.ts +55 -0
  319. package/dist/tools/scheduler-tools.d.ts +51 -0
  320. package/dist/tools/sql-adapters.d.ts +18 -0
  321. package/dist/tools/sql.d.ts +25 -0
  322. package/dist/tools/task-list.d.ts +77 -0
  323. package/dist/tools/todo.d.ts +8 -0
  324. package/dist/tools/web.d.ts +184 -0
  325. package/dist/tools/web.js +32 -5
  326. package/dist/tools/worktree.d.ts +81 -0
  327. package/package.json +2 -2
  328. package/dist/tools/gitea-issue.d.ts +0 -13
  329. package/dist/tools/gitea-issue.js +0 -75
@@ -1,3 +1,19 @@
1
+ /**
2
+ * `withRetry` (design/61 §9, core thin helper) — the consumer side of the typed-retryable error contract
3
+ * (`remote-env.ts`). Retries a Result-returning op, but **only** on a whitelisted typed-retryable code, up to
4
+ * `maxAttempts`, with optional backoff. All remote adapters (E2B / SSH / ADB) reuse it so they don't each
5
+ * re-implement "retry by typed code + backoff" (and risk getting the contract wrong).
6
+ *
7
+ * 🔴 **Apply ONLY to IDEMPOTENT establishment ops** — `connect` / `reconnect` (design/61 §2(A)). The error
8
+ * contract is "the caller decides retry per idempotency; the adapter MUST NOT blind-retry"; this helper IS that
9
+ * caller-side decision, scoped to the safe (idempotent) layer. **Never wrap a side-effecting operation** — and
10
+ * never put a permanent code (`"auth_failed"`, `"unsupported"`) in `retryableCodes` (retrying a rejected key
11
+ * burns the budget / can lock the account).
12
+ *
13
+ * Determinism / fault injection (council §9 F): the `op(attempt)` closure is the injection point (a test
14
+ * scripts a per-attempt error sequence), and `opts.sleep` overrides the real backoff delay — so
15
+ * "first attempt fails, second succeeds" is tested deterministically without a real device/network.
16
+ */
1
17
  export type RetryResult<T, E> = {
2
18
  ok: true;
3
19
  value: T;
@@ -6,10 +22,18 @@ export type RetryResult<T, E> = {
6
22
  error: E;
7
23
  };
8
24
  export interface RetryPolicy {
25
+ /** Error codes that warrant a retry (a typed-retryable whitelist). A code not in this list returns immediately. */
9
26
  retryableCodes: readonly string[];
27
+ /** Total attempts, `>= 1`. (e.g. ADB first-auth = 2; SSH connect-drop = a few.) */
10
28
  maxAttempts: number;
29
+ /** Backoff before attempt N (1-based; called with the attempt that just FAILED, so N≥1). Default: no wait. */
11
30
  backoffMs?: (failedAttempt: number) => number;
12
31
  }
32
+ /**
33
+ * Run `op` until it succeeds, a non-retryable error surfaces, or `maxAttempts` is exhausted. `op` receives the
34
+ * 1-based attempt number. Returns the success, or the LAST failure (so an exhausted-but-still-failing op surfaces
35
+ * its final typed error to the caller).
36
+ */
13
37
  export declare function withRetry<T, E extends {
14
38
  code: string;
15
39
  }>(op: (attempt: number) => Promise<RetryResult<T, E>>, policy: RetryPolicy, opts?: {
@@ -1,37 +1,197 @@
1
1
  import type { TaskResult } from "./types.js";
2
+ /**
3
+ * design/97 CORE-7 — one recorded `ctx.agent` result in a workflow's RESUME journal. Unlike the observation-only
4
+ * {@link WorkflowRunStore} (best-effort, swallowed on throw), the journal is LOAD-BEARING: a resume replays the
5
+ * longest unchanged PREFIX of these (keyed by the deterministic {@link workflowAgentCallKey}) and runs only the
6
+ * first changed/new call + everything after it live. Same script + same args → 100% cache hit.
7
+ */
2
8
  export interface WorkflowJournalEntry {
9
+ /** The agent's deterministic call key (`ordinal:specIdentityHash`) — the replay match key. */
3
10
  callKey: string;
11
+ /** The agent's TaskResult, replayed verbatim when a resume's call key matches at the same ordinal. */
4
12
  result: TaskResult;
5
13
  }
14
+ /** REF-D5: the resume-claim key triple — ONE shape for the interface pair and every implementation
15
+ * (it was re-inlined seven times; a key-field rename must red every leg at once). */
6
16
  export interface ResumeClaimArgs {
7
17
  sourceRunId: string;
8
18
  newRunId: string;
9
19
  scope: string;
10
20
  }
21
+ /**
22
+ * The seam a resume reads/writes its journal through. Core ships {@link InMemoryWorkflowJournalStore}; a durable
23
+ * deployment provides a Postgres-backed one (service SVC-2) so a workflow survives a replica crash. The owner of
24
+ * a run is the sole writer of its journal.
25
+ *
26
+ * 🔐 `scope` (the TENANT/principal of the resuming run) is LOAD-BEARING for multi-tenant isolation (CORE-9 audit
27
+ * BLOCKER): an LLM controls the `resumeFromRunId`, so `load` MUST be constrained to the caller's scope — exactly
28
+ * as {@link WorkflowRunStore} puts scope in the WHERE of every method. A store implementation MUST return ONLY a
29
+ * run's journal whose recorded scope equals the requested `scope` (a cross-scope `resumeFromRunId` resolves to an
30
+ * EMPTY journal → the resume safely diverges to a live re-run, never disclosing another tenant's results).
31
+ *
32
+ * SESSION-axis RULING (clay 裁定 2026-07-22, B-2 会话轴扫描案; FINAL form settled in the γ batch):
33
+ * `load` is deliberately SCOPE-only, PERMANENTLY — same-scope callers from a different session may
34
+ * replay a run's journal. Rationale: (a) same-scope = same trust domain (the task-list store
35
+ * records the same posture) and a runId is engine-minted, not enumerable; (b) cross-session resume
36
+ * is `resumeFromRunId`'s CORE legitimate use — a fresh session picking up yesterday's run has a new
37
+ * taskId AND a new sessionId by construction, so ANY engine-side session/owner clamp on this face
38
+ * refuses exactly the contract the tool teaches (the [1455] prefix-replay pins are the regression
39
+ * proof; a γ-batch recheck attempt was reverted on them). Layering: the ENGINE owns the scope
40
+ * hard-wall (this WHERE); the SESSION acceptance arm belongs to the SERVING layer, which has a
41
+ * session-bound connection to adjudicate against (server 1.247's wire-face soft acceptance). This
42
+ * face diverges from poll/stop (scope+owner/session positive-match) BY DESIGN: they gate on the
43
+ * caller's CURRENT identity, resume asserts continuity with a PRIOR leg.
44
+ */
11
45
  export interface WorkflowJournalStore {
46
+ /** Entries for `runId` IF its recorded scope === `scope`, ASCENDING by ordinal; otherwise EMPTY (cross-scope). */
12
47
  load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
48
+ /** Record one agent's result under `(runId, scope)`. MUST be idempotent per `(runId, ordinal)` — a resumed run
49
+ * re-appends the cached results of its replayed prefix, so a second append for the same ordinal overwrites. */
13
50
  append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
51
+ /** [1409]③ (CC diagnostics parity, additive): a HUMAN/MODEL-readable coordinate for this run's journal —
52
+ * the file store returns the on-disk jsonl path (CC "Read journal.jsonl" local form); a service store
53
+ * returns its API route (cloud form). `undefined`/absent ⇒ the completion notification's diagnostics
54
+ * line teaches only the TaskOutput route (which is always live). Engine-facing string, never parsed. */
14
55
  locator?(runId: string, scope: string): string | undefined;
56
+ /**
57
+ * RB-242 ([1980] server → [1981] core ruling, 2026-07-30) — ADMISSION for a resume, so two concurrent
58
+ * `resumeFromRunId` legs off ONE source run cannot fork its execution.
59
+ *
60
+ * Why a store seam at all: the engine's own mutex is in-process only (`startWorkflow` refuses a duplicate
61
+ * `runId` and refuses resuming from a source still ACTIVE here). A resume mints a FRESH runId, so a second
62
+ * resume off the same source — from another process, replica, or a later session — trips neither guard: both
63
+ * legs replay the same prefix and then re-run the whole suffix LIVE, duplicating every side effect after it,
64
+ * each writing its own journal, neither ever seeing the other. The only place that fork is visible is the
65
+ * shared durable layer, i.e. here.
66
+ *
67
+ * OPTIONAL, and absence is a first-class state: a store that does NOT implement this has NO STORE-LEVEL lease
68
+ * semantics. RB-367 ([2054] test AI dual-process probe): a store without the hook is no longer completely
69
+ * unguarded, though — `runWorkflow`'s engine falls back to a process-local admission table for exactly this
70
+ * shape, so a same-process double `resumeFromRunId` off one source is still refused even against a store
71
+ * that never implements this member. That fallback cannot see a SECOND process, which is the one thing a
72
+ * real implementation is for — core's own {@link InMemoryWorkflowJournalStore} DOES implement this member
73
+ * (with in-process-only semantics; a single-process store cannot promise more), so the engine's fallback
74
+ * table is, in practice, only ever exercised by some OTHER store that omits the hook.
75
+ *
76
+ * Contract:
77
+ * - `granted: true` ⇒ the caller (`newRunId`) now HOLDS the claim on `(sourceRunId, scope)` and owes exactly
78
+ * one {@link releaseResumeClaim} for it. The engine calls that at the run's terminal boundary — completed,
79
+ * failed, cancelled/aborted, or abandoned by a total-timeout — in a `finally`.
80
+ * - `granted: false` ⇒ someone else holds it. The engine REFUSES the resume with an explicit error rather
81
+ * than forking; `holder` (the holding leg's `newRunId`, when the store can attribute it) goes into that
82
+ * error text so an operator can find the run that is actually still going. `holder` is store-supplied text
83
+ * and is charset-clamped before it lands in a message.
84
+ * - IDEMPOTENT for the same holder: a re-claim by the SAME `newRunId` on a claim it already holds MUST be
85
+ * granted (a retry must not deadlock a run against itself).
86
+ * - `scope` is the journal scope passed through VERBATIM ([1981]). A claim is keyed by `(sourceRunId, scope)`,
87
+ * NOT by `sourceRunId` alone: a cross-scope caller already resolves to an EMPTY journal (the CORE-9 wall
88
+ * above) so it cannot fork anything, and letting it take a global claim would hand any tenant a
89
+ * denial-of-resume lever over another's runs.
90
+ * - THROWING is a store failure, not a refusal — it propagates and fails the resume. Return `{granted:false}`
91
+ * to refuse.
92
+ * - A GRANT MEANS POSSESSION (RB-356①, codex 异源复审 2026-07-29). `granted: true` may only be returned once
93
+ * the claim is RECORDED where every other contender will see it; a store that cannot tell whether it holds
94
+ * the claim must refuse, with `holder` omitted if it cannot attribute the winner. The asymmetry is the
95
+ * reason: an unearned refusal fails one resume loudly and is recoverable, an unearned grant forks a run
96
+ * and its duplicated side effects are not.
97
+ * - ACQUISITION MUST BE ATOMIC (RB-354). Contenders must never be able to observe a claim MID-ACQUISITION
98
+ * and read that state as "no claim" — an implementation that reserves the key first and fills the record
99
+ * in a second step needs the reservation itself to be the exclusion (SQL: one INSERT; core's file store:
100
+ * write a temp, then `link` it into place). Note this interacts with crash recovery: any rule of the form
101
+ * "an incomplete record is dead, take it over" is only safe if an in-flight acquisition can never look
102
+ * incomplete.
103
+ *
104
+ * TWO INVALIDATION DOMAINS, each covering what the other cannot ([1981], settled with server):
105
+ * 1. ENGINE release — the normal path, prompt, covers every terminal outcome of a live engine.
106
+ * 2. STORE TTL — the backstop for an engine that DIED holding a claim (crash/kill/replica loss): it never
107
+ * reaches its `finally`, so only a store-side expiry can ever free that row. Server's SQL twins land this
108
+ * as the bake-store `idem_key UNIQUE` + TTL shape; core's file store carries the same TTL semantics.
109
+ * A store implementing neither expiry nor release would wedge the source run's resume permanently.
110
+ */
15
111
  resumeClaim?(input: ResumeClaimArgs): Promise<{
16
112
  granted: boolean;
17
113
  holder?: string;
18
114
  }>;
115
+ /**
116
+ * RB-242 ([1981]) — release the claim {@link resumeClaim} granted to `newRunId` on `(sourceRunId, scope)`.
117
+ * Called by the engine at the resuming run's TERMINAL boundary (in a `finally`, so abort/error/abandon are
118
+ * covered), and ONLY when the matching claim was actually granted.
119
+ *
120
+ * MUST be idempotent and MUST NOT release a claim the caller does not hold: the claim may already be gone
121
+ * (TTL-reaped after a stall, then taken over by another leg), and unlinking the successor's claim would
122
+ * re-open exactly the fork this seam exists to prevent. A missing claim is a no-op, never an error.
123
+ *
124
+ * A throw here is swallowed by the engine — a release failure must never replace the run's real outcome, and
125
+ * the store's TTL is the standing backstop for the row it left behind.
126
+ *
127
+ * RB-355 (codex 异源复审 2026-07-29): the engine's terminal boundary now BOUNDS this call (and its wait on
128
+ * the {@link resumeClaim} verdict) by a timeout — a store that hangs would otherwise keep the run's `done`
129
+ * promise pending forever, after the outcome is already decided. A release that outlives that bound is
130
+ * treated exactly like one that throws: swallowed, with the TTL as the backstop. Implementations should not
131
+ * rely on the engine waiting indefinitely for cleanup.
132
+ */
19
133
  releaseResumeClaim?(input: ResumeClaimArgs): Promise<void>;
20
134
  }
135
+ /** The ordinal a callKey (`ordinal:hash`) was minted at — the journal's positional index. The sole producer
136
+ * ({@link workflowAgentCallKey}) always emits a well-formed `<int>:<hash>`; a MALFORMED key (no leading int)
137
+ * falls back to `0` rather than throwing — callers must not feed it arbitrary strings (audit note). */
21
138
  export declare const MAX_JOURNAL_RESULT_BYTES: number;
139
+ /** True when a serialized journal result is too large to store (UTF-8 byte length over {@link MAX_JOURNAL_RESULT_BYTES}). */
22
140
  export declare function oversizeJournalResult(serialized: string): boolean;
23
141
  export declare function callKeyOrdinal(callKey: string): number;
142
+ /** RB-243 ([1937]): the errorCode a journal OVERSIZE TOMBSTONE carries — the resume path recognizes it to
143
+ * say WHY an ordinal never replays. `failed` + this code lands on the T2A-4 no-replay arm, so replay
144
+ * behavior is identical to the entry simply being absent (the pre-tombstone shape). */
24
145
  export declare const JOURNAL_OVERSIZE_ERROR_CODE = "workflow.journal_oversize";
146
+ /** RB-243 ([1937]): mint the tiny FAILED entry the ENGINE journals in place of an oversize result. The
147
+ * tombstone is minted in the engine, NOT in a store — the stores' skip semantics are unchanged (the
148
+ * §4.1 backend-parity pin depends on that layering; a store-side tombstone would re-create the RB-168
149
+ * dialect divergence). Never copies `result.result`/`structuredOutput` (the oversize payload); keeps
150
+ * taskId/sessionId/stats so the journal read-face still shows the session handle and the paid spend. */
25
151
  export declare function journalOversizeTombstone(result: TaskResult, bytes: number): TaskResult;
152
+ /**
153
+ * RB-367 ([2054] test AI dual-process probe, core-side judgment) — the TTL a resume claim {@link
154
+ * InMemoryWorkflowJournalStore} grants survives WITHOUT an engine release: the backstop for an engine that
155
+ * abandoned a granted claim without ever reaching the terminal `finally` that releases it (a bug, not the
156
+ * normal path — the normal path releases promptly). Same VALUE as the file store's `RESUME_CLAIM_TTL_MS`
157
+ * (`src/stores/file/workflow-journal-store.ts`) — kept as an independent constant rather than imported from
158
+ * it, because `core/` is upstream of `stores/file/` (the file store already imports FROM this module; the
159
+ * reverse would invert that layering) and an in-memory store's claims die with the process regardless, so the
160
+ * two constants have no shared-drift risk to guard against by literally sharing one declaration.
161
+ */
26
162
  export declare const IN_MEMORY_RESUME_CLAIM_TTL_MS: number;
163
+ /** In-process journal store (core default). A single-replica run; durable resume across crashes needs the
164
+ * service's Postgres store (SVC-2). Records each run's `scope` and enforces it on load (CORE-9 audit BLOCKER:
165
+ * a cross-scope resumeFromRunId resolves to an empty journal — no cross-tenant disclosure).
166
+ *
167
+ * RB-367: also carries a REAL {@link WorkflowJournalStore.resumeClaim}/{@link
168
+ * WorkflowJournalStore.releaseResumeClaim} implementation (this store used to be the canonical example of a
169
+ * store WITHOUT the seam — its own header said so). Left unimplemented, the DEFAULT deployment (no
170
+ * `journalStore` supplied, or this store supplied explicitly) had NO protection against two same-process
171
+ * `resumeFromRunId` calls off one source forking its execution — the class of bug RB-242 exists to close, and
172
+ * a test AI's dual-process probe found it wide open on the default path while the file store's half already
173
+ * worked. A single-process store's promise is necessarily single-process: this implementation gives no
174
+ * cross-process guarantee (there is no shared medium to publish a claim on), so a multi-process deployment
175
+ * still needs a store that IS durable/shared (the file or SQL stores) for the seam to mean anything beyond
176
+ * this one process. */
27
177
  export declare class InMemoryWorkflowJournalStore implements WorkflowJournalStore {
28
178
  private readonly runs;
179
+ /** RB-367 resume claims, nested by scope then sourceRunId (rather than a single string-concatenated key) so
180
+ * untrusted-length `scope`/`sourceRunId` text (`sourceRunId` is `resumeFromRunId` — LLM-controlled) can never
181
+ * collide across the boundary between the two fields. */
29
182
  private readonly resumeClaims;
30
183
  private claimsForScope;
184
+ /** RB-367 — contract parity with the file store: keyed by `(sourceRunId, scope)`; idempotent re-grant to the
185
+ * SAME holder; an unexpired claim held by someone else refuses (naming the holder); an expired or absent
186
+ * claim is free to take. Acquisition is a single synchronous Map read-then-write with no `await` between
187
+ * the check and the publish — there is no window (matching the atomicity RB-354 requires of a durable
188
+ * implementation) in which a concurrent caller could observe the key as free mid-acquire. */
31
189
  resumeClaim(input: ResumeClaimArgs): Promise<{
32
190
  granted: boolean;
33
191
  holder?: string;
34
192
  }>;
193
+ /** RB-367 — release ONLY a claim `newRunId` still holds (a missing/foreign/already-taken-over claim is a
194
+ * no-op, never an error — matches the file store's contract). */
35
195
  releaseResumeClaim(input: ResumeClaimArgs): Promise<void>;
36
196
  load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
37
197
  append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
@@ -1,5 +1,30 @@
1
1
  import { type WorkflowRunStore } from "./workflow-run-store.js";
2
2
  import type { WorkflowRun, WorkflowRunStatus } from "../orchestration/workflow.js";
3
+ /**
4
+ * design/97 S1b/§D.8 — the **runner-agnostic** cross-backend contract for {@link WorkflowRunStore}, so the
5
+ * SAME assertions run over every backend AND **service can import it to verify its own PG implementation**
6
+ * (§D.8: "contract 函数须 service 可复用,放可被 import 的位置,非纯 test/ 私有").
7
+ *
8
+ * It lives in `src/` (compiled into `dist/`, re-exported from the package barrel) and uses Node's built-in
9
+ * `assert` — NO vitest dependency — so it runs under any harness (vitest, node:test, a bare script). Core's
10
+ * `test/cross-backend-shared.ts` wraps {@link workflowRunStoreContract} in a single vitest `it()` to bind it
11
+ * to the InMemory + File backends; service binds it to its PG store.
12
+ *
13
+ * The fixtures pin `createdAt`/`endedAt` so the `listByScope` ordering + `reap` retention are deterministic
14
+ * (no wall clock). `make()` must return a FRESH, empty store each call.
15
+ */
16
+ /** Build a {@link WorkflowRun} fixture (terminal `completed` by default). All fields explicit + deterministic. */
3
17
  export declare function createWorkflowRun(over?: Partial<WorkflowRun>): WorkflowRun;
18
+ /**
19
+ * The full cross-backend contract for {@link WorkflowRunStore}. `make` returns a fresh store; `runAssertion`
20
+ * lets a harness label/await each case (a vitest binding passes `it`; a bare runner can pass a sequential
21
+ * runner). The DEFAULT runs the cases inline and sequentially (so `service` can call
22
+ * `await workflowRunStoreContract(make)` with no harness at all).
23
+ *
24
+ * ⚠️ Binding note (service [390]): the cases are REGISTERED eagerly and awaited via `Promise.all` — an async
25
+ * `runAssertion` that schedules work concurrently will interleave cases. Bindings whose stores share state
26
+ * (e.g. SQL twins on one table) must CHAIN the assertions themselves (serialize inside `runAssertion`).
27
+ */
4
28
  export declare function workflowRunStoreContract(make: () => WorkflowRunStore, runAssertion?: (name: string, fn: () => Promise<void>) => void): Promise<void>;
29
+ /** Re-export so a contract caller can label runs by status without re-importing the run module. */
5
30
  export type { WorkflowRunStatus };
@@ -1,47 +1,165 @@
1
1
  import type { WorkflowRun, WorkflowRunStatus } from "../orchestration/workflow.js";
2
+ /**
3
+ * design/97 S1b — the **WorkflowRunStore** persistence seam for the workflow-mode primitive
4
+ * (`src/orchestration/workflow.ts`, S1a). It is the durable home for a {@link WorkflowRun} so the
5
+ * `/workflows` observability layer (S1c) can list runs IN PROGRESS *and the history* (completed / failed),
6
+ * across replicas — the "看之前的" core seam (design/97 §A/§D.1).
7
+ *
8
+ * **Distinct from the four load-bearing durable seams** (session / checkpoint / memory / tool-result): those
9
+ * are the engine's suspend/resume命脉, so core ships PG adapters for them. A WorkflowRunStore is an
10
+ * **observation layer — opt-in, non-engine-critical** — so per design/97 §D.8 (clay 2026-06-22 拍) core
11
+ * ships ONLY: this interface + {@link InMemoryWorkflowRunStore} (process-local / default / tests) +
12
+ * `FileWorkflowRunStore` (zero-dependency, TOC-local persistence — `src/stores/file/workflow-run-store.ts`).
13
+ * **The PG backend lives in service (TOB), implementing THIS interface** when it needs cross-replica
14
+ * `/workflows` history. `WorkflowRun` / `WorkflowRunStatus` are imported type-only — this module is purely
15
+ * the store contract + projection, it does NOT re-home the run model (that stays in `workflow.ts`).
16
+ *
17
+ * Mirrors the {@link import("./checkpoint-store.js").CheckpointStore} shape: create-once `put`, `get`, an
18
+ * **atomic CAS `update`** (scope + `rev` optimistic-concurrency — concurrent agents racing to record progress
19
+ * on the same run), a `listByScope` returning lightweight {@link WorkflowRunSummary} projections (history
20
+ * included, NOT pending-only like CheckpointStore.listByScope), and a `reap` retention sweep that only ever
21
+ * deletes TERMINAL runs (never a running one). The {@link summarizeWorkflowRun} projection is SHARED across
22
+ * all backends (anti-drift, exactly like `summarizeCheckpoint`).
23
+ */
24
+ /**
25
+ * A lightweight, read-only projection of one {@link WorkflowRun} returned in bulk by
26
+ * {@link WorkflowRunStore.listByScope} so the `/workflows` list view (CLI design/92 / client design/90) can
27
+ * enumerate a scope's runs in ONE call — no N+1 `get`s, no full run payloads. Every field is DERIVED from
28
+ * the persisted run (nothing here is a new source of truth or a gate input). Built by the shared
29
+ * {@link summarizeWorkflowRun} so the projection is IDENTICAL across the in-memory, file, and (service) PG
30
+ * backends.
31
+ */
2
32
  export interface WorkflowRunSummary {
33
+ /** The run id ({@link WorkflowRun.id}). */
3
34
  id: string;
35
+ /** The tenant / grouping key this run lives in ({@link WorkflowRun.scope}). */
4
36
  scope: string;
37
+ /** γ 批 ([1510] server request): the ORIGINATING session id ({@link WorkflowRun.originatingSessionId}),
38
+ * projected so a serving layer's LIST face can apply its session acceptance/filter arm without an
39
+ * N+1 `get` of the full run. Absent for a direct `runWorkflow` call / sessionless deployment. */
5
40
  originatingSessionId?: string;
41
+ /** design/99 MF-W: the workflow's display name from the script's `export const meta` ({@link WorkflowRun.name}).
42
+ * Projected here so the `/workflows` list view labels each row WITHOUT an N+1 `get` of the full run.
43
+ * Absent for a direct `runWorkflow` call that supplied no name. */
6
44
  name?: string;
45
+ /** One-line description from the script's meta ({@link WorkflowRun.description}); absent if none. */
7
46
  description?: string;
47
+ /** Lifecycle status ({@link WorkflowRun.status}): running / completed / failed. */
8
48
  status: WorkflowRunStatus;
49
+ /** Count of agent-runs that ended failed ({@link WorkflowRun.agentFailures}) — present only when > 0.
50
+ * Lets a list view flag a "completed, with failures" run without an N+1 `get` of the full run. */
9
51
  agentFailures?: number;
52
+ /** Title of the latest phase recorded ({@link WorkflowRun.phases}`.at(-1).title`) — what the run is on RIGHT
53
+ * NOW for a `running` row (so the `/workflows` list shows the live phase without subscribing to the event
54
+ * stream). Absent when no phase has started yet. */
10
55
  currentPhase?: string;
56
+ /** Number of phases recorded so far ({@link WorkflowRun.phases}`.length`). */
11
57
  phaseCount: number;
58
+ /** Number of agent-runs recorded so far ({@link WorkflowRun.agents}`.length`). */
12
59
  agentCount: number;
60
+ /** Total tokens spent = own + nested (`stats.tokens + stats.nested.tokens`) — the figure a triage view
61
+ * sorts/compares by. own/nested stay SEPARATE on the full run (R-5); the summary folds them for display. */
13
62
  tokens: number;
63
+ /** When the run started ({@link WorkflowRun.startedAt}, epoch ms). */
14
64
  startedAt: number;
65
+ /** When the run finished ({@link WorkflowRun.endedAt}, epoch ms) — absent while still running. */
15
66
  endedAt?: number;
67
+ /** When the run record was created ({@link WorkflowRun.createdAt}, epoch ms) — the `listByScope` sort key. */
16
68
  createdAt: number;
17
69
  }
70
+ /**
71
+ * design/97 S1b: project a {@link WorkflowRun} to its lightweight {@link WorkflowRunSummary}. SHARED by every
72
+ * {@link WorkflowRunStore} impl so the projection is IDENTICAL across the in-memory, file, and (service) PG
73
+ * backends (the anti-drift guard — exactly like `summarizeCheckpoint`). Pure; reads no clock/random. Total
74
+ * spend folds own + nested (`stats.tokens + stats.nested.tokens`), the one place the two are summed.
75
+ */
18
76
  export declare function summarizeWorkflowRun(run: WorkflowRun): WorkflowRunSummary;
77
+ /** Whether a run is in a TERMINAL state — the ONLY states {@link WorkflowRunStore.reap} may delete (a
78
+ * `running` run is never reaped, so an in-flight workflow's record can't vanish out from under it). */
19
79
  export declare function isTerminalWorkflowStatus(status: WorkflowRunStatus): boolean;
80
+ /** A typed workflow-run-store error so callers branch on `code` (mirrors `CheckpointError`). */
20
81
  export declare class WorkflowRunStoreError extends Error {
21
82
  readonly code: "workflow_run.already_exists";
22
83
  constructor(code: "workflow_run.already_exists", message: string);
23
84
  }
85
+ /** REF-D18: `listByScope`'s query options — the SINGLE shape the interface method and both the
86
+ * In-Memory and File implementations reference. */
24
87
  export interface WorkflowRunListByScopeOptions {
25
88
  status?: WorkflowRunStatus;
26
89
  limit?: number;
27
90
  session?: string;
28
91
  }
92
+ /**
93
+ * The pluggable persistence seam for workflow runs (design/97 S1b). Symmetric with `CheckpointStore`:
94
+ * create-once `put`, `get`, an atomic CAS `update`, a `listByScope` query (history included), and a `reap`
95
+ * retention sweep. A durable backend (service's PG) makes `/workflows` history cross-replica; the default
96
+ * {@link InMemoryWorkflowRunStore} is process-only (single instance / tests).
97
+ */
29
98
  export interface WorkflowRunStore {
99
+ /** Create-once. Throws {@link WorkflowRunStoreError} `already_exists` on a token collision (never a silent
100
+ * overwrite — a reused id would clobber a live run's record). */
30
101
  put(id: string, run: WorkflowRun): Promise<void>;
31
102
  get(id: string): Promise<WorkflowRun | null>;
103
+ /**
104
+ * Atomic CAS update of an EXISTING run (`UPDATE … SET … WHERE id=? AND scope=?` [`AND rev=?`]). Returns
105
+ * `true` for the winner, `false` when the row is missing, the `scope` does not match (multi-tenant
106
+ * isolation — a wrong-scope update must not win), or `expect.rev` does not match the live row's `rev`
107
+ * (an intervening update bumped it). On success the store bumps the row's `rev` (monotonic) and persists
108
+ * the new `run`. The workflow owner is the sole writer (best-effort progress recording — design/97 §F),
109
+ * so `expect.rev` lets it detect a lost write rather than silently clobber.
110
+ */
32
111
  update(id: string, scope: string, run: WorkflowRun, expect?: {
33
112
  rev: number;
34
113
  }): Promise<boolean>;
114
+ /**
115
+ * List lightweight {@link WorkflowRunSummary} projections for runs in `scope` — **history included** (the
116
+ * completed / failed runs, NOT pending-only like `CheckpointStore.listByScope`), so this is the "看之前的"
117
+ * core query. Newest first (`createdAt` DESC). `opts.status` filters to one lifecycle state; `opts.limit`
118
+ * caps the result count (applied AFTER the sort, so it keeps the newest N). An empty scope returns `[]`.
119
+ */
120
+ /** `opts.session` ([1513] server ask, δ 尾件): STORE-side push-down filter on
121
+ * `originatingSessionId` — strict equality, rows WITHOUT the field excluded (filter semantics,
122
+ * deliberately unlike the per-id acceptance arm's absent-pass). Applied BEFORE limit, so the
123
+ * serving layer's "this session's workflows" is FULL-history, not page-window ([1513] cli note). */
35
124
  listByScope(scope: string, opts?: WorkflowRunListByScopeOptions): Promise<WorkflowRunSummary[]>;
125
+ /**
126
+ * Retention sweep: delete OLD **terminal** runs in `scope` (a `running` run is NEVER deleted — design/97
127
+ * §D.1/§F). Returns the count deleted (for metrics). Retention is by either policy (apply BOTH when both
128
+ * given — a run is reaped if it fails either bound):
129
+ * - `opts.maxAgeMs`: delete a terminal run whose `endedAt` (else `createdAt`) is older than `now -
130
+ * maxAgeMs`.
131
+ * - `opts.keep`: keep only the newest `keep` terminal runs (by `createdAt` DESC); delete the rest.
132
+ * With NEITHER option set, nothing is deleted (a no-op `0`) — retention is always an explicit policy.
133
+ */
36
134
  reap(scope: string, now: number, opts?: {
37
135
  maxAgeMs?: number;
38
136
  keep?: number;
39
137
  }): Promise<number>;
40
138
  }
139
+ /**
140
+ * REF-A5 — THE optimistic-concurrency guard for {@link WorkflowRunStore.update}, shared by every backend:
141
+ * scope in the WHERE (multi-tenant isolation — a wrong-scope update never wins), row must exist, and an
142
+ * `expect.rev` that no longer matches the LIVE row loses (an intervening update bumped it). Returns the
143
+ * row to persist, or `null` when the CAS lost; PURE — it neither reads nor writes any store state, so
144
+ * each backend keeps its own persistence mechanism (in-memory `Map.set` vs ledger commit) around it.
145
+ * RB-59 (a cross-instance CAS both instances won) landed on this predicate.
146
+ */
41
147
  export declare function nextWorkflowRunOnUpdate(cur: WorkflowRun | undefined, id: string, scope: string, run: WorkflowRun, expect?: {
42
148
  rev: number;
43
149
  }): WorkflowRun | null;
150
+ /**
151
+ * REF-A5 — THE `listByScope` query projection, shared by every backend: scope/status/session filter →
152
+ * newest-first by `createdAt` → truncate → {@link summarizeWorkflowRun}. Pure over any row iterable, so
153
+ * the in-memory backend passes its `Map` values and the file backend passes the replayed ledger's.
154
+ */
44
155
  export declare function queryWorkflowRuns(runs: Iterable<WorkflowRun>, scope: string, opts?: WorkflowRunListByScopeOptions): WorkflowRunSummary[];
156
+ /**
157
+ * Default in-process {@link WorkflowRunStore}. Single-instance / tests only — it does NOT survive a restart
158
+ * or span replicas. Single-threaded JS already serializes `update`, so the CAS is trivially atomic here;
159
+ * `structuredClone` on the boundaries prevents aliasing (a later mutation of the caller's object — the live
160
+ * `WorkflowRun` the workflow keeps mutating — must not corrupt the stored row, and a returned run must not
161
+ * be a live reference the caller can mutate back into the store).
162
+ */
45
163
  export declare class InMemoryWorkflowRunStore implements WorkflowRunStore {
46
164
  private runs;
47
165
  put(id: string, run: WorkflowRun): Promise<void>;
@@ -54,5 +172,6 @@ export declare class InMemoryWorkflowRunStore implements WorkflowRunStore {
54
172
  maxAgeMs?: number;
55
173
  keep?: number;
56
174
  }): Promise<number>;
175
+ /** Test/inspection helper: number of stored runs. */
57
176
  get size(): number;
58
177
  }
@@ -1,3 +1,4 @@
1
+ import { assertRetentionPolicy } from "./retention-policy.js";
1
2
  export function summarizeWorkflowRun(run) {
2
3
  const latestPhase = run.phases.at(-1);
3
4
  return {
@@ -73,6 +74,7 @@ export class InMemoryWorkflowRunStore {
73
74
  return queryWorkflowRuns(this.runs.values(), scope, opts);
74
75
  }
75
76
  async reap(scope, now, opts) {
77
+ assertRetentionPolicy("WorkflowRunStore.reap", opts);
76
78
  if (opts?.maxAgeMs === undefined && opts?.keep === undefined)
77
79
  return 0;
78
80
  const terminal = [...this.runs.values()].filter((r) => r.scope === scope && isTerminalWorkflowStatus(r.status));