@sema-agent/core 5.20.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 (312) hide show
  1. package/CHANGELOG.md +9 -0
  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/send-message-tool.d.ts +92 -0
  13. package/dist/agents/session-util.d.ts +5 -0
  14. package/dist/agents/subagent-steps.d.ts +66 -0
  15. package/dist/agents/subagent.d.ts +600 -0
  16. package/dist/agents/suspend-guard.d.ts +29 -0
  17. package/dist/agents/teacher.d.ts +75 -0
  18. package/dist/agents/team.d.ts +120 -1
  19. package/dist/agents/tool-filter.d.ts +34 -0
  20. package/dist/agents/verify.d.ts +198 -0
  21. package/dist/bench/metrics.d.ts +455 -0
  22. package/dist/brain/anthropic.d.ts +30 -0
  23. package/dist/brain/circuit-breaker.d.ts +33 -0
  24. package/dist/brain/context-overflow.d.ts +60 -3
  25. package/dist/brain/degrading.d.ts +67 -0
  26. package/dist/brain/errors.d.ts +42 -0
  27. package/dist/brain/failover.d.ts +15 -0
  28. package/dist/brain/media-degrade.d.ts +39 -0
  29. package/dist/brain/model-presets.d.ts +31 -0
  30. package/dist/brain/open-responses.d.ts +19 -0
  31. package/dist/brain/openai.d.ts +46 -0
  32. package/dist/brain/reasoning.d.ts +106 -1
  33. package/dist/brain/repetition.d.ts +83 -0
  34. package/dist/brain/request-params.d.ts +56 -0
  35. package/dist/brain/retry.d.ts +46 -0
  36. package/dist/brain/routing.d.ts +12 -0
  37. package/dist/brain/status-sink.d.ts +30 -1
  38. package/dist/brain/stream-engine.d.ts +147 -0
  39. package/dist/brain/stream-shared.d.ts +34 -0
  40. package/dist/brain/terminal-cause.d.ts +31 -0
  41. package/dist/brain/timeout.d.ts +107 -0
  42. package/dist/brain/tool-call-id.d.ts +20 -0
  43. package/dist/brain/tool-call-repair.d.ts +13 -0
  44. package/dist/config/catalog.d.ts +47 -0
  45. package/dist/config/defaults.d.ts +33 -0
  46. package/dist/core/a2a-task-state.d.ts +53 -0
  47. package/dist/core/a2a.d.ts +51 -0
  48. package/dist/core/arg-summary.d.ts +62 -0
  49. package/dist/core/ask-question.d.ts +272 -2
  50. package/dist/core/auto-compaction.d.ts +467 -0
  51. package/dist/core/auto-compaction.js +20 -6
  52. package/dist/core/auto-mode-prompt-assets.d.ts +8 -0
  53. package/dist/core/auto-mode-prompt.d.ts +27 -0
  54. package/dist/core/auto-mode.d.ts +54 -3
  55. package/dist/core/auto-promote.d.ts +100 -0
  56. package/dist/core/background-agent-store.d.ts +292 -0
  57. package/dist/core/background-shell.d.ts +110 -0
  58. package/dist/core/cache-break-detector.d.ts +34 -0
  59. package/dist/core/canonical-json.d.ts +57 -0
  60. package/dist/core/checkpoint-store.d.ts +1574 -16
  61. package/dist/core/compliance.d.ts +30 -0
  62. package/dist/core/consolidate-scope.d.ts +75 -0
  63. package/dist/core/context-edit.d.ts +99 -0
  64. package/dist/core/context-guard.d.ts +46 -0
  65. package/dist/core/exec-gate.d.ts +44 -0
  66. package/dist/core/exec-output-tail.d.ts +61 -0
  67. package/dist/core/file-snapshot-store.d.ts +104 -0
  68. package/dist/core/fs-write-gate-policy.d.ts +36 -0
  69. package/dist/core/git-worktree-env.d.ts +31 -0
  70. package/dist/core/governance-codes.d.ts +19 -0
  71. package/dist/core/hooks.d.ts +608 -1
  72. package/dist/core/human-input-projection.d.ts +37 -0
  73. package/dist/core/human-input-projection.js +13 -0
  74. package/dist/core/image-downsample.d.ts +74 -0
  75. package/dist/core/locked-config.d.ts +37 -0
  76. package/dist/core/lsp-diagnostics.d.ts +77 -0
  77. package/dist/core/lsp-protocol.d.ts +29 -0
  78. package/dist/core/lsp-session.d.ts +60 -1
  79. package/dist/core/lsp.d.ts +150 -1
  80. package/dist/core/mailbox-store.d.ts +57 -0
  81. package/dist/core/mcp.d.ts +381 -0
  82. package/dist/core/media-byte-cap.d.ts +21 -0
  83. package/dist/core/memory-admission.d.ts +71 -0
  84. package/dist/core/memory-engine/content-origin.d.ts +64 -0
  85. package/dist/core/memory-engine/data-plane.d.ts +43 -0
  86. package/dist/core/memory-engine/dual-root.d.ts +24 -0
  87. package/dist/core/memory-engine/engine.d.ts +230 -0
  88. package/dist/core/memory-engine/engine.js +103 -35
  89. package/dist/core/memory-engine/file-backend.d.ts +155 -0
  90. package/dist/core/memory-engine/frontmatter.d.ts +16 -0
  91. package/dist/core/memory-engine/layout.d.ts +217 -0
  92. package/dist/core/memory-engine/memory-backend-contract.d.ts +12 -0
  93. package/dist/core/memory-engine/migrate.d.ts +9 -0
  94. package/dist/core/memory-engine/scan.d.ts +12 -0
  95. package/dist/core/memory-engine/scope-contract.d.ts +61 -1
  96. package/dist/core/memory-engine/sync-client.d.ts +87 -0
  97. package/dist/core/memory-engine/sync.d.ts +60 -0
  98. package/dist/core/memory-engine/tools.d.ts +41 -0
  99. package/dist/core/memory-engine/types.d.ts +188 -0
  100. package/dist/core/memory-recall.d.ts +141 -0
  101. package/dist/core/memory-vector.d.ts +20 -0
  102. package/dist/core/memory.d.ts +458 -0
  103. package/dist/core/message-utils.d.ts +6 -0
  104. package/dist/core/oracle-isolation.d.ts +69 -0
  105. package/dist/core/permission-rule-consent.d.ts +138 -0
  106. package/dist/core/permission-rule-model.d.ts +122 -0
  107. package/dist/core/permission-rule-store.d.ts +119 -3
  108. package/dist/core/permission-rules.d.ts +87 -1
  109. package/dist/core/present-plan-tool.d.ts +20 -0
  110. package/dist/core/pricing.d.ts +26 -0
  111. package/dist/core/property-harness.d.ts +86 -0
  112. package/dist/core/protocol-naming.d.ts +38 -0
  113. package/dist/core/protocol-table.d.ts +61 -0
  114. package/dist/core/push-queue.d.ts +1 -0
  115. package/dist/core/remote-env.d.ts +383 -1
  116. package/dist/core/retention-policy.d.ts +33 -0
  117. package/dist/core/retention.d.ts +51 -0
  118. package/dist/core/roles.d.ts +59 -0
  119. package/dist/core/runner/active-skill-scope.d.ts +34 -0
  120. package/dist/core/runner/assemble-result.d.ts +134 -0
  121. package/dist/core/runner/compaction-call-options.d.ts +97 -1
  122. package/dist/core/runner/grounding-signal.d.ts +10 -0
  123. package/dist/core/runner/image.d.ts +17 -0
  124. package/dist/core/runner/image.js +29 -15
  125. package/dist/core/runner/memory-consolidation.d.ts +60 -0
  126. package/dist/core/runner/prepare-memory.d.ts +59 -0
  127. package/dist/core/runner/prepare-task.d.ts +1011 -2
  128. package/dist/core/runner/prepare-task.js +12 -11
  129. package/dist/core/runner/prompt-suggestions.d.ts +7 -0
  130. package/dist/core/runner/runtask.d.ts +304 -3
  131. package/dist/core/runner/session-file-state-replay.d.ts +49 -0
  132. package/dist/core/runner/session-rule-policy.d.ts +58 -0
  133. package/dist/core/runner/strict-output-schema.d.ts +18 -3
  134. package/dist/core/runner/synthetic-tools.d.ts +113 -0
  135. package/dist/core/runner/teardown-bounded.d.ts +34 -0
  136. package/dist/core/runner/tool-disclosure.d.ts +227 -0
  137. package/dist/core/runner/tool-output-projection.d.ts +6 -0
  138. package/dist/core/runner/turn-attachments.d.ts +634 -0
  139. package/dist/core/runner/usage-accounting.d.ts +32 -0
  140. package/dist/core/runtime.d.ts +9 -0
  141. package/dist/core/safe-notify.d.ts +64 -0
  142. package/dist/core/safety-axis-vocab.d.ts +23 -0
  143. package/dist/core/safety-merge-corpus.d.ts +37 -0
  144. package/dist/core/scheduler.d.ts +121 -0
  145. package/dist/core/secret-env.d.ts +32 -0
  146. package/dist/core/select-model.d.ts +15 -0
  147. package/dist/core/sensitive-path-policy.d.ts +42 -0
  148. package/dist/core/session-policy-store.d.ts +94 -0
  149. package/dist/core/session-reconcile.d.ts +80 -0
  150. package/dist/core/session-store.d.ts +85 -0
  151. package/dist/core/session.d.ts +153 -0
  152. package/dist/core/shared-memory/contract.d.ts +22 -0
  153. package/dist/core/shared-memory/normalize.d.ts +123 -2
  154. package/dist/core/shared-memory/tools.d.ts +14 -0
  155. package/dist/core/shared-memory/types.d.ts +105 -0
  156. package/dist/core/shutdown-debug.d.ts +6 -0
  157. package/dist/core/side-query.d.ts +38 -0
  158. package/dist/core/side-query.js +6 -1
  159. package/dist/core/skill-tool-specifier.d.ts +72 -0
  160. package/dist/core/skills-directory.d.ts +100 -1
  161. package/dist/core/spec-contract.d.ts +89 -0
  162. package/dist/core/store-contracts/background-agent-store-contract.d.ts +31 -0
  163. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +26 -0
  164. package/dist/core/store-contracts/contract-harness.d.ts +20 -0
  165. package/dist/core/store-contracts/contract-kit-version.d.ts +27 -0
  166. package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +8 -0
  167. package/dist/core/store-contracts/mailbox-store-contract.d.ts +42 -0
  168. package/dist/core/store-contracts/tool-result-store-contract.d.ts +13 -0
  169. package/dist/core/strategy-store.d.ts +37 -0
  170. package/dist/core/stub-env.d.ts +7 -0
  171. package/dist/core/surrogate-safe-slice.d.ts +35 -0
  172. package/dist/core/task-notification.d.ts +202 -0
  173. package/dist/core/task-outcome.d.ts +53 -0
  174. package/dist/core/task-registry-agent.d.ts +337 -1
  175. package/dist/core/task-registry-monitor.d.ts +12 -0
  176. package/dist/core/task-registry-shared.d.ts +540 -0
  177. package/dist/core/task-registry.d.ts +343 -0
  178. package/dist/core/task-registry.js +13 -2
  179. package/dist/core/task-tool-shape.d.ts +44 -0
  180. package/dist/core/tighten-task-spec.d.ts +21 -0
  181. package/dist/core/tool-detach.d.ts +21 -0
  182. package/dist/core/tool-errors.d.ts +131 -0
  183. package/dist/core/tool-errors.js +4 -0
  184. package/dist/core/tool-name-aliases.d.ts +27 -0
  185. package/dist/core/tool-policy.d.ts +555 -0
  186. package/dist/core/tool-result-budget.d.ts +32 -0
  187. package/dist/core/tool-result-store.d.ts +174 -1
  188. package/dist/core/tools.d.ts +45 -0
  189. package/dist/core/trace.d.ts +323 -0
  190. package/dist/core/types.d.ts +3859 -2
  191. package/dist/core/untrusted-egress.d.ts +8 -0
  192. package/dist/core/untrusted-text.d.ts +156 -0
  193. package/dist/core/usage-window-store.d.ts +95 -0
  194. package/dist/core/version.d.ts +1 -0
  195. package/dist/core/warm-resume.d.ts +17 -0
  196. package/dist/core/wiring-manifest.d.ts +169 -0
  197. package/dist/core/with-retry.d.ts +24 -0
  198. package/dist/core/workflow-journal-store.d.ts +160 -0
  199. package/dist/core/workflow-run-store-contract.d.ts +25 -0
  200. package/dist/core/workflow-run-store.d.ts +119 -0
  201. package/dist/engine/compaction/compaction.d.ts +256 -1
  202. package/dist/engine/compaction/utils.d.ts +94 -0
  203. package/dist/engine/execution-env/kill-tree.d.ts +29 -0
  204. package/dist/engine/execution-env/node-execution-env.d.ts +94 -0
  205. package/dist/engine/harness/agent-harness.d.ts +116 -0
  206. package/dist/engine/harness/agent-harness.js +3 -14
  207. package/dist/engine/harness/messages.d.ts +15 -0
  208. package/dist/engine/harness/types.d.ts +464 -2
  209. package/dist/engine/llm/diagnostics.d.ts +4 -0
  210. package/dist/engine/llm/event-stream.d.ts +3 -0
  211. package/dist/engine/llm/index.d.ts +7 -0
  212. package/dist/engine/llm/types.d.ts +500 -3
  213. package/dist/engine/llm/validation.d.ts +3 -0
  214. package/dist/engine/loop/agent-loop.d.ts +87 -2
  215. package/dist/engine/loop/runtime-deps.d.ts +7 -0
  216. package/dist/engine/loop/types.d.ts +424 -0
  217. package/dist/engine/lsp/frame-decoder.d.ts +13 -0
  218. package/dist/engine/lsp/node-lsp-manager.d.ts +107 -0
  219. package/dist/engine/lsp/stdio-lsp-transport.d.ts +7 -0
  220. package/dist/engine/session/import-validate.d.ts +27 -0
  221. package/dist/engine/session/log-digest.d.ts +93 -0
  222. package/dist/engine/session/memory-repo.d.ts +6 -0
  223. package/dist/engine/session/memory-storage.d.ts +2 -0
  224. package/dist/engine/session/session.d.ts +75 -0
  225. package/dist/engine/session/storage-base.d.ts +8 -0
  226. package/dist/fixtures/index.d.ts +36 -0
  227. package/dist/index.d.ts +16 -1
  228. package/dist/index.js +0 -1
  229. package/dist/internal/harness-types.d.ts +6 -0
  230. package/dist/internal/harness.d.ts +11 -0
  231. package/dist/internal/llm.d.ts +6 -0
  232. package/dist/orchestration/builtin-workflows.d.ts +53 -0
  233. package/dist/orchestration/dev-vm-script-runner.d.ts +19 -0
  234. package/dist/orchestration/goal.d.ts +57 -0
  235. package/dist/orchestration/goal.js +3 -0
  236. package/dist/orchestration/run-spec.d.ts +42 -0
  237. package/dist/orchestration/run-workflow-tool.d.ts +169 -0
  238. package/dist/orchestration/workflow-governance.d.ts +61 -0
  239. package/dist/orchestration/workflow-meta.d.ts +28 -0
  240. package/dist/orchestration/workflow-observe.d.ts +60 -0
  241. package/dist/orchestration/workflow-primitives.d.ts +23 -1
  242. package/dist/orchestration/workflow-sandbox-conformance.d.ts +38 -0
  243. package/dist/orchestration/workflow-script-runner.d.ts +88 -0
  244. package/dist/orchestration/workflow-script-store.d.ts +98 -0
  245. package/dist/orchestration/workflow-size-guideline.d.ts +67 -0
  246. package/dist/orchestration/workflow-types.d.ts +169 -2
  247. package/dist/orchestration/workflow.d.ts +358 -0
  248. package/dist/prompt-assembly/artifact-store.d.ts +33 -0
  249. package/dist/prompt-assembly/artifact.d.ts +25 -0
  250. package/dist/prompt-assembly/assemble.d.ts +20 -0
  251. package/dist/prompt-assembly/composer.d.ts +29 -0
  252. package/dist/prompt-assembly/epoch.d.ts +55 -1
  253. package/dist/prompt-assembly/event-registry.d.ts +35 -0
  254. package/dist/prompt-assembly/explain.d.ts +12 -0
  255. package/dist/prompt-assembly/tool-catalog.d.ts +38 -0
  256. package/dist/prompt-assembly/turn-snapshot.d.ts +40 -0
  257. package/dist/prompt-assembly/types.d.ts +115 -0
  258. package/dist/prompts/coordinator.d.ts +27 -0
  259. package/dist/prompts/default.d.ts +539 -0
  260. package/dist/prompts/simple-sections.d.ts +45 -0
  261. package/dist/prompts/supervisor.d.ts +66 -0
  262. package/dist/scenarios/env.d.ts +28 -0
  263. package/dist/scenarios/full-body.d.ts +50 -0
  264. package/dist/scenarios/scenario-registry.d.ts +60 -0
  265. package/dist/scenarios/teacher-quickstart.d.ts +27 -0
  266. package/dist/server/http.d.ts +17 -0
  267. package/dist/stores/cc/lockfile.d.ts +6 -0
  268. package/dist/stores/cc/mailbox-store.d.ts +8 -0
  269. package/dist/stores/cc/roster-adapter.d.ts +4 -0
  270. package/dist/stores/cc/sidecar-transcript.d.ts +66 -0
  271. package/dist/stores/file/background-agent-store.d.ts +24 -0
  272. package/dist/stores/file/checkpoint-store.d.ts +38 -0
  273. package/dist/stores/file/file-snapshot-store.d.ts +25 -0
  274. package/dist/stores/file/fs-atomic.d.ts +155 -0
  275. package/dist/stores/file/index.d.ts +89 -0
  276. package/dist/stores/file/mailbox-store.d.ts +36 -0
  277. package/dist/stores/file/memory-store.d.ts +82 -0
  278. package/dist/stores/file/permission-rule-store.d.ts +79 -0
  279. package/dist/stores/file/session-policy-store.d.ts +28 -0
  280. package/dist/stores/file/session-store.d.ts +40 -0
  281. package/dist/stores/file/shared-ledger.d.ts +83 -0
  282. package/dist/stores/file/tool-result-store.d.ts +11 -0
  283. package/dist/stores/file/usage-window-store.d.ts +18 -0
  284. package/dist/stores/file/workflow-journal-store.d.ts +100 -0
  285. package/dist/stores/file/workflow-run-store.d.ts +11 -0
  286. package/dist/tools/fs/bash-readonly-classifier.d.ts +282 -0
  287. package/dist/tools/fs/encoding.d.ts +60 -0
  288. package/dist/tools/fs/fs-bash.d.ts +133 -0
  289. package/dist/tools/fs/fs-pdf.d.ts +28 -0
  290. package/dist/tools/fs/fs-search-tools.d.ts +5 -0
  291. package/dist/tools/fs/fs-shared.d.ts +360 -0
  292. package/dist/tools/fs/fs-write.d.ts +16 -0
  293. package/dist/tools/fs/gh-rate-limit.d.ts +22 -0
  294. package/dist/tools/fs/index.d.ts +79 -0
  295. package/dist/tools/fs/notebook.d.ts +56 -0
  296. package/dist/tools/fs/pdf.d.ts +70 -0
  297. package/dist/tools/fs/pdf.js +8 -2
  298. package/dist/tools/fs/repo-map.d.ts +10 -0
  299. package/dist/tools/fs/safety.d.ts +376 -0
  300. package/dist/tools/fs/search.d.ts +131 -0
  301. package/dist/tools/loop-tick.d.ts +24 -0
  302. package/dist/tools/monitor.d.ts +55 -0
  303. package/dist/tools/scheduler-tools.d.ts +51 -0
  304. package/dist/tools/sql-adapters.d.ts +18 -0
  305. package/dist/tools/sql.d.ts +25 -0
  306. package/dist/tools/task-list.d.ts +77 -0
  307. package/dist/tools/todo.d.ts +8 -0
  308. package/dist/tools/web.d.ts +184 -0
  309. package/dist/tools/worktree.d.ts +81 -0
  310. package/package.json +2 -2
  311. package/dist/tools/gitea-issue.d.ts +0 -13
  312. package/dist/tools/gitea-issue.js +0 -75
@@ -1,35 +1,126 @@
1
+ /**
2
+ * design/99 §E6 — per-session permission rules: a durable, session-scoped policy narrowing folded into the
3
+ * task-time {@link import("./tool-policy.js").ToolPolicy} (see {@link import("./runner/session-rule-policy.js").createSessionRulePolicy}).
4
+ *
5
+ * Why core-owned (not a parallel service store): the rules must be ENFORCED by the same policy fold that runs
6
+ * every tool call, so storage and enforcement can't drift (the [210] capability-token invariant). core defines
7
+ * the seam + an InMemory default; a deployment supplies a DURABLE cross-replica backend (its CAS-rev semantics
8
+ * must match this InMemory one byte-for-byte — a cross-backend equivalence test is the contract).
9
+ *
10
+ * Safety model:
11
+ * - **tighten-only by default**: a NORMAL write may only ADD denies / NARROW allowlists / NARROW allowDirs. A
12
+ * write that LOOSENS (removes a deny, widens an allowlist/allowDirs) is REFUSED unless it is an OPERATOR
13
+ * write. core has NO role system — `operator` is a boolean the caller FREEZES from a verified principal
14
+ * (mirrors `steer.trusted`); core never adjudicates operator-ness, it only honors the flag.
15
+ * - **CAS rev** (optimistic concurrency): a `putRules` with a stale `expectedRev` is rejected, so two replicas
16
+ * can't lose an update (mirrors the checkpoint store's `resolve` OCC).
17
+ * - **structured fields only**: core interprets `toolAllow/Deny`, `allowDirs`, `commandAllow/Deny` (argv[0]
18
+ * names). A richer rule DSL is the service's/profile's job — core grows no rule interpreter.
19
+ * - **NOT A SANDBOX**: command rules inherit the coarse argv[0] matching (wrappers like `sh -c`/`sudo` bypass);
20
+ * the real isolation boundary is the executionEnv, never these rules.
21
+ */
22
+ /** The structured per-session rules. All optional; an absent field imposes no constraint of that kind. */
1
23
  export interface SessionPermissionRules {
24
+ /** If set, ONLY these tool names are allowed (others denied). A narrowing allowlist. */
2
25
  toolAllow?: string[];
26
+ /** Tool names always denied (deny wins). */
3
27
  toolDeny?: string[];
28
+ /** If set, WRITE tools (`write_file`/`edit_file`) may only target paths within these dirs; a write-capable
29
+ * tool that can't be path-confined (e.g. `bash`) is denied while this is set. Stored RAW; resolved at run time. */
4
30
  allowDirs?: string[];
31
+ /** If set, ONLY these `bash` command names (argv[0]) are allowed. */
5
32
  commandAllow?: string[];
33
+ /** `bash` command names (argv[0]) always denied. */
6
34
  commandDeny?: string[];
7
35
  }
36
+ /** {@link SessionPermissionRules} plus the monotonic `rev` the store stamps (the OCC key). */
8
37
  export interface StoredSessionRules extends SessionPermissionRules {
9
38
  rev: number;
10
39
  }
40
+ /** Options for {@link SessionPolicyStore.putRules}. */
11
41
  export interface PutRulesOptions {
42
+ /** CAS: reject the write if the stored `rev` is not this (optimistic concurrency). Omit to skip the check. */
12
43
  expectedRev?: number;
44
+ /** When true, this write MAY loosen the rules (remove a deny / widen an allowlist or allowDirs). A NORMAL
45
+ * (non-operator) write is TIGHTEN-ONLY. core does NOT verify operator-ness — the caller (service) FREEZES
46
+ * this from a verified, role-checked principal (mirrors `steer.trusted`). */
13
47
  operator?: boolean;
14
48
  }
49
+ /** Typed error from {@link SessionPolicyStore.putRules}. `conflict` = stale `expectedRev`; `loosen_forbidden`
50
+ * = a non-operator write tried to relax the rules. */
15
51
  export declare class SessionPolicyError extends Error {
16
52
  readonly code: "conflict" | "loosen_forbidden";
17
53
  constructor(code: "conflict" | "loosen_forbidden", message: string);
18
54
  }
55
+ /**
56
+ * The seam a deployment implements for DURABLE, cross-replica per-session rules. Unset on `RunnerDeps` ⇒ the
57
+ * feature is OFF (no rules read, zero behavior change). Keyed on `(sessionId, principal-owner)`: core passes
58
+ * the running task's `principal` as the owner key; ownership/operator AUTHORIZATION is the implementation's job
59
+ * (core has no identity system). `getRules` returns the rules to ENFORCE for a task; `putRules` writes them.
60
+ */
61
+ /** A (principal, rules) pair for one session — the unit {@link SessionPolicyStore.listBySession} returns. */
19
62
  export interface SessionRulesRecord {
63
+ /** The principal these rules apply to (undefined = the session-wide default). */
20
64
  principal: string | undefined;
21
65
  rules: StoredSessionRules;
22
66
  }
23
67
  export interface SessionPolicyStore {
24
68
  getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
25
69
  putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
70
+ /**
71
+ * 2c session-sync ([271]): every (principal, rules) record for `sessionId` across ALL principals — `getRules`
72
+ * is per-(sessionId, principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy.
73
+ * The importer replays each via `putRules(sessionId, record.principal, record.rules)`. Optional — a backend
74
+ * that supports cross-backend policy export implements it (local backend uses the in-memory/file impl).
75
+ */
26
76
  listBySession?(sessionId: string): Promise<SessionRulesRecord[]>;
77
+ /**
78
+ * 2c session-sync, the ERASE half (server C1b, 2026-07-26): remove EVERY (principal, rules) record for
79
+ * `sessionId`. An importer that OVERWRITES a session must be able to erase the abandoned branch's policy
80
+ * rows — without this seam they survive the overwrite and keep enforcing the old branch's rules against
81
+ * the new content. Optional like {@link listBySession} (a sync-capable backend implements both); the two
82
+ * bundled impls do. Idempotent: deleting a session with no records is a no-op.
83
+ */
27
84
  deleteBySession?(sessionId: string): Promise<void>;
28
85
  }
86
+ /**
87
+ * LEXICAL path containment for the tighten gate. **Must agree with the runtime's view of what a dir IS** (the
88
+ * runtime resolves `..`/`.`/`//` via `canonicalizeTarget`): both args are `posix.normalize`d FIRST so a
89
+ * non-operator can't smuggle a write-confinement OUTSIDE the prior dir by writing `<prior>/../etc` — which is
90
+ * lexically "within `<prior>`" by a naive `startsWith` but canonicalizes to `/etc` at run time (the tighten
91
+ * gate must never UNDER-report a loosening).
92
+ *
93
+ * The `..`-resolving `posix.normalize` pass is THIS function's own job (the tighten gate's `..`-smuggling
94
+ * defense above) and is NOT shared with the runtime's `isWithin` (session-rule-policy.ts), which compares
95
+ * already-canonicalized keys and never resolves `..` itself — the two functions agree on every OTHER input
96
+ * (same win-family fold, same prefix semantics) but diverge on inputs that still carry an unresolved `..`,
97
+ * which is exactly the tighten-gate-only case this function exists to catch.
98
+ *
99
+ * RESIDUAL (documented, bounded): the gate is LEXICAL and cannot follow symlinks at write time, while the
100
+ * runtime IS symlink-aware. A stored allowDir whose LAST component is a pre-existing symlink to outside the
101
+ * prior set would be seen as "within" by this gate yet resolve elsewhere at run time. This is bounded: while an
102
+ * `allowDirs` confinement is active the runtime DENIES `bash` (non-path-confinable), so a confined non-operator
103
+ * cannot CREATE such a symlink — it must pre-exist on the deployment's filesystem. Operators / the durable
104
+ * backend should keep `allowDirs` to real directories; a future symlink-aware write-time canonicalize closes it.
105
+ */
29
106
  declare function lexWithin(root: string, p: string): boolean;
107
+ /** Normalize a rules object's `allowDirs` (lexical `posix.normalize`) so stored rules are `..`-free and the
108
+ * tighten diff compares the same canonical-ish form the runtime resolves. */
30
109
  export declare function normalizeRules(rules: SessionPermissionRules): SessionPermissionRules;
110
+ /**
111
+ * Pure tighten-only diff: the reasons `next` LOOSENS `prior` (empty ⇒ `next` is a pure tightening or no-op).
112
+ * Direction per field — deny-lists: removing an entry loosens. allowlists: removing the list (→ everything
113
+ * allowed) or adding an entry outside the prior set loosens; prior-undefined → any defined next only narrows.
114
+ * allowDirs: removing confinement, or adding a dir not within some prior dir, loosens (coarse raw containment).
115
+ */
31
116
  export declare function loosenReasons(prior: SessionPermissionRules, next: SessionPermissionRules): string[];
117
+ /** strip any caller-passed `rev` from a rules object so the store is the sole authority on it. */
32
118
  export declare function stripRev(rules: SessionPermissionRules): SessionPermissionRules;
119
+ /**
120
+ * In-process default {@link SessionPolicyStore} (single-replica). A deployment that needs cross-replica
121
+ * durability supplies its own backend with the SAME CAS-rev + tighten-only semantics. Single-threaded JS makes
122
+ * the read-check-write trivially atomic here; a durable backend folds it into a CAS WHERE clause.
123
+ */
33
124
  export declare class InMemorySessionPolicyStore implements SessionPolicyStore {
34
125
  private readonly map;
35
126
  private key;
@@ -38,6 +129,9 @@ export declare class InMemorySessionPolicyStore implements SessionPolicyStore {
38
129
  listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
39
130
  deleteBySession(sessionId: string): Promise<void>;
40
131
  }
132
+ /** Test-only escape hatch for the module-private tighten-gate containment predicate (mirrors the
133
+ * `__testables` convention in file-snapshot-store.ts) — lets a regression pin the fold's win-family
134
+ * conditionality directly rather than only through `loosenReasons`'s allowDirs branch. */
41
135
  export declare const __testables: {
42
136
  lexWithin: typeof lexWithin;
43
137
  };
@@ -1,19 +1,99 @@
1
1
  import type { AgentMessage } from "../internal/harness-types.js";
2
2
  import type { Session } from "./session.js";
3
3
  import type { ToolEffect } from "./types.js";
4
+ /**
5
+ * Wake/resume side-effect reconciliation.
6
+ *
7
+ * Problem (design/07 · C3): a task can be interrupted (crash, kill, timeout, lost instance) in the
8
+ * window between an assistant message with tool calls being persisted and that turn's tool results
9
+ * being flushed. The session event log then ends with one or more **orphan tool calls** — a tool
10
+ * call with no matching `toolResult`. On wake this is two bugs at once:
11
+ * 1. Most chat APIs reject a request whose assistant tool_call is not followed by a tool result.
12
+ * 2. The tool's `execute` may already have run (the side effect happened) — or may not have. The
13
+ * outcome is genuinely unknown.
14
+ *
15
+ * Contract enforced here:
16
+ * - **Never auto-replay a tool.** Reconstruction (`buildSessionContext`) already does not re-run
17
+ * tools; this guarantees we also never synthesize a "success" that hides an unknown outcome.
18
+ * - For each orphan, append a synthetic `toolResult` (isError) that tells the model the call was
19
+ * interrupted and its outcome is unknown — making the transcript API-valid AND handing the
20
+ * decision to the model under its verification discipline.
21
+ * - Use the tool's {@link ToolEffect} to tune the message: `read` carries no risk at all; `idempotent`
22
+ * is replayable (re-issuing converges the state, but the interrupted attempt may have partially
23
+ * landed — RB-221 [1937]); `write` (and unknown tools, conservatively) must be verified/confirmed
24
+ * before repeating.
25
+ *
26
+ * This runs on `acquire` of a resumed session, before the next turn starts. New/empty sessions and
27
+ * cleanly-finished sessions have no orphans and are left untouched.
28
+ */
29
+ /** One broken half of a tool call/result pair in the active branch (see {@link OrphanToolCall.kind}). */
4
30
  export interface OrphanToolCall {
5
31
  toolCallId: string;
6
32
  toolName: string;
33
+ /**
34
+ * RB-398-a part 2 (blackboard [2105]): which half of the pair is missing.
35
+ * - absent (the historical shape) = a **call-orphan**: an assistant `toolCall` with no matching
36
+ * `toolResult` — the reconcile-actionable kind ({@link reconcileInterruptedSession} closes it
37
+ * with a synthetic interrupted result).
38
+ * - `"result"` = a **result-orphan**: a `toolResult` with no matching assistant `toolCall` — a
39
+ * protocol-invalid context every mainstream chat API rejects. The oracle used to walk only
40
+ * assistant→result, so it reported a context in exactly this state as clean while the
41
+ * request-time sweeper (`context-guard`'s `dropOrphanToolResults`) was silently deleting the
42
+ * result. NOT reconcile-actionable (appending another result would not repair it); consumers
43
+ * deciding on repair must filter on this field.
44
+ * Additive: absent = call-orphan, so pre-existing consumers and persisted expectations are
45
+ * byte-identical for the historical direction.
46
+ */
7
47
  kind?: "result";
8
48
  }
49
+ /** The machine-readable discriminator on a reconcile-appended result — `details.errorKind` on the persisted
50
+ * message and `errorCode` on the synthetic `tool_end` frame, so a consumer never parses the text. Two-valued
51
+ * (a third value needs a cross-repo slip, see {@link reconcileInterruptedSession}). */
9
52
  export type ReconciledErrorKind = "interrupted_never_started" | "interrupted_outcome_unknown";
53
+ /** One orphan closed by {@link reconcileInterruptedSession}, with everything the caller needs to mint the
54
+ * matching stream frame WITHOUT re-reading the session. */
10
55
  export type RecoveredOrphan = OrphanToolCall & {
56
+ /** The persisted toolResult entry id (message-identity Phase 1). */
11
57
  entryId: string;
58
+ /** The model-facing text that was persisted — the synthetic `tool_end`'s `output` body. Handed back
59
+ * because the caller's frame is otherwise BODY-LESS: a consumer rendering tool output from frames saw an
60
+ * empty result for every interrupted call, while the model-facing transcript carried the full
61
+ * `[INTERRUPTED]` explanation (the two faces disagreed about the same call). */
12
62
  text: string;
63
+ /** Which of the two honest states this orphan was closed in — the frame's `errorCode`. */
13
64
  errorKind: ReconciledErrorKind;
14
65
  };
66
+ /** Outcome of reconciling a resumed session. */
15
67
  export interface ReconcileReport {
68
+ /** Orphan calls that were closed with a synthetic interrupted result (empty when nothing to do). `entryId` =
69
+ * the persisted toolResult entry id — message-identity Phase 1: the caller emits a `message_committed` for each
70
+ * so a consumer's `entryId → message` map stays complete for these reconcile-appended results (they render a
71
+ * `tool_end`). They are never a compaction floor (`toolResult` is excluded from cut points), so this is map
72
+ * completeness, not `preserved_segment` correctness. */
16
73
  recovered: RecoveredOrphan[];
17
74
  }
75
+ /**
76
+ * Find assistant tool calls in `messages` that have no matching `toolResult`.
77
+ * Order follows first appearance in the transcript (orphans are normally only at the tail).
78
+ *
79
+ * `suspendedBatch` (design/45 §15.2 net-add #7): the set of tool-call ids belonging to a **deliberately
80
+ * suspended** batch (the pending + not-yet-started calls of a durable-checkpoint suspension). These are
81
+ * NOT orphans — they are waiting to be resumed via `runner.resume()`, not crash-interrupted. Wake-path
82
+ * reconcile must skip them, or it would append `[INTERRUPTED]` results and **destroy the suspended
83
+ * batch** (the bug this guard fixes: the wake/resume naming overload became real data destruction).
84
+ * `runner.resume()` uses a checkpoint-aware entry that bypasses this reconcile entirely for those calls.
85
+ */
18
86
  export declare function findOrphanToolCalls(messages: AgentMessage[], suspendedBatch?: ReadonlySet<string>): OrphanToolCall[];
87
+ /**
88
+ * Reconcile a resumed session's active branch: close any orphan tool calls with a synthetic
89
+ * interrupted `toolResult` (never re-running the tool). Returns what was recovered.
90
+ *
91
+ * @param toolEffects optional name→effect map (from the task's tools). Unknown names are treated as
92
+ * `write` — the conservative default, so an unrecognized interrupted call is never said to be safe.
93
+ * @param startedToolCallIds optional set of tool-call ids whose execution START the caller observed
94
+ * during the run being reconciled (the live-abort caller has this knowledge; the wake/crash path
95
+ * does not — the previous process is gone). When provided, an orphan absent from the set provably
96
+ * never started: it is closed with a "never executed — safe to re-issue" result instead of the
97
+ * conservative unknown-outcome wording. When omitted, every orphan keeps the conservative texts.
98
+ */
19
99
  export declare function reconcileInterruptedSession(session: Session, toolEffects?: Map<string, ToolEffect>, suspendedBatch?: ReadonlySet<string>, startedToolCallIds?: ReadonlySet<string>): Promise<ReconcileReport>;
@@ -3,41 +3,126 @@ import { type AcquiredSession, type SessionStore, type SessionStoreSummary } fro
3
3
  import type { StoreDurability } from "./checkpoint-store.js";
4
4
  import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
5
5
  export { SESSION_DEFAULT_TTL_DAYS };
6
+ /** What idle-eviction and `release` do to the underlying repo. */
6
7
  export type EvictPolicy = "delete" | "forget";
7
8
  export interface TtlSessionStoreOptions {
9
+ /** Idle TTL before a cached session is evicted. Default 7 days. */
8
10
  defaultTtlDays?: number;
11
+ /** Optional background sweep interval (ms). Off by default (sweeps opportunistically on acquire). */
9
12
  sweepIntervalMs?: number;
13
+ /**
14
+ * Backing session repo. Default `InMemorySessionRepo` (process-only, lost on restart). Supply a
15
+ * **durable** repo (e.g. a TiDB-backed `SessionRepo`, or the vendored file repo) to persist
16
+ * sessions: `acquire` then **resumes** an existing session via `repo.open`, so any stateless
17
+ * runner instance — even after a restart or on another replica — continues the same conversation.
18
+ */
10
19
  repo?: SessionRepo;
20
+ /**
21
+ * What idle-eviction and `release` do to the repo:
22
+ * - `"delete"` — remove the session from the repo too (correct for the throwaway in-memory repo).
23
+ * - `"forget"` — only drop the in-memory cache; the **durable** backend keeps the history so it can
24
+ * be resumed later (the backend runs its own retention/GC).
25
+ *
26
+ * Defaults to `"delete"` for the built-in in-memory repo and `"forget"` when a custom `repo` is
27
+ * supplied — so a durable store never silently deletes history on an idle timer.
28
+ */
11
29
  evict?: EvictPolicy;
30
+ /**
31
+ * design/173 §2.3 — the store's {@link StoreDurability} declaration. This cache layer cannot
32
+ * inspect whether the injected {@link repo} persists across processes, so the DEPLOYMENT that
33
+ * wired the repo states the fact (e.g. `FileStorageBackend` passes `"durable"`). Defaults:
34
+ * without a custom `repo` the truth is knowable — `"process-local"` is declared; with a custom
35
+ * `repo` and no explicit value the store declares NOTHING, which the wiring manifest reads
36
+ * fail-closed as process-local (under-promise, never over-promise).
37
+ */
12
38
  durability?: StoreDurability;
13
39
  }
40
+ /**
41
+ * TTL-cached {@link SessionStore} over a pluggable {@link SessionRepo}.
42
+ *
43
+ * Sessions are cached in process memory and evicted after `defaultTtlDays` of inactivity. With the
44
+ * default in-memory repo they live only in this process (lost on restart). Supply a **durable** repo
45
+ * and the same caching/concurrency logic becomes a "session center": on `acquire`, a session not in
46
+ * the live cache is **resumed from the repo** (`repo.open`) rather than recreated — so a stateless
47
+ * runner resumes a conversation after a restart or across replicas. The optimistic-lock seam
48
+ * (`SessionWriteOptions.expectedLeafId` → `SessionError("conflict")`) keeps two replicas writing the
49
+ * same session safe; the Runner reconciles a lost CAS and retries.
50
+ *
51
+ * See `design/10-会话持久化与会话中心.md` for the durable-backend (TiDB) contract.
52
+ */
14
53
  export declare class TtlSessionStore implements SessionStore {
54
+ /** Managed-retention declaration (ruled 2026-08-05, retention.ts): with a custom repo the default
55
+ * evict policy is `forget` — cache eviction only, the durable rows live on — and there is no
56
+ * tenant-scoped scheduled-deletion contract on any arm. Honestly `"none"`: a deployment with a
57
+ * LOCKED retention policy refuses to start over this store. (The `SessionStore` interface itself
58
+ * is declaration-agnostic; the retention face is the structural `RetentionDeclaring` shape.) */
15
59
  readonly retention: "none";
60
+ /** design/173 §2.3 — see {@link TtlSessionStoreOptions.durability} for the declaration rules. */
16
61
  readonly durability?: StoreDurability;
17
62
  private repo;
18
63
  private entries;
64
+ /** Session-ownership rows (the multi-tenant admission capability face — see SessionStore.ownerOf).
65
+ * Deliberately NOT part of `entries`: an evicted-but-durable session (`forget` / evict:"forget")
66
+ * still EXISTS, and its ownership row outliving the cache entry is what keeps a live session from
67
+ * reading as `undefined` (= re-claimable by anyone). Rows die only when the repo's history does. */
19
68
  private owners;
69
+ /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) share one session. */
20
70
  private pending;
71
+ /** Sessions pinned by a design/45 checkpoint — skipped by idle sweep until unpinned (B6/§5). */
21
72
  private pinned;
22
73
  private defaultTtlMs;
23
74
  private evictPolicy;
24
75
  private sweepTimer?;
25
76
  constructor(opts?: TtlSessionStoreOptions);
77
+ /** Get an existing session by id (resuming from the repo if needed), or create one. */
26
78
  acquire(sessionId?: string, opts?: {
27
79
  requireExisting?: boolean;
28
80
  }): Promise<AcquiredSession>;
81
+ /** Resume an existing session from the repo, or create it if the backend has no such id (unless
82
+ * `requireExisting`, in which case a genuinely-missing id fails loud instead — design/114 Phase3). */
29
83
  private openOrCreate;
30
84
  private createAndStore;
85
+ /** Refresh the idle timer for a session. */
31
86
  touch(sessionId: string): void;
87
+ /** Record the most recent task run on a cached session (the `/resume` `lastRunId`); also bumps the idle
88
+ * timer. Best-effort — a no-op if the session isn't cached here (the run will still be re-attachable via
89
+ * its checkpoint). */
32
90
  noteTaskRun(sessionId: string, taskId: string): void;
91
+ /** List the live cache as {@link SessionStoreSummary} projections, newest-first by `lastActiveAt` (so a
92
+ * shell's `/resume` shows recent sessions + their last run first). The in-memory store lists only its
93
+ * live cache, not evicted/durable history — a durable backend overrides this to list persisted sessions. */
33
94
  list(): Promise<SessionStoreSummary[]>;
95
+ /**
96
+ * design/110 (F3) — FORK a session: copy `sourceId`'s committed history into a NEW session and return its id
97
+ * (or `null` when the source doesn't exist). Delegates to the backing repo's `fork`, so a durable repo persists the
98
+ * branch; the in-memory repo holds it in-process. Registering the result in this cache makes `acquire(forkedId)`
99
+ * hit immediately — the synchronous child run that `Agent(subagent_type:"fork")` launches resumes the fork without a repo round-trip.
100
+ *
101
+ * Adding this method is what makes {@link hasSessionFork} true for a `TtlSessionStore` wrapping ANY repo, so the
102
+ * fork capability mounts (it was INERT before — the durable repo had `fork` but the store never exposed it).
103
+ */
34
104
  fork(sourceId: string, owner?: string | null): Promise<string | null>;
105
+ /** Drop a session from the live set. With a durable repo (`evict: "forget"`) the history is kept. */
35
106
  release(sessionId: string): Promise<void>;
107
+ /** Drop only the cached view — the repo keeps the session regardless of `evictPolicy`, so a re-acquire
108
+ * re-opens the SAME history (B-17: the reconcile retry / prepare-throw paths must never delete). Cost
109
+ * on the default in-memory repo: ONE retained session object **per forgotten id** until process end
110
+ * (invisible to `size`/`sweep`, which track the cache) — accumulation across many distinct forgotten
111
+ * ids is the accepted trade-off vs silent history loss; deployments with churn use a durable repo,
112
+ * where retention is the backend's normal job. A pinned id stays pinned (a forget must never expose a
113
+ * suspended task's session to sweep eviction). */
36
114
  forget(sessionId: string): void;
115
+ /** Pin a session so idle sweep can't evict it while a design/45 checkpoint awaits resume (§5). */
37
116
  pin(sessionId: string): void;
117
+ /** Release a design/45 pin (on resume or checkpoint expiry); the session resumes normal idle eviction. */
38
118
  unpin(sessionId: string): void;
119
+ /** Multi-tenant admission capability (see the SessionStore contract for the three-state semantics
120
+ * and the first-write-wins requirement — both are load-bearing for the deployment's owner gate). */
39
121
  ownerOf(sessionId: string): Promise<string | null | undefined>;
122
+ /** First-write-wins idempotent upsert; a synchronous Map write is atomic per the contract. Makes no
123
+ * authorization decision — the authorizer re-reads {@link ownerOf} and judges there. */
40
124
  register(sessionId: string, owner: string | null): Promise<void>;
125
+ /** Evict idle-expired sessions from the cache. Deletes durable history only when `evict: "delete"`. */
41
126
  sweep(now?: number): void;
42
127
  get size(): number;
43
128
  dispose(): void;
@@ -7,39 +7,192 @@ export { SessionError } from "../internal/harness.js";
7
7
  export type { SessionWriteOptions } from "../internal/harness.js";
8
8
  import type { Session } from "../internal/harness.js";
9
9
  import type { SessionTreeEntry } from "../internal/harness.js";
10
+ /** True when `err` is a session optimistic-lock conflict (a concurrent writer won the branch leaf). */
10
11
  export declare function isSessionConflict(err: unknown): boolean;
12
+ /** A session handed back by a {@link SessionStore}: the harness `Session` plus its resolved id. */
11
13
  export interface AcquiredSession {
12
14
  session: Session;
13
15
  sessionId: string;
14
16
  }
17
+ /**
18
+ * A lightweight, read-only projection of one session for a `/resume` list (shell K-5 — re-attach without an
19
+ * N+1 walk). Returned in bulk by {@link SessionStore.list}, newest-first by `lastActiveAt` by convention.
20
+ * `lastTaskId` is the engine's term for what the service/wire surfaces as the session's **`lastRunId`** — the
21
+ * most recent task run on this session, so a shell re-attaches in ONE hop.
22
+ *
23
+ * Named `*StoreSummary` (not the bare `SessionSummary` an earlier version used) because that name collided
24
+ * with an unrelated, differently-shaped `SessionSummary` downstream (the SDK's wire GET /v1/sessions row) —
25
+ * same name, two shapes, the exact "同名异形" pattern a cross-repo cleanup was independently eradicating
26
+ * elsewhere (黑板 [1912]/[1913]/[1914]). This type is this store interface's own projection, never itself
27
+ * serialized onto the wire — a service backend re-projects/renames its fields (`lastTaskId` → `lastRunId`)
28
+ * when building the actual wire response.
29
+ */
15
30
  export interface SessionStoreSummary {
31
+ /** The session id. */
16
32
  sessionId: string;
33
+ /** When the session was created ({@link SessionMetadata.createdAt}, ISO string); absent when the backend's
34
+ * cache view doesn't carry it. */
17
35
  createdAt?: string;
36
+ /** Epoch ms the session was last acquired / touched / ran — the newest-first sort key for "recent sessions". */
18
37
  lastActiveAt: number;
38
+ /** The most recent task run on this session (core's `taskId`; the service projects it as `lastRunId`).
39
+ * Absent until a task has run on the session (recorded via {@link SessionStore.noteTaskRun}). */
19
40
  lastTaskId?: string;
41
+ /** F7 fork-GC seam: the parent session this one was forked from ({@link SessionMetadata.forkedFrom}).
42
+ * Lets a consumer render/reap fork lineage; absent on non-forked sessions and on cache views that
43
+ * predate the fork. The durable source of truth is the repo's persisted metadata. */
20
44
  forkedFrom?: string;
21
45
  }
46
+ /**
47
+ * The session lifecycle/caching layer the {@link Runner} depends on.
48
+ *
49
+ * `TtlSessionStore` is the default (in-memory, 7-day idle TTL). Provide a custom implementation
50
+ * to back sessions with durable, external storage (the "Session center"): `acquire` reconstructs
51
+ * a session from the persisted event log so any stateless runner instance can resume it.
52
+ *
53
+ * Implementations must be safe under concurrent `acquire` of the same id (return one session).
54
+ */
22
55
  export interface SessionStore {
56
+ /**
57
+ * design/173 §2.3 — durability declaration (see
58
+ * {@link import("./checkpoint-store.js").StoreDurability}): whether sessions this store accepts
59
+ * SURVIVE A PROCESS RESTART. That is the whole claim of this axis — multi-replica
60
+ * coordination/concurrency is a separate backend property this field does not license (review
61
+ * fold r1-F2/r2-F3; a single-instance file-backed store declares `"durable"` honestly).
62
+ * Declaration, never duck-typing (the wiring manifest relays it verbatim — it never sniffs class
63
+ * names); ABSENT is read fail-closed as `"process-local"` (under-promise, never over-promise).
64
+ * `TtlSessionStore` over a durable {@link SessionRepo} declares via its `durability` option — the
65
+ * store cannot inspect the repo's persistence, so the DEPLOYMENT that wired the repo states the
66
+ * fact.
67
+ */
23
68
  readonly durability?: import("./checkpoint-store.js").StoreDurability;
69
+ /** Get an existing session by id, or create one (optionally with a caller-supplied id). design/114 Phase3:
70
+ * `opts.requireExisting` ⇒ a store MUST fail loud (throw a `not_found` {@link SessionError}) on a genuinely
71
+ * missing id rather than create-on-miss — so a reuse-style warm-resume of a gone session errors instead of
72
+ * silently starting a fresh empty run. A store that cannot honor it MUST still not silently create (either
73
+ * implement the check or reject the option). */
24
74
  acquire(sessionId?: string, opts?: {
25
75
  requireExisting?: boolean;
26
76
  }): Promise<AcquiredSession>;
77
+ /** Mark a session recently active (resets idle TTL where applicable). May be async for durable stores. */
27
78
  touch(sessionId: string): void | Promise<void>;
79
+ /** Drop a session from the live set (e.g. a throwaway task). Durable history may be retained by the backend. */
28
80
  release(sessionId: string): Promise<void>;
81
+ /**
82
+ * Drop only the CACHED view of a session — **never** deletes history (unlike `release`, which with the
83
+ * in-memory `TtlSessionStore`'s default `evict: "delete"` policy removes the session from the repo too).
84
+ * The Runner uses this where the intent is "discard a stale view and re-wake", and on a prepare-phase
85
+ * throw after acquire (audit B-17 + the 1.93.0 throw-cleanup contract gap: calling `release` there
86
+ * silently DELETED an existing session's whole history on the default store — the re-acquire then
87
+ * continued on an empty same-id session). **Optional, but custom stores SHOULD implement it**: when
88
+ * absent the Runner performs NO fallback (it never substitutes `release`, whose contract permits
89
+ * deletion — B-17 council) — the cached view is simply not dropped, so a persistent reconcile conflict
90
+ * exhausts its retry budget and propagates instead of risking history. A store whose `release` is
91
+ * already history-safe can implement `forget = release`.
92
+ */
29
93
  forget?(sessionId: string): void | Promise<void>;
94
+ /**
95
+ * Protect a session from idle eviction while a design/45 checkpoint references it (a suspended task
96
+ * must still be resumable). `pin` before suspend, `unpin` on resume/expiry. **Optional**: a durable
97
+ * backend that never idle-GCs sessions (service's `tidb-session-store`) implements both as no-ops and
98
+ * is still safe; only the in-memory `TtlSessionStore` needs a real pin (its `sweep` skips pinned ids).
99
+ * See design/45 §5 / M3.
100
+ */
30
101
  pin?(sessionId: string): void | Promise<void>;
31
102
  unpin?(sessionId: string): void | Promise<void>;
103
+ /**
104
+ * Record that a task run STARTED on a session (the Runner calls this at task start, when both ids are
105
+ * known) so the store can surface it as {@link SessionStoreSummary.lastTaskId} — letting a `/resume` list
106
+ * re-attach to the most recent run, even one still in-flight or suspended. **Optional + best-effort**: a
107
+ * store that surfaces no session list may ignore it; it MUST NOT throw. The engine's `taskId` is the
108
+ * service's `lastRunId`.
109
+ */
32
110
  noteTaskRun?(sessionId: string, taskId: string): void | Promise<void>;
111
+ /**
112
+ * Enumerate known sessions as lightweight {@link SessionStoreSummary} projections (newest-first by
113
+ * `lastActiveAt`) so a shell's `/resume` lists sessions + their last run in ONE call. **Optional**: the
114
+ * in-memory `TtlSessionStore` lists its live cache; a durable backend lists persisted sessions. A store
115
+ * that cannot enumerate omits this (the shell then has no session-list affordance — honest degrade).
116
+ */
33
117
  list?(): Promise<SessionStoreSummary[]>;
118
+ /**
119
+ * design/110 — FORK a session: copy `sourceId`'s durable history (root→leaf) into a NEW session and return its
120
+ * id (or `null` if the source doesn't exist). The forked session is independent (writes to it don't touch the
121
+ * source). Backs CC `/fork` (continue with the parent's full context) AND the inheriting-context subagent fork
122
+ * (run a child on the fork so it inherits the parent context + shares the prompt-cache prefix). **Optional**:
123
+ * a store implements it only if it can replay history. `TtlSessionStore` DOES — it delegates to its
124
+ * {@link SessionRepo} (so {@link hasSessionFork} is true) — BUT the fork is only as durable as that repo:
125
+ * an in-memory repo forks IN-PROCESS yet loses the copy on restart, while a durable repo (file/pg) makes the
126
+ * fork survive restart/replica. **Capability presence ≠ durability** — a cross-replica consumer (design/114
127
+ * warm-resume) MUST wire a durable repo; core cannot verify durability, only fail-loud on a missing source at
128
+ * fork time (returns `null`). `owner` (if given) scopes the new session for multi-tenant stores, and a store that
129
+ * also implements {@link register} MUST make the fork's ownership row agree with it — a fork COPIES the source's
130
+ * history, so the copy must never be weaker than the original: with no `owner` argument the fork inherits the
131
+ * SOURCE's row (a source with no row mints none, keeping the authorizer's claim branch intact). An EXPLICIT
132
+ * `owner: null` follows the same inheritance, deliberately: "anonymous" on a copy of an OWNED source would
133
+ * make that history claimable by anyone — a downgrade this face exists to prevent — so `null` only takes
134
+ * effect when the source itself is unowned/anonymous. MUST copy the COMMITTED leaf (an in-flight
135
+ * uncommitted turn of an active source is not included — a fork is a snapshot at the fork point).
136
+ */
34
137
  fork?(sourceId: string, owner?: string | null): Promise<string | null>;
138
+ /**
139
+ * Session-ownership capability face (multi-tenant admission — the deployment's authorizer probes for
140
+ * BOTH methods and runs its owner gate only when they exist; a store without them forces the
141
+ * deployment into its fail-closed arm, refusing caller-supplied session ids under multi-tenancy).
142
+ *
143
+ * `ownerOf` is THREE-state and the states must never collapse:
144
+ * - `undefined` — the session has NO ownership row (unknown id): the authorizer claims it for the
145
+ * calling principal (or 404s under require-existing).
146
+ * - `null` — the session EXISTS and is anonymous (unowned): admitted (single-tenant/dev sharing).
147
+ * - `string` — owned: a different principal is refused.
148
+ * Collapsing `undefined` into `null` breaks the gate in both directions at once (an unknown id
149
+ * reads as an admissible anonymous session; an anonymous session reads as claimable).
150
+ */
35
151
  ownerOf?(sessionId: string): Promise<string | null | undefined>;
152
+ /**
153
+ * Idempotent ownership upsert, FIRST-WRITE-WINS and atomic: the first registered owner is kept,
154
+ * later calls neither overwrite nor throw. Deliberately makes NO authorization decision — the
155
+ * caller re-reads {@link ownerOf} after registering and judges there (which is exactly why the
156
+ * first-write-wins read-back must be atomic: a concurrent loser has to SEE the winner's owner on
157
+ * re-read, or it silently attaches to the winner's session). In-memory synchronous writes satisfy
158
+ * this trivially; durable implementations need a real uniqueness guarantee, not read-then-write.
159
+ * The ownership row must never expire BEFORE its session (a live session reading as `undefined`
160
+ * would be re-claimable by anyone); same-lifetime or longer both satisfy the contract — so a store that
161
+ * deletes the row when it deletes the history must gate that on the history delete having SUCCEEDED.
162
+ * An implementation must never store a value that {@link ownerOf} reads back as `undefined` (a row that
163
+ * EXISTS yet reads as "no row" is permanently claimable and never ownable): a FIRST write whose `owner` is
164
+ * neither a string nor `null` is refused loudly instead — the no-overwrite-no-throw rule above governs
165
+ * LATER calls, which stay silent no-ops whatever they carry.
166
+ */
36
167
  register?(sessionId: string, owner: string | null): Promise<void>;
168
+ /** Number of live/cached sessions (best-effort). */
37
169
  readonly size: number;
170
+ /** Release resources (timers, connections). Best-effort; must not throw. */
38
171
  dispose(): void | Promise<void>;
39
172
  }
173
+ /** Structural detection: does this store expose the optional {@link SessionStore.fork}? Mounts the fork
174
+ * capability only when true (INERT otherwise — mirrors `hasScheduler`/`hasBackgroundShell`). */
40
175
  export declare function hasSessionFork(store: SessionStore): store is SessionStore & {
41
176
  fork: NonNullable<SessionStore["fork"]>;
42
177
  };
178
+ /**
179
+ * F3 bounded-wake recipe, packaged. Given the **path-ordered** entries of a branch
180
+ * (root→leaf, e.g. `await session.getBranch(leafId)` or a durable store's leaf-walk),
181
+ * returns the smallest self-contained tail a stateless runner needs to resume:
182
+ *
183
+ * - `floorEntryId` = the **latest** compaction's `firstKeptEntryId` (null if never compacted),
184
+ * - `tail` = entries from that floor to the leaf — i.e. the surviving compaction summary
185
+ * plus every message kept after it. Everything before the floor is already folded into
186
+ * that summary, so it can stay cold in durable storage.
187
+ *
188
+ * Hand the result straight to a floored storage:
189
+ * `new InMemorySessionStorage({ entries: tail, floorEntryId })` (or your durable equivalent).
190
+ * `buildContext()` on that is identical to waking from the full tree — see `session-floor.test.ts`.
191
+ *
192
+ * Pure and allocation-light: it scans for the last compaction and slices. If the tree was never
193
+ * compacted (or the floor isn't on this path — shouldn't happen for a real branch) it returns the
194
+ * full input unchanged with `floorEntryId: null`, so callers can always use the result verbatim.
195
+ */
43
196
  export declare function boundedTail(pathEntries: readonly SessionTreeEntry[]): {
44
197
  tail: SessionTreeEntry[];
45
198
  floorEntryId: string | null;
@@ -1,4 +1,24 @@
1
+ /**
2
+ * design/177 — the SharedMemoryStoreProvider conformance suite: the cross-repo acceptance contract a
3
+ * third-party / deployment-authored provider runs against itself. Same shape as the memory-backend
4
+ * suite it is modeled on — runner-agnostic (node:assert, zero vitest), a FRESH instance per case, and
5
+ * an optional `runAssertion` wrapper so a vitest consumer can turn each case into an `it`.
6
+ *
7
+ * FIXTURES ARE HANDED IN, not written through the seam. Unlike the entry-transactional backend suite
8
+ * (which can seed itself through its own patch face), this seam is read-only and has no public write
9
+ * port at all, so `make` receives the intended contents and returns a provider that reads them back.
10
+ *
11
+ * NO CROSS-INSTANCE / CAS CASE — a DECLARED absence, not an omission: a compare-and-set case exists to
12
+ * cover a write-race axis, and a face with no write channel has no such axis. There is consequently no
13
+ * skip hook either: every case below is mandatory, so a passing run never means "most of it passed".
14
+ *
15
+ * WHAT IS ASSERTED. Only the PROVIDER's own obligations — faithfulness, absence-as-a-value, prefix
16
+ * completeness, cancellation, isolation. Rendering, pagination, sanitizing and refusal wording are the
17
+ * engine's job and are covered by the engine's own tests; asserting them here would make a third party
18
+ * responsible for behavior it does not implement.
19
+ */
1
20
  import type { SharedMemorySnapshot, SharedMemoryStoreInfo, SharedMemoryStoreProvider } from "./types.js";
21
+ /** What an implementation must load into its own storage before returning a provider over it. */
2
22
  export interface SharedMemoryFixture {
3
23
  state: SharedMemorySnapshot["state"];
4
24
  stores: Array<{
@@ -12,6 +32,8 @@ export interface SharedMemoryFixture {
12
32
  }
13
33
  export interface SharedMemoryStoreContractHooks {
14
34
  make: (fixture: SharedMemoryFixture) => Promise<SharedMemoryStoreProvider> | SharedMemoryStoreProvider;
35
+ /** Wrap each case (vitest `it`, sequential chaining, …). Default: run them sequentially. */
15
36
  runAssertion?: (name: string, fn: () => Promise<void>) => void | Promise<void>;
16
37
  }
38
+ /** Run the full conformance suite. Throws (assert) on the first violated clause. */
17
39
  export declare function sharedMemoryStoreContract(hooks: SharedMemoryStoreContractHooks): Promise<void>;