@fitchmultz/pi-subagents 0.44.6 → 0.45.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 (947) hide show
  1. package/CHANGELOG.md +265 -7
  2. package/LICENSE +1 -0
  3. package/README.md +58 -1483
  4. package/agents/context-builder.md +19 -1
  5. package/agents/debugger.md +10 -3
  6. package/agents/delegate.md +1 -0
  7. package/agents/fixer.md +4 -1
  8. package/agents/oracle.md +13 -2
  9. package/agents/planner.md +14 -3
  10. package/agents/researcher.md +11 -2
  11. package/agents/reviewer-claude.md +11 -2
  12. package/agents/reviewer-gpt.md +12 -2
  13. package/agents/reviewer-ponytail.md +11 -1
  14. package/agents/reviewer-security.md +13 -2
  15. package/agents/reviewer.md +12 -3
  16. package/agents/scout.md +12 -1
  17. package/agents/ui-designer.md +10 -3
  18. package/agents/watcher.md +1 -0
  19. package/agents/worker.md +8 -2
  20. package/agents/writer.md +6 -3
  21. package/dist/agents/agent-defaults.js +9 -0
  22. package/dist/agents/agent-definition.js +196 -0
  23. package/dist/agents/agent-management.js +64 -905
  24. package/dist/agents/agent-scope.js +4 -2
  25. package/dist/agents/agent-selection.js +10 -5
  26. package/dist/agents/agent-serializer.js +55 -67
  27. package/dist/agents/agents.js +75 -716
  28. package/dist/agents/builtin-overrides.js +218 -0
  29. package/dist/agents/chain-serializer.js +3 -367
  30. package/dist/agents/config-values.js +52 -0
  31. package/dist/agents/discovery-paths.js +91 -0
  32. package/dist/agents/frontmatter.js +15 -5
  33. package/dist/agents/identity.js +25 -8
  34. package/dist/agents/json-chain.js +254 -0
  35. package/dist/agents/management-config.js +332 -0
  36. package/dist/agents/management-format.js +231 -0
  37. package/dist/agents/management-persistence.js +287 -0
  38. package/dist/agents/management-targets.js +136 -0
  39. package/dist/agents/management-warnings.js +81 -0
  40. package/dist/agents/markdown-chain.js +210 -0
  41. package/dist/agents/package-agents.js +154 -0
  42. package/dist/agents/skill-files.js +157 -0
  43. package/dist/agents/skill-search-paths.js +246 -0
  44. package/dist/agents/skill-types.js +1 -0
  45. package/dist/agents/skills.js +54 -499
  46. package/dist/extension/acceptance-schema.js +55 -0
  47. package/dist/extension/build-info.js +1 -1
  48. package/dist/extension/chain-schema.js +196 -0
  49. package/dist/extension/child-runtime.js +237 -0
  50. package/dist/extension/compact-tools.js +122 -51
  51. package/dist/extension/config-schema.js +29 -0
  52. package/dist/extension/config.js +27 -11
  53. package/dist/extension/control-notice-schema.js +37 -0
  54. package/dist/extension/control-notices.js +13 -6
  55. package/dist/extension/delegation-bridges.js +43 -0
  56. package/dist/extension/doctor.js +48 -25
  57. package/dist/extension/everyday-schemas.js +207 -0
  58. package/dist/extension/fanout-child.js +18 -319
  59. package/dist/extension/index.js +8 -563
  60. package/dist/extension/message-renderers.js +225 -0
  61. package/dist/extension/nested-control-inbox.js +154 -0
  62. package/dist/extension/parent-runtime.js +339 -0
  63. package/dist/extension/primary-tool.js +69 -0
  64. package/dist/extension/result-snapshot.js +22 -0
  65. package/dist/extension/runtime-reload.js +62 -0
  66. package/dist/extension/runtime-storage.js +43 -0
  67. package/dist/extension/schema-overrides.js +75 -0
  68. package/dist/extension/schemas.js +265 -336
  69. package/dist/extension/tool-input.js +27 -12
  70. package/dist/extension/tool-result.js +19 -4
  71. package/dist/history/answer-texts.js +57 -0
  72. package/dist/history/canonical-result.js +76 -43
  73. package/dist/history/canonical-selection.js +40 -0
  74. package/dist/history/database-generation.js +82 -0
  75. package/dist/history/database.js +57 -33
  76. package/dist/history/entry-ingestion.js +61 -0
  77. package/dist/history/index.js +242 -97
  78. package/dist/history/ingest.js +168 -331
  79. package/dist/history/output-digests.js +40 -0
  80. package/dist/history/preview.js +210 -0
  81. package/dist/history/prune.js +33 -0
  82. package/dist/history/queries.js +60 -349
  83. package/dist/history/query-scope.js +190 -0
  84. package/dist/history/rows.js +77 -0
  85. package/dist/history/run-projection.js +137 -0
  86. package/dist/history/run-queries.js +153 -0
  87. package/dist/history/schema.js +31 -0
  88. package/dist/history/search-queries.js +163 -0
  89. package/dist/history/selected-record.js +108 -0
  90. package/dist/history/source-file.js +45 -0
  91. package/dist/history/source-watches.js +141 -0
  92. package/dist/history/store.js +164 -153
  93. package/dist/history/text-chunks.js +169 -0
  94. package/dist/history/text.js +35 -0
  95. package/dist/history/transcript-queries.js +177 -0
  96. package/dist/history/types.js +5 -1
  97. package/dist/history/values.js +36 -0
  98. package/dist/history/wire-inputs.js +122 -0
  99. package/dist/history/wire-models.js +38 -0
  100. package/dist/history/worker-runtime.js +441 -0
  101. package/dist/history/worker.js +66 -462
  102. package/dist/intercom/intercom-bridge.js +48 -20
  103. package/dist/intercom/live-intercom.js +80 -23
  104. package/dist/intercom/nested-result-tree.js +163 -0
  105. package/dist/intercom/result-delivery.js +40 -0
  106. package/dist/intercom/result-intercom.js +178 -261
  107. package/dist/pi-intercom/agent-dir.js +7 -3
  108. package/dist/pi-intercom/async.js +27 -0
  109. package/dist/pi-intercom/bridge-ask.js +57 -0
  110. package/dist/pi-intercom/bridge-config.js +79 -0
  111. package/dist/pi-intercom/bridge-http.js +86 -0
  112. package/dist/pi-intercom/bridge-protocol.js +106 -0
  113. package/dist/pi-intercom/bridge-routes.js +64 -0
  114. package/dist/pi-intercom/bridge-session.js +416 -0
  115. package/dist/pi-intercom/bridge.js +182 -603
  116. package/dist/pi-intercom/broker/broker.js +236 -244
  117. package/dist/pi-intercom/broker/client.js +189 -304
  118. package/dist/pi-intercom/broker/connection.js +165 -0
  119. package/dist/pi-intercom/broker/framing.js +5 -3
  120. package/dist/pi-intercom/broker/paths.js +4 -1
  121. package/dist/pi-intercom/broker/pid.js +53 -25
  122. package/dist/pi-intercom/broker/snapshot-reader.js +78 -0
  123. package/dist/pi-intercom/broker/snapshots.js +188 -0
  124. package/dist/pi-intercom/broker/spawn.js +36 -25
  125. package/dist/pi-intercom/config.js +85 -71
  126. package/dist/pi-intercom/connection.js +297 -0
  127. package/dist/pi-intercom/identity.js +58 -0
  128. package/dist/pi-intercom/inbound-delivery.js +356 -0
  129. package/dist/pi-intercom/inbound-journal.js +303 -0
  130. package/dist/pi-intercom/inbound-record.js +113 -0
  131. package/dist/pi-intercom/inbound.js +179 -0
  132. package/dist/pi-intercom/index.js +79 -2933
  133. package/dist/pi-intercom/inspection.js +62 -0
  134. package/dist/pi-intercom/interview.js +236 -0
  135. package/dist/pi-intercom/lifecycle.js +168 -0
  136. package/dist/pi-intercom/message-format.js +69 -0
  137. package/dist/pi-intercom/outbound.js +304 -0
  138. package/dist/pi-intercom/peer-awareness.js +66 -0
  139. package/dist/pi-intercom/reply-tracker.js +62 -43
  140. package/dist/pi-intercom/reply-wait.js +201 -0
  141. package/dist/pi-intercom/runtime-types.js +3 -0
  142. package/dist/pi-intercom/session-events.js +180 -0
  143. package/dist/pi-intercom/session-targets.js +42 -34
  144. package/dist/pi-intercom/subagent-events.js +238 -0
  145. package/dist/pi-intercom/subagent-live-events.js +129 -86
  146. package/dist/pi-intercom/supervisor.js +282 -0
  147. package/dist/pi-intercom/tool-arguments.js +74 -0
  148. package/dist/pi-intercom/tool-registration.js +95 -0
  149. package/dist/pi-intercom/tool-results.js +38 -0
  150. package/dist/pi-intercom/tool-schemas.js +82 -0
  151. package/dist/pi-intercom/tools.js +51 -0
  152. package/dist/pi-intercom/topic-actions.js +177 -0
  153. package/dist/pi-intercom/topics.js +177 -92
  154. package/dist/pi-intercom/transport.js +1 -0
  155. package/dist/pi-intercom/types.js +151 -114
  156. package/dist/pi-intercom/ui/compose-paste.js +93 -0
  157. package/dist/pi-intercom/ui/compose.js +121 -143
  158. package/dist/pi-intercom/ui/inline-message.js +59 -59
  159. package/dist/pi-intercom/ui/message-renderers.js +147 -0
  160. package/dist/pi-intercom/ui/overlay.js +181 -0
  161. package/dist/pi-intercom/ui/session-format.js +124 -0
  162. package/dist/pi-intercom/ui/session-list.js +76 -43
  163. package/dist/pi-intercom/ui/tool-renderers.js +110 -0
  164. package/dist/pi-intercom/ui/topic-view.js +88 -0
  165. package/dist/pi-intercom/validation.js +59 -0
  166. package/dist/runs/background/async-chain-plan.js +265 -0
  167. package/dist/runs/background/async-control.js +134 -58
  168. package/dist/runs/background/async-execution.js +136 -633
  169. package/dist/runs/background/async-job-admission.js +40 -0
  170. package/dist/runs/background/async-job-projection.js +169 -0
  171. package/dist/runs/background/async-job-tracker.js +297 -327
  172. package/dist/runs/background/async-launch.js +216 -0
  173. package/dist/runs/background/async-plan.js +116 -0
  174. package/dist/runs/background/async-result-file.js +47 -23
  175. package/dist/runs/background/async-resume.js +195 -253
  176. package/dist/runs/background/async-run-discovery.js +213 -0
  177. package/dist/runs/background/async-run-location.js +135 -0
  178. package/dist/runs/background/async-run-record.js +1 -0
  179. package/dist/runs/background/async-run-repair.js +223 -0
  180. package/dist/runs/background/async-run-summary.js +1 -0
  181. package/dist/runs/background/async-single-plan.js +105 -0
  182. package/dist/runs/background/async-status-projection.js +155 -0
  183. package/dist/runs/background/async-status-render.js +117 -0
  184. package/dist/runs/background/async-status-validation.js +181 -0
  185. package/dist/runs/background/async-status.js +3 -442
  186. package/dist/runs/background/async-value.js +21 -0
  187. package/dist/runs/background/completion-dedupe.js +36 -12
  188. package/dist/runs/background/completion-delivery.js +211 -205
  189. package/dist/runs/background/completion-receipts.js +237 -0
  190. package/dist/runs/background/legacy-status-acceptance.js +34 -0
  191. package/dist/runs/background/notify.js +96 -48
  192. package/dist/runs/background/parallel-groups.js +36 -19
  193. package/dist/runs/background/result-payload.js +178 -0
  194. package/dist/runs/background/result-watcher.js +348 -339
  195. package/dist/runs/background/run-id-resolver.js +53 -33
  196. package/dist/runs/background/run-schemas.js +189 -0
  197. package/dist/runs/background/run-status-contracts.js +14 -0
  198. package/dist/runs/background/run-status-render.js +270 -0
  199. package/dist/runs/background/run-status.js +231 -339
  200. package/dist/runs/background/runner-attempt-output.js +155 -0
  201. package/dist/runs/background/runner-attempt.js +263 -0
  202. package/dist/runs/background/runner-child-executor.js +166 -0
  203. package/dist/runs/background/runner-child-observer.js +192 -0
  204. package/dist/runs/background/runner-completion-summary.js +71 -0
  205. package/dist/runs/background/runner-completion.js +211 -0
  206. package/dist/runs/background/runner-config.js +1 -0
  207. package/dist/runs/background/runner-contract.js +1 -0
  208. package/dist/runs/background/runner-dynamic.js +121 -0
  209. package/dist/runs/background/runner-finalization.js +149 -0
  210. package/dist/runs/background/runner-graph.js +68 -0
  211. package/dist/runs/background/runner-initial-status.js +60 -0
  212. package/dist/runs/background/runner-lifecycle.js +142 -0
  213. package/dist/runs/background/runner-monitor.js +309 -0
  214. package/dist/runs/background/runner-parallel.js +204 -0
  215. package/dist/runs/background/runner-prepare-step.js +86 -0
  216. package/dist/runs/background/runner-reporting.js +153 -0
  217. package/dist/runs/background/runner-status.js +148 -0
  218. package/dist/runs/background/runner-step-output.js +92 -0
  219. package/dist/runs/background/runner-step-record.js +45 -0
  220. package/dist/runs/background/runner-step.js +112 -0
  221. package/dist/runs/background/runner-streaming.js +173 -0
  222. package/dist/runs/background/runner-workflow.js +213 -0
  223. package/dist/runs/background/stale-run-reconciler.js +155 -292
  224. package/dist/runs/background/subagent-runner-launcher.js +33 -13
  225. package/dist/runs/background/subagent-runner.js +51 -1842
  226. package/dist/runs/background/top-level-async.js +2 -1
  227. package/dist/runs/foreground/chain-clarify.js +293 -1017
  228. package/dist/runs/foreground/clarification-draft.js +87 -0
  229. package/dist/runs/foreground/clarify-contracts.js +1 -0
  230. package/dist/runs/foreground/clarify-invocation.js +129 -38
  231. package/dist/runs/foreground/clarify-text-editor.js +210 -0
  232. package/dist/runs/foreground/clarify-view.js +167 -0
  233. package/dist/runs/foreground/execution-input.js +226 -353
  234. package/dist/runs/foreground/execution-routing.js +33 -0
  235. package/dist/runs/foreground/execution-timeout.js +81 -0
  236. package/dist/runs/foreground/foreground-control.js +222 -763
  237. package/dist/runs/foreground/foreground-memory.js +99 -0
  238. package/dist/runs/foreground/invocation-context.js +55 -0
  239. package/dist/runs/foreground/invocation-execution.js +331 -0
  240. package/dist/runs/foreground/invocation-expansion.js +48 -0
  241. package/dist/runs/foreground/management-actions.js +295 -0
  242. package/dist/runs/foreground/nested-control.js +179 -0
  243. package/dist/runs/foreground/question-continuation.js +78 -0
  244. package/dist/runs/foreground/question-control.js +155 -50
  245. package/dist/runs/foreground/resume-target.js +221 -0
  246. package/dist/runs/foreground/revival-launch-policy.js +58 -0
  247. package/dist/runs/foreground/run-async-path.js +190 -178
  248. package/dist/runs/foreground/run-interrupt.js +164 -0
  249. package/dist/runs/foreground/run-nudge.js +278 -0
  250. package/dist/runs/foreground/saved-revival.js +251 -0
  251. package/dist/runs/foreground/subagent-executor.js +135 -424
  252. package/dist/runs/foreground/subagent-params.js +67 -30
  253. package/dist/runs/foreground/wait-observation.js +104 -0
  254. package/dist/runs/foreground/wait-registration.js +17 -0
  255. package/dist/runs/foreground/wait-run.js +283 -123
  256. package/dist/runs/shared/acceptance-contract.js +193 -99
  257. package/dist/runs/shared/acceptance-defaults.js +1 -0
  258. package/dist/runs/shared/acceptance-evaluation.js +201 -181
  259. package/dist/runs/shared/acceptance-finalization.js +256 -180
  260. package/dist/runs/shared/acceptance-outcome.js +53 -0
  261. package/dist/runs/shared/acceptance-reports.js +137 -72
  262. package/dist/runs/shared/acceptance-submission.js +167 -0
  263. package/dist/runs/shared/acceptance-verification.js +100 -0
  264. package/dist/runs/shared/chain-outputs.js +80 -52
  265. package/dist/runs/shared/child-attempt-types.js +1 -0
  266. package/dist/runs/shared/child-attempt.js +314 -667
  267. package/dist/runs/shared/child-boundaries.js +173 -0
  268. package/dist/runs/shared/child-context-filter.js +44 -0
  269. package/dist/runs/shared/child-event-observer.js +390 -0
  270. package/dist/runs/shared/child-execution-cwd.js +73 -43
  271. package/dist/runs/shared/child-execution-outcome.js +72 -0
  272. package/dist/runs/shared/child-invocation.js +54 -0
  273. package/dist/runs/shared/child-message-validation.js +215 -0
  274. package/dist/runs/shared/child-native-accounting.js +106 -0
  275. package/dist/runs/shared/child-native-observation.js +69 -0
  276. package/dist/runs/shared/child-observation-spool.js +164 -0
  277. package/dist/runs/shared/child-observations.js +157 -76
  278. package/dist/runs/shared/child-presence.js +3 -0
  279. package/dist/runs/shared/child-route-env.js +119 -0
  280. package/dist/runs/shared/child-stream-observer.js +221 -0
  281. package/dist/runs/shared/child-stream-projection.js +137 -0
  282. package/dist/runs/shared/child-structured-tool.js +27 -0
  283. package/dist/runs/shared/claude-code.js +121 -158
  284. package/dist/runs/shared/claude-model.js +42 -0
  285. package/dist/runs/shared/claude-session.js +83 -0
  286. package/dist/runs/shared/claude-types.js +1 -0
  287. package/dist/runs/shared/completion-guard.js +18 -18
  288. package/dist/runs/shared/control-event-policy.js +8 -0
  289. package/dist/runs/shared/control-notification-owner.js +13 -0
  290. package/dist/runs/shared/dynamic-fanout.js +188 -173
  291. package/dist/runs/shared/dynamic-item.js +102 -0
  292. package/dist/runs/shared/history-index.js +62 -36
  293. package/dist/runs/shared/legacy-async-recovery.js +114 -0
  294. package/dist/runs/shared/legacy-output-recovery.js +148 -0
  295. package/dist/runs/shared/mcp-config-schema.js +36 -0
  296. package/dist/runs/shared/mcp-direct-tool-allowlist.js +147 -116
  297. package/dist/runs/shared/model-fallback.js +97 -38
  298. package/dist/runs/shared/mutating-tool-guard.js +128 -88
  299. package/dist/runs/shared/native-finalization-types.js +1 -0
  300. package/dist/runs/shared/native-finalization.js +155 -76
  301. package/dist/runs/shared/native-usage.js +238 -98
  302. package/dist/runs/shared/nested-control-records.js +128 -0
  303. package/dist/runs/shared/nested-events.js +8 -700
  304. package/dist/runs/shared/nested-path.js +32 -28
  305. package/dist/runs/shared/nested-projection.js +155 -0
  306. package/dist/runs/shared/nested-protocol.js +18 -0
  307. package/dist/runs/shared/nested-registry.js +191 -0
  308. package/dist/runs/shared/nested-render.js +106 -58
  309. package/dist/runs/shared/nested-route.js +220 -0
  310. package/dist/runs/shared/nested-validation.js +216 -0
  311. package/dist/runs/shared/owned-child-evidence.js +125 -0
  312. package/dist/runs/shared/owned-run-list.js +146 -0
  313. package/dist/runs/shared/owned-run-observation.js +267 -0
  314. package/dist/runs/shared/owned-run-progress.js +109 -0
  315. package/dist/runs/shared/owned-run-read-state.js +1 -0
  316. package/dist/runs/shared/owned-run-results.js +202 -0
  317. package/dist/runs/shared/owned-run-status.js +180 -0
  318. package/dist/runs/shared/owned-run-view.js +51 -0
  319. package/dist/runs/shared/owned-workflow-details.js +17 -0
  320. package/dist/runs/shared/owner-output.js +43 -0
  321. package/dist/runs/shared/parallel-utils.js +36 -28
  322. package/dist/runs/shared/parent-receipts.js +90 -32
  323. package/dist/runs/shared/parent-usage.js +169 -57
  324. package/dist/runs/shared/pi-args.js +108 -201
  325. package/dist/runs/shared/pi-launch-input.js +1 -0
  326. package/dist/runs/shared/pi-session-launch.js +86 -0
  327. package/dist/runs/shared/pi-spawn.js +35 -21
  328. package/dist/runs/shared/run-history-queries.js +126 -35
  329. package/dist/runs/shared/run-history.js +101 -35
  330. package/dist/runs/shared/run-metadata-paths.js +14 -0
  331. package/dist/runs/shared/run-metadata.js +158 -0
  332. package/dist/runs/shared/run-persistence.js +131 -0
  333. package/dist/runs/shared/run-records.js +9 -679
  334. package/dist/runs/shared/run-restoration.js +338 -0
  335. package/dist/runs/shared/run-state-owner.js +26 -0
  336. package/dist/runs/shared/session-cwd-preload.js +27 -18
  337. package/dist/runs/shared/single-output.js +148 -70
  338. package/dist/runs/shared/stable-value.js +15 -0
  339. package/dist/runs/shared/streaming-text.js +23 -12
  340. package/dist/runs/shared/structured-output.js +25 -13
  341. package/dist/runs/shared/subagent-control.js +167 -100
  342. package/dist/runs/shared/subagent-prompt-runtime.js +27 -130
  343. package/dist/runs/shared/subagent-tool-loop-guard.js +81 -52
  344. package/dist/runs/shared/supervisor-question-state.js +145 -0
  345. package/dist/runs/shared/supervisor-questions.js +185 -254
  346. package/dist/runs/shared/workflow-graph.js +259 -167
  347. package/dist/runs/shared/workflow-policy.js +120 -48
  348. package/dist/runs/shared/worktree-contract.js +1 -0
  349. package/dist/runs/shared/worktree-hook.js +138 -0
  350. package/dist/runs/shared/worktree-location.js +47 -0
  351. package/dist/runs/shared/worktree-preservation.js +8 -0
  352. package/dist/runs/shared/worktree-process.js +107 -0
  353. package/dist/runs/shared/worktree.js +183 -322
  354. package/dist/shared/agent-context-policy.js +34 -44
  355. package/dist/shared/agent-dir.js +14 -0
  356. package/dist/shared/artifacts.js +30 -12
  357. package/dist/shared/atomic-json.js +4 -1
  358. package/dist/shared/chain-files.js +72 -0
  359. package/dist/shared/concurrency.js +18 -0
  360. package/dist/shared/content-preview.js +115 -0
  361. package/dist/shared/cooperative.js +5 -2
  362. package/dist/shared/execution-cwd.js +13 -5
  363. package/dist/shared/file-coalescer.js +2 -1
  364. package/dist/shared/fork-context.js +10 -12
  365. package/dist/shared/formatters.js +45 -19
  366. package/dist/shared/journal-frames.js +164 -0
  367. package/dist/shared/journal-projections.js +121 -0
  368. package/dist/shared/journal-reader.js +6 -407
  369. package/dist/shared/json-projection.js +191 -0
  370. package/dist/shared/lazy-tools.js +19 -8
  371. package/dist/shared/message-output.js +116 -0
  372. package/dist/shared/model-info.js +31 -14
  373. package/dist/shared/native-import.js +20 -0
  374. package/dist/shared/native-journal.js +140 -0
  375. package/dist/shared/native-session.js +5 -2
  376. package/dist/shared/native-tui.js +7 -2
  377. package/dist/shared/native-typebox.js +15 -3
  378. package/dist/shared/output-page.js +63 -0
  379. package/dist/shared/post-exit-stdio-guard.js +128 -88
  380. package/dist/shared/prompt-sections.js +2 -0
  381. package/dist/shared/session-entries.js +70 -35
  382. package/dist/shared/session-identity.js +5 -3
  383. package/dist/shared/settings.js +62 -192
  384. package/dist/shared/status-format.js +97 -37
  385. package/dist/shared/task-file-policy.js +24 -0
  386. package/dist/shared/temp-root.js +44 -15
  387. package/dist/shared/types/acceptance.js +1 -0
  388. package/dist/shared/types/async.js +1 -0
  389. package/dist/shared/types/config.js +1 -0
  390. package/dist/shared/types/details.js +1 -0
  391. package/dist/shared/types/history.js +1 -0
  392. package/dist/shared/types/inputs.js +1 -0
  393. package/dist/shared/types/intercom.js +1 -0
  394. package/dist/shared/types/launch.js +1 -0
  395. package/dist/shared/types/messages.js +1 -0
  396. package/dist/shared/types/nested.js +1 -0
  397. package/dist/shared/types/owned-runs.js +1 -0
  398. package/dist/shared/types/progress.js +1 -0
  399. package/dist/shared/types/questions.js +1 -0
  400. package/dist/shared/types/results.js +1 -0
  401. package/dist/shared/types/state.js +1 -0
  402. package/dist/shared/types/usage.js +1 -0
  403. package/dist/shared/types/workflow.js +1 -0
  404. package/dist/shared/types.js +75 -30
  405. package/dist/shared/unknown.js +52 -0
  406. package/dist/shared/utils.js +54 -260
  407. package/dist/slash/command-discovery.js +82 -0
  408. package/dist/slash/command-input.js +190 -0
  409. package/dist/slash/prompt-template-bridge.js +94 -270
  410. package/dist/slash/prompt-template-contract.js +77 -0
  411. package/dist/slash/prompt-template-results.js +125 -0
  412. package/dist/slash/request-protocol.js +68 -0
  413. package/dist/slash/result-delivery.js +237 -0
  414. package/dist/slash/saved-chain.js +78 -0
  415. package/dist/slash/slash-bridge.js +123 -89
  416. package/dist/slash/slash-commands.js +101 -572
  417. package/dist/slash/slash-live-state.js +68 -47
  418. package/dist/tui/action-hints.js +50 -22
  419. package/dist/tui/activity-lines.js +32 -0
  420. package/dist/tui/agent-browser.js +290 -0
  421. package/dist/tui/agent-controls.js +289 -0
  422. package/dist/tui/agent-conversation.js +444 -0
  423. package/dist/tui/agent-dock.js +171 -0
  424. package/dist/tui/agent-history.js +50 -183
  425. package/dist/tui/agent-model.js +62 -0
  426. package/dist/tui/agent-picker.js +262 -0
  427. package/dist/tui/agent-view.js +232 -1537
  428. package/dist/tui/compact-results.js +193 -0
  429. package/dist/tui/conversation-details.js +190 -0
  430. package/dist/tui/conversation-frame.js +135 -0
  431. package/dist/tui/conversation-history.js +201 -0
  432. package/dist/tui/conversation-menu.js +78 -0
  433. package/dist/tui/conversation-notices.js +79 -0
  434. package/dist/tui/conversation-selection.js +58 -0
  435. package/dist/tui/conversation-viewport.js +129 -0
  436. package/dist/tui/direction-message.js +27 -0
  437. package/dist/tui/display.js +282 -0
  438. package/dist/tui/editor-viewport.js +18 -0
  439. package/dist/tui/expanded-content.js +87 -0
  440. package/dist/tui/expanded-multi.js +170 -0
  441. package/dist/tui/expanded-single.js +65 -0
  442. package/dist/tui/history-cards.js +216 -0
  443. package/dist/tui/history-display.js +238 -0
  444. package/dist/tui/history-json.js +16 -0
  445. package/dist/tui/history-metadata.js +152 -0
  446. package/dist/tui/history-record.js +251 -0
  447. package/dist/tui/history-text.js +34 -0
  448. package/dist/tui/indexed-history.js +116 -0
  449. package/dist/tui/nested-widget.js +120 -0
  450. package/dist/tui/picker-page.js +59 -0
  451. package/dist/tui/render.js +58 -1501
  452. package/dist/tui/result-facts.js +64 -0
  453. package/dist/tui/task-observation.js +59 -0
  454. package/dist/tui/task-store.js +366 -0
  455. package/dist/tui/text-values.js +16 -0
  456. package/dist/tui/view-connections.js +47 -0
  457. package/dist/tui/view-model.js +94 -0
  458. package/dist/tui/view-persistence.js +51 -0
  459. package/dist/tui/view-ports.js +1 -0
  460. package/dist/tui/view-session.js +37 -0
  461. package/dist/tui/widget-details.js +144 -0
  462. package/dist/tui/widget-status.js +245 -0
  463. package/dist/tui/widget.js +145 -0
  464. package/dist/tui/workflow-labels.js +223 -0
  465. package/dist/tui/workflow-outcomes.js +124 -0
  466. package/docs/code-quality.md +38 -0
  467. package/docs/development.md +99 -0
  468. package/docs/intercom-bridge.md +23 -18
  469. package/docs/intercom.md +132 -106
  470. package/docs/performance.md +19 -17
  471. package/docs/reference.md +1472 -0
  472. package/generated/RunValidators.d.ts +28 -0
  473. package/generated/RunValidators.js +2 -0
  474. package/package.json +27 -6
  475. package/prompts/parallel-cleanup.md +3 -0
  476. package/prompts/parallel-context-build.md +3 -0
  477. package/prompts/parallel-handoff-plan.md +6 -0
  478. package/prompts/parallel-research.md +4 -0
  479. package/prompts/parallel-review.md +2 -0
  480. package/prompts/review-loop.md +2 -0
  481. package/scripts/build.mjs +129 -85
  482. package/scripts/check-compat.mjs +48 -11
  483. package/scripts/compat-host.mjs +35 -7
  484. package/scripts/generate-run-schemas.mjs +210 -0
  485. package/scripts/intercom-bridge-credentials.mjs +155 -28
  486. package/scripts/intercom-remote.mjs +175 -50
  487. package/scripts/local-install-smoke.mjs +47 -37
  488. package/scripts/native-package-smoke.mjs +135 -25
  489. package/scripts/package-smoke.mjs +360 -140
  490. package/scripts/prepare.mjs +54 -34
  491. package/scripts/real-pi-processes.mjs +261 -0
  492. package/scripts/real-pi-smoke.mjs +432 -351
  493. package/scripts/run-tests.mjs +29 -15
  494. package/skills/pi-intercom/SKILL.md +11 -11
  495. package/skills/pi-intercom/references/peer-sessions.md +7 -2
  496. package/skills/pi-subagents/SKILL.md +1 -1
  497. package/skills/pi-subagents/references/full-orchestration-guide.md +254 -112
  498. package/src/agents/agent-defaults.ts +13 -0
  499. package/src/agents/agent-definition.ts +253 -0
  500. package/src/agents/agent-management.ts +111 -808
  501. package/src/agents/agent-scope.ts +7 -3
  502. package/src/agents/agent-selection.ts +26 -16
  503. package/src/agents/agent-serializer.ts +84 -93
  504. package/src/agents/agents.ts +199 -906
  505. package/src/agents/builtin-overrides.ts +297 -0
  506. package/src/agents/chain-serializer.ts +3 -329
  507. package/src/agents/config-values.ts +57 -0
  508. package/src/agents/discovery-paths.ts +134 -0
  509. package/src/agents/frontmatter.ts +35 -17
  510. package/src/agents/identity.ts +49 -18
  511. package/src/agents/json-chain.ts +305 -0
  512. package/src/agents/management-config.ts +342 -0
  513. package/src/agents/management-format.ts +243 -0
  514. package/src/agents/management-persistence.ts +408 -0
  515. package/src/agents/management-targets.ts +260 -0
  516. package/src/agents/management-warnings.ts +115 -0
  517. package/src/agents/markdown-chain.ts +237 -0
  518. package/src/agents/package-agents.ts +231 -0
  519. package/src/agents/skill-files.ts +182 -0
  520. package/src/agents/skill-search-paths.ts +289 -0
  521. package/src/agents/skill-types.ts +31 -0
  522. package/src/agents/skills.ts +152 -610
  523. package/src/extension/acceptance-schema.ts +88 -0
  524. package/src/extension/chain-schema.ts +276 -0
  525. package/src/extension/child-runtime.ts +302 -0
  526. package/src/extension/compact-tools.ts +259 -102
  527. package/src/extension/config-schema.ts +59 -0
  528. package/src/extension/config.ts +39 -17
  529. package/src/extension/control-notice-schema.ts +55 -0
  530. package/src/extension/control-notices.ts +57 -33
  531. package/src/extension/delegation-bridges.ts +59 -0
  532. package/src/extension/doctor.ts +171 -135
  533. package/src/extension/everyday-schemas.ts +252 -0
  534. package/src/extension/fanout-child.ts +29 -308
  535. package/src/extension/index.ts +17 -642
  536. package/src/extension/message-renderers.ts +334 -0
  537. package/src/extension/nested-control-inbox.ts +196 -0
  538. package/src/extension/parent-runtime.ts +405 -0
  539. package/src/extension/primary-tool.ts +130 -0
  540. package/src/extension/result-snapshot.ts +35 -0
  541. package/src/extension/runtime-reload.ts +72 -0
  542. package/src/extension/runtime-storage.ts +49 -0
  543. package/src/extension/schema-overrides.ts +114 -0
  544. package/src/extension/schemas.ts +451 -418
  545. package/src/extension/tool-input.ts +40 -13
  546. package/src/extension/tool-result.ts +45 -14
  547. package/src/history/answer-texts.ts +74 -0
  548. package/src/history/canonical-result.ts +127 -51
  549. package/src/history/canonical-selection.ts +54 -0
  550. package/src/history/database-generation.ts +86 -0
  551. package/src/history/database.ts +86 -48
  552. package/src/history/entry-ingestion.ts +129 -0
  553. package/src/history/index.ts +426 -148
  554. package/src/history/ingest.ts +372 -342
  555. package/src/history/output-digests.ts +41 -0
  556. package/src/history/preview.ts +235 -0
  557. package/src/history/prune.ts +48 -0
  558. package/src/history/queries.ts +122 -256
  559. package/src/history/query-scope.ts +278 -0
  560. package/src/history/rows.ts +118 -0
  561. package/src/history/run-projection.ts +164 -0
  562. package/src/history/run-queries.ts +199 -0
  563. package/src/history/schema.ts +34 -0
  564. package/src/history/search-queries.ts +216 -0
  565. package/src/history/selected-record.ts +140 -0
  566. package/src/history/source-file.ts +51 -0
  567. package/src/history/source-watches.ts +156 -0
  568. package/src/history/store.ts +310 -197
  569. package/src/history/text-chunks.ts +181 -0
  570. package/src/history/text.ts +43 -0
  571. package/src/history/transcript-queries.ts +251 -0
  572. package/src/history/types.ts +27 -164
  573. package/src/history/values.ts +36 -0
  574. package/src/history/wire-inputs.ts +141 -0
  575. package/src/history/wire-models.ts +51 -0
  576. package/src/history/worker-runtime.ts +502 -0
  577. package/src/history/worker.ts +94 -336
  578. package/src/intercom/intercom-bridge.ts +136 -69
  579. package/src/intercom/live-intercom.ts +197 -92
  580. package/src/intercom/nested-result-tree.ts +213 -0
  581. package/src/intercom/result-delivery.ts +56 -0
  582. package/src/intercom/result-intercom.ts +364 -382
  583. package/src/pi-intercom/agent-dir.ts +9 -3
  584. package/src/pi-intercom/async.ts +33 -0
  585. package/src/pi-intercom/bridge-ask.ts +64 -0
  586. package/src/pi-intercom/bridge-config.ts +101 -0
  587. package/src/pi-intercom/bridge-http.ts +97 -0
  588. package/src/pi-intercom/bridge-protocol.ts +123 -0
  589. package/src/pi-intercom/bridge-routes.ts +90 -0
  590. package/src/pi-intercom/bridge-session.ts +506 -0
  591. package/src/pi-intercom/bridge.ts +258 -547
  592. package/src/pi-intercom/broker/broker.ts +326 -263
  593. package/src/pi-intercom/broker/client.ts +273 -354
  594. package/src/pi-intercom/broker/connection.ts +192 -0
  595. package/src/pi-intercom/broker/framing.ts +11 -6
  596. package/src/pi-intercom/broker/paths.ts +28 -25
  597. package/src/pi-intercom/broker/pid.ts +68 -24
  598. package/src/pi-intercom/broker/snapshot-reader.ts +91 -0
  599. package/src/pi-intercom/broker/snapshots.ts +241 -0
  600. package/src/pi-intercom/broker/spawn.ts +58 -34
  601. package/src/pi-intercom/config.ts +113 -108
  602. package/src/pi-intercom/connection.ts +346 -0
  603. package/src/pi-intercom/identity.ts +71 -0
  604. package/src/pi-intercom/inbound-delivery.ts +450 -0
  605. package/src/pi-intercom/inbound-journal.ts +350 -0
  606. package/src/pi-intercom/inbound-record.ts +141 -0
  607. package/src/pi-intercom/inbound.ts +237 -0
  608. package/src/pi-intercom/index.ts +86 -2923
  609. package/src/pi-intercom/inspection.ts +82 -0
  610. package/src/pi-intercom/interview.ts +295 -0
  611. package/src/pi-intercom/lifecycle.ts +183 -0
  612. package/src/pi-intercom/message-format.ts +77 -0
  613. package/src/pi-intercom/outbound.ts +418 -0
  614. package/src/pi-intercom/peer-awareness.ts +84 -0
  615. package/src/pi-intercom/reply-tracker.ts +121 -59
  616. package/src/pi-intercom/reply-wait.ts +247 -0
  617. package/src/pi-intercom/runtime-types.ts +97 -0
  618. package/src/pi-intercom/session-events.ts +247 -0
  619. package/src/pi-intercom/session-targets.ts +124 -72
  620. package/src/pi-intercom/subagent-events.ts +299 -0
  621. package/src/pi-intercom/subagent-live-events.ts +212 -109
  622. package/src/pi-intercom/supervisor.ts +362 -0
  623. package/src/pi-intercom/tool-arguments.ts +120 -0
  624. package/src/pi-intercom/tool-registration.ts +121 -0
  625. package/src/pi-intercom/tool-results.ts +54 -0
  626. package/src/pi-intercom/tool-schemas.ts +134 -0
  627. package/src/pi-intercom/tools.ts +80 -0
  628. package/src/pi-intercom/topic-actions.ts +243 -0
  629. package/src/pi-intercom/topics.ts +267 -121
  630. package/src/pi-intercom/transport.ts +14 -0
  631. package/src/pi-intercom/types.ts +288 -190
  632. package/src/pi-intercom/ui/compose-paste.ts +109 -0
  633. package/src/pi-intercom/ui/compose.ts +173 -157
  634. package/src/pi-intercom/ui/inline-message.ts +80 -62
  635. package/src/pi-intercom/ui/message-renderers.ts +182 -0
  636. package/src/pi-intercom/ui/overlay.ts +238 -0
  637. package/src/pi-intercom/ui/session-format.ts +146 -0
  638. package/src/pi-intercom/ui/session-list.ts +145 -62
  639. package/src/pi-intercom/ui/tool-renderers.ts +148 -0
  640. package/src/pi-intercom/ui/topic-view.ts +107 -0
  641. package/src/pi-intercom/validation.ts +69 -0
  642. package/src/runs/background/async-chain-plan.ts +451 -0
  643. package/src/runs/background/async-control.ts +201 -71
  644. package/src/runs/background/async-execution.ts +206 -829
  645. package/src/runs/background/async-job-admission.ts +54 -0
  646. package/src/runs/background/async-job-projection.ts +225 -0
  647. package/src/runs/background/async-job-tracker.ts +416 -360
  648. package/src/runs/background/async-launch.ts +319 -0
  649. package/src/runs/background/async-plan.ts +285 -0
  650. package/src/runs/background/async-result-file.ts +79 -37
  651. package/src/runs/background/async-resume.ts +338 -285
  652. package/src/runs/background/async-run-discovery.ts +270 -0
  653. package/src/runs/background/async-run-location.ts +194 -0
  654. package/src/runs/background/async-run-record.ts +14 -0
  655. package/src/runs/background/async-run-repair.ts +307 -0
  656. package/src/runs/background/async-run-summary.ts +71 -0
  657. package/src/runs/background/async-single-plan.ts +168 -0
  658. package/src/runs/background/async-status-projection.ts +188 -0
  659. package/src/runs/background/async-status-render.ts +164 -0
  660. package/src/runs/background/async-status-validation.ts +238 -0
  661. package/src/runs/background/async-status.ts +9 -481
  662. package/src/runs/background/async-value.ts +29 -0
  663. package/src/runs/background/completion-dedupe.ts +103 -54
  664. package/src/runs/background/completion-delivery.ts +313 -187
  665. package/src/runs/background/completion-receipts.ts +309 -0
  666. package/src/runs/background/legacy-status-acceptance.ts +38 -0
  667. package/src/runs/background/notify.ts +167 -106
  668. package/src/runs/background/parallel-groups.ts +95 -46
  669. package/src/runs/background/result-payload.ts +268 -0
  670. package/src/runs/background/result-watcher.ts +484 -387
  671. package/src/runs/background/run-id-resolver.ts +126 -57
  672. package/src/runs/background/run-schemas.ts +240 -0
  673. package/src/runs/background/run-status-contracts.ts +46 -0
  674. package/src/runs/background/run-status-render.ts +364 -0
  675. package/src/runs/background/run-status.ts +338 -356
  676. package/src/runs/background/runner-attempt-output.ts +281 -0
  677. package/src/runs/background/runner-attempt.ts +354 -0
  678. package/src/runs/background/runner-child-executor.ts +232 -0
  679. package/src/runs/background/runner-child-observer.ts +270 -0
  680. package/src/runs/background/runner-completion-summary.ts +106 -0
  681. package/src/runs/background/runner-completion.ts +274 -0
  682. package/src/runs/background/runner-config.ts +52 -0
  683. package/src/runs/background/runner-contract.ts +51 -0
  684. package/src/runs/background/runner-dynamic.ts +185 -0
  685. package/src/runs/background/runner-finalization.ts +203 -0
  686. package/src/runs/background/runner-graph.ts +91 -0
  687. package/src/runs/background/runner-initial-status.ts +72 -0
  688. package/src/runs/background/runner-lifecycle.ts +159 -0
  689. package/src/runs/background/runner-monitor.ts +399 -0
  690. package/src/runs/background/runner-parallel.ts +280 -0
  691. package/src/runs/background/runner-prepare-step.ts +128 -0
  692. package/src/runs/background/runner-reporting.ts +192 -0
  693. package/src/runs/background/runner-status.ts +220 -0
  694. package/src/runs/background/runner-step-output.ts +158 -0
  695. package/src/runs/background/runner-step-record.ts +76 -0
  696. package/src/runs/background/runner-step.ts +150 -0
  697. package/src/runs/background/runner-streaming.ts +220 -0
  698. package/src/runs/background/runner-workflow.ts +284 -0
  699. package/src/runs/background/stale-run-reconciler.ts +279 -336
  700. package/src/runs/background/subagent-runner-launcher.ts +67 -35
  701. package/src/runs/background/subagent-runner.ts +80 -2048
  702. package/src/runs/background/top-level-async.ts +9 -7
  703. package/src/runs/foreground/chain-clarify.ts +499 -1334
  704. package/src/runs/foreground/clarification-draft.ts +92 -0
  705. package/src/runs/foreground/clarify-contracts.ts +33 -0
  706. package/src/runs/foreground/clarify-invocation.ts +192 -65
  707. package/src/runs/foreground/clarify-text-editor.ts +229 -0
  708. package/src/runs/foreground/clarify-view.ts +222 -0
  709. package/src/runs/foreground/execution-input.ts +358 -424
  710. package/src/runs/foreground/execution-routing.ts +41 -0
  711. package/src/runs/foreground/execution-timeout.ts +92 -0
  712. package/src/runs/foreground/foreground-control.ts +333 -812
  713. package/src/runs/foreground/foreground-memory.ts +132 -0
  714. package/src/runs/foreground/invocation-context.ts +88 -0
  715. package/src/runs/foreground/invocation-execution.ts +465 -0
  716. package/src/runs/foreground/invocation-expansion.ts +58 -0
  717. package/src/runs/foreground/management-actions.ts +360 -0
  718. package/src/runs/foreground/nested-control.ts +248 -0
  719. package/src/runs/foreground/question-continuation.ts +112 -0
  720. package/src/runs/foreground/question-control.ts +297 -70
  721. package/src/runs/foreground/resume-target.ts +330 -0
  722. package/src/runs/foreground/revival-launch-policy.ts +112 -0
  723. package/src/runs/foreground/run-async-path.ts +307 -223
  724. package/src/runs/foreground/run-interrupt.ts +202 -0
  725. package/src/runs/foreground/run-nudge.ts +355 -0
  726. package/src/runs/foreground/saved-revival.ts +356 -0
  727. package/src/runs/foreground/subagent-executor.ts +195 -493
  728. package/src/runs/foreground/subagent-params.ts +314 -242
  729. package/src/runs/foreground/wait-observation.ts +160 -0
  730. package/src/runs/foreground/wait-registration.ts +18 -0
  731. package/src/runs/foreground/wait-run.ts +361 -119
  732. package/src/runs/shared/acceptance-contract.ts +410 -209
  733. package/src/runs/shared/acceptance-defaults.ts +1 -0
  734. package/src/runs/shared/acceptance-evaluation.ts +314 -240
  735. package/src/runs/shared/acceptance-finalization.ts +447 -305
  736. package/src/runs/shared/acceptance-outcome.ts +69 -0
  737. package/src/runs/shared/acceptance-reports.ts +220 -126
  738. package/src/runs/shared/acceptance-submission.ts +251 -0
  739. package/src/runs/shared/acceptance-verification.ts +118 -0
  740. package/src/runs/shared/acceptance.ts +25 -25
  741. package/src/runs/shared/chain-outputs.ts +190 -98
  742. package/src/runs/shared/child-attempt-types.ts +118 -0
  743. package/src/runs/shared/child-attempt.ts +372 -608
  744. package/src/runs/shared/child-boundaries.ts +217 -0
  745. package/src/runs/shared/child-context-filter.ts +53 -0
  746. package/src/runs/shared/child-event-observer.ts +496 -0
  747. package/src/runs/shared/child-execution-cwd.ts +139 -68
  748. package/src/runs/shared/child-execution-outcome.ts +103 -0
  749. package/src/runs/shared/child-invocation.ts +74 -0
  750. package/src/runs/shared/child-message-validation.ts +233 -0
  751. package/src/runs/shared/child-native-accounting.ts +154 -0
  752. package/src/runs/shared/child-native-observation.ts +79 -0
  753. package/src/runs/shared/child-observation-spool.ts +188 -0
  754. package/src/runs/shared/child-observations.ts +219 -89
  755. package/src/runs/shared/child-presence.ts +6 -0
  756. package/src/runs/shared/child-route-env.ts +142 -0
  757. package/src/runs/shared/child-stream-observer.ts +302 -0
  758. package/src/runs/shared/child-stream-projection.ts +160 -0
  759. package/src/runs/shared/child-structured-tool.ts +38 -0
  760. package/src/runs/shared/claude-code.ts +201 -228
  761. package/src/runs/shared/claude-model.ts +67 -0
  762. package/src/runs/shared/claude-session.ts +124 -0
  763. package/src/runs/shared/claude-types.ts +31 -0
  764. package/src/runs/shared/completion-guard.ts +35 -26
  765. package/src/runs/shared/control-event-policy.ts +15 -0
  766. package/src/runs/shared/control-notification-owner.ts +20 -0
  767. package/src/runs/shared/dynamic-fanout.ts +402 -238
  768. package/src/runs/shared/dynamic-item.ts +114 -0
  769. package/src/runs/shared/history-index.ts +112 -51
  770. package/src/runs/shared/legacy-async-recovery.ts +153 -0
  771. package/src/runs/shared/legacy-output-recovery.ts +214 -0
  772. package/src/runs/shared/mcp-config-schema.ts +46 -0
  773. package/src/runs/shared/mcp-direct-tool-allowlist.ts +396 -307
  774. package/src/runs/shared/model-fallback.ts +302 -180
  775. package/src/runs/shared/mutating-tool-guard.ts +259 -158
  776. package/src/runs/shared/native-finalization-types.ts +29 -0
  777. package/src/runs/shared/native-finalization.ts +233 -109
  778. package/src/runs/shared/native-usage.ts +379 -96
  779. package/src/runs/shared/nested-control-records.ts +178 -0
  780. package/src/runs/shared/nested-events.ts +45 -752
  781. package/src/runs/shared/nested-path.ts +47 -34
  782. package/src/runs/shared/nested-projection.ts +230 -0
  783. package/src/runs/shared/nested-protocol.ts +67 -0
  784. package/src/runs/shared/nested-registry.ts +273 -0
  785. package/src/runs/shared/nested-render.ts +198 -100
  786. package/src/runs/shared/nested-route.ts +292 -0
  787. package/src/runs/shared/nested-validation.ts +291 -0
  788. package/src/runs/shared/owned-child-evidence.ts +184 -0
  789. package/src/runs/shared/owned-run-list.ts +187 -0
  790. package/src/runs/shared/owned-run-observation.ts +368 -0
  791. package/src/runs/shared/owned-run-progress.ts +172 -0
  792. package/src/runs/shared/owned-run-read-state.ts +7 -0
  793. package/src/runs/shared/owned-run-results.ts +289 -0
  794. package/src/runs/shared/owned-run-status.ts +239 -0
  795. package/src/runs/shared/owned-run-view.ts +65 -0
  796. package/src/runs/shared/owned-workflow-details.ts +24 -0
  797. package/src/runs/shared/owner-output.ts +73 -0
  798. package/src/runs/shared/parallel-utils.ts +93 -129
  799. package/src/runs/shared/parent-receipts.ts +177 -50
  800. package/src/runs/shared/parent-usage.ts +314 -91
  801. package/src/runs/shared/pi-args.ts +241 -311
  802. package/src/runs/shared/pi-launch-input.ts +48 -0
  803. package/src/runs/shared/pi-session-launch.ts +94 -0
  804. package/src/runs/shared/pi-spawn.ts +57 -33
  805. package/src/runs/shared/run-history-queries.ts +189 -38
  806. package/src/runs/shared/run-history.ts +197 -76
  807. package/src/runs/shared/run-id-ambiguity.ts +7 -3
  808. package/src/runs/shared/run-metadata-paths.ts +17 -0
  809. package/src/runs/shared/run-metadata.ts +240 -0
  810. package/src/runs/shared/run-persistence.ts +181 -0
  811. package/src/runs/shared/run-records.ts +19 -607
  812. package/src/runs/shared/run-restoration.ts +455 -0
  813. package/src/runs/shared/run-state-owner.ts +31 -0
  814. package/src/runs/shared/session-cwd-preload.ts +39 -19
  815. package/src/runs/shared/single-output.ts +358 -205
  816. package/src/runs/shared/stable-value.ts +15 -0
  817. package/src/runs/shared/streaming-text.ts +39 -12
  818. package/src/runs/shared/structured-output.ts +76 -46
  819. package/src/runs/shared/subagent-control.ts +330 -197
  820. package/src/runs/shared/subagent-prompt-runtime.ts +92 -174
  821. package/src/runs/shared/subagent-tool-loop-guard.ts +118 -68
  822. package/src/runs/shared/supervisor-question-state.ts +221 -0
  823. package/src/runs/shared/supervisor-questions.ts +396 -401
  824. package/src/runs/shared/workflow-graph.ts +400 -198
  825. package/src/runs/shared/workflow-policy.ts +245 -110
  826. package/src/runs/shared/worktree-contract.ts +29 -0
  827. package/src/runs/shared/worktree-hook.ts +181 -0
  828. package/src/runs/shared/worktree-location.ts +61 -0
  829. package/src/runs/shared/worktree-preservation.ts +10 -0
  830. package/src/runs/shared/worktree-process.ts +132 -0
  831. package/src/runs/shared/worktree.ts +459 -587
  832. package/src/shared/agent-context-policy.ts +176 -145
  833. package/src/shared/agent-dir.ts +15 -0
  834. package/src/shared/artifacts.ts +99 -68
  835. package/src/shared/atomic-json.ts +14 -11
  836. package/src/shared/chain-files.ts +72 -0
  837. package/src/shared/concurrency.ts +27 -0
  838. package/src/shared/content-preview.ts +117 -0
  839. package/src/shared/cooperative.ts +17 -13
  840. package/src/shared/execution-cwd.ts +34 -9
  841. package/src/shared/file-coalescer.ts +24 -22
  842. package/src/shared/fork-context.ts +52 -48
  843. package/src/shared/formatters.ts +88 -47
  844. package/src/shared/journal-frames.ts +197 -0
  845. package/src/shared/journal-projections.ts +133 -0
  846. package/src/shared/journal-reader.ts +18 -333
  847. package/src/shared/json-projection.ts +224 -0
  848. package/src/shared/lazy-tools.ts +76 -27
  849. package/src/shared/message-output.ts +146 -0
  850. package/src/shared/model-info.ts +94 -58
  851. package/src/shared/native-import.ts +25 -0
  852. package/src/shared/native-journal.ts +172 -0
  853. package/src/shared/native-session.ts +8 -3
  854. package/src/shared/native-tui.ts +13 -3
  855. package/src/shared/native-typebox.ts +18 -3
  856. package/src/shared/output-page.ts +76 -0
  857. package/src/shared/post-exit-stdio-guard.ts +232 -172
  858. package/src/shared/prompt-sections.ts +13 -7
  859. package/src/shared/session-entries.ts +103 -44
  860. package/src/shared/session-identity.ts +17 -10
  861. package/src/shared/settings.ts +182 -367
  862. package/src/shared/status-format.ts +207 -98
  863. package/src/shared/stream-json.d.ts +10 -0
  864. package/src/shared/task-file-policy.ts +35 -0
  865. package/src/shared/temp-root.ts +122 -71
  866. package/src/shared/types/acceptance.ts +149 -0
  867. package/src/shared/types/async.ts +247 -0
  868. package/src/shared/types/config.ts +139 -0
  869. package/src/shared/types/details.ts +104 -0
  870. package/src/shared/types/history.ts +217 -0
  871. package/src/shared/types/inputs.ts +4 -0
  872. package/src/shared/types/intercom.ts +57 -0
  873. package/src/shared/types/launch.ts +39 -0
  874. package/src/shared/types/messages.ts +104 -0
  875. package/src/shared/types/nested.ts +153 -0
  876. package/src/shared/types/owned-runs.ts +148 -0
  877. package/src/shared/types/progress.ts +106 -0
  878. package/src/shared/types/questions.ts +103 -0
  879. package/src/shared/types/results.ts +119 -0
  880. package/src/shared/types/state.ts +56 -0
  881. package/src/shared/types/usage.ts +36 -0
  882. package/src/shared/types/workflow.ts +187 -0
  883. package/src/shared/types.ts +222 -1161
  884. package/src/shared/ui-mode.ts +1 -1
  885. package/src/shared/unknown.ts +59 -0
  886. package/src/shared/utils.ts +111 -305
  887. package/src/slash/command-discovery.ts +109 -0
  888. package/src/slash/command-input.ts +232 -0
  889. package/src/slash/prompt-template-bridge.ts +153 -400
  890. package/src/slash/prompt-template-contract.ts +152 -0
  891. package/src/slash/prompt-template-results.ts +189 -0
  892. package/src/slash/request-protocol.ts +87 -0
  893. package/src/slash/result-delivery.ts +312 -0
  894. package/src/slash/saved-chain.ts +115 -0
  895. package/src/slash/slash-bridge.ts +202 -156
  896. package/src/slash/slash-commands.ts +176 -598
  897. package/src/slash/slash-live-state.ts +287 -229
  898. package/src/tui/action-hints.ts +147 -61
  899. package/src/tui/activity-lines.ts +40 -0
  900. package/src/tui/agent-browser.ts +354 -0
  901. package/src/tui/agent-controls.ts +355 -0
  902. package/src/tui/agent-conversation.ts +514 -0
  903. package/src/tui/agent-dock.ts +250 -0
  904. package/src/tui/agent-history.ts +78 -203
  905. package/src/tui/agent-model.ts +78 -0
  906. package/src/tui/agent-picker.ts +325 -0
  907. package/src/tui/agent-view.ts +391 -1310
  908. package/src/tui/compact-results.ts +342 -0
  909. package/src/tui/conversation-details.ts +206 -0
  910. package/src/tui/conversation-frame.ts +184 -0
  911. package/src/tui/conversation-history.ts +246 -0
  912. package/src/tui/conversation-menu.ts +119 -0
  913. package/src/tui/conversation-notices.ts +116 -0
  914. package/src/tui/conversation-selection.ts +66 -0
  915. package/src/tui/conversation-viewport.ts +169 -0
  916. package/src/tui/direction-message.ts +35 -0
  917. package/src/tui/display.ts +409 -0
  918. package/src/tui/editor-viewport.ts +19 -0
  919. package/src/tui/expanded-content.ts +134 -0
  920. package/src/tui/expanded-multi.ts +264 -0
  921. package/src/tui/expanded-single.ts +92 -0
  922. package/src/tui/history-cards.ts +316 -0
  923. package/src/tui/history-display.ts +334 -0
  924. package/src/tui/history-json.ts +17 -0
  925. package/src/tui/history-metadata.ts +177 -0
  926. package/src/tui/history-record.ts +321 -0
  927. package/src/tui/history-text.ts +34 -0
  928. package/src/tui/indexed-history.ts +149 -0
  929. package/src/tui/nested-widget.ts +192 -0
  930. package/src/tui/picker-page.ts +80 -0
  931. package/src/tui/render.ts +100 -1546
  932. package/src/tui/result-facts.ts +99 -0
  933. package/src/tui/task-observation.ts +96 -0
  934. package/src/tui/task-store.ts +432 -0
  935. package/src/tui/text-values.ts +20 -0
  936. package/src/tui/view-connections.ts +64 -0
  937. package/src/tui/view-model.ts +181 -0
  938. package/src/tui/view-persistence.ts +57 -0
  939. package/src/tui/view-ports.ts +55 -0
  940. package/src/tui/view-session.ts +42 -0
  941. package/src/tui/widget-details.ts +220 -0
  942. package/src/tui/widget-status.ts +331 -0
  943. package/src/tui/widget.ts +208 -0
  944. package/src/tui/workflow-labels.ts +340 -0
  945. package/src/tui/workflow-outcomes.ts +150 -0
  946. package/tsconfig.build.json +11 -10
  947. package/tsconfig.json +30 -17
@@ -1,370 +1,81 @@
1
- import * as fs from "node:fs";
2
- import { createHash } from "node:crypto";
3
- import { JsonProjection } from "../shared/journal-reader.js";
4
- import { compactEntry, identity, previewLimits, previewProjection } from "./ingest.js";
5
- import { HistoryIndexError } from "./types.js";
6
- import { hash, HistoryStore, integer, pageLimit } from "./store.js";
1
+ import { HistoryIndexError, } from "./types.js";
2
+ import { entryRow } from "./rows.js";
3
+ import { QueryScope } from "./query-scope.js";
4
+ import { RunQueries } from "./run-queries.js";
5
+ import { TranscriptQueries } from "./transcript-queries.js";
6
+ import { SearchQueries } from "./search-queries.js";
7
+ import { validateRecord } from "./selected-record.js";
8
+ import { compactEntry } from "./preview.js";
9
+ import { hasText } from "./text.js";
10
+ /** One query facade with separate run, transcript, search and selected-detail responsibilities. */
7
11
  export class HistoryQueries {
8
- store;
9
- info;
10
- configurations = new Map();
11
- constructor(store, info) { this.store = store; this.info = info; }
12
- sourcesRemoved() { this.configurations.clear(); }
13
- cursor(query, keys, offset) {
14
- return Buffer.from(JSON.stringify({ generation: this.store.generation, owner: this.store.owner, version: this.info().version, query, keys, offset })).toString("base64url");
12
+ scope;
13
+ runs;
14
+ transcript;
15
+ searchQueries;
16
+ constructor(store, info) {
17
+ this.scope = new QueryScope(store, info);
18
+ this.runs = new RunQueries(this.scope);
19
+ this.transcript = new TranscriptQueries(this.scope);
20
+ this.searchQueries = new SearchQueries(this.scope);
15
21
  }
16
- parseCursor(value, query) {
17
- if (value === undefined)
18
- return;
19
- let cursor;
20
- try {
21
- if (value.length > 4096)
22
- throw new Error();
23
- cursor = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
24
- if (!Array.isArray(cursor.keys) || cursor.keys.length > 8 || cursor.keys.some((key) => typeof key !== "number" && typeof key !== "string") || !Number.isSafeInteger(cursor.offset) || cursor.offset < 0)
25
- throw new Error();
26
- }
27
- catch {
28
- throw new HistoryIndexError("INVALID_CURSOR", "Invalid history cursor.");
29
- }
30
- if (cursor.generation !== this.store.generation || cursor.owner !== this.store.owner || cursor.query !== query || cursor.version !== this.info().version)
31
- throw new HistoryIndexError("STALE_CURSOR", "History changed or this cursor belongs to a different query/source; restart paging.");
32
- return cursor;
33
- }
34
- child(runId, index) {
35
- integer(index, 0);
36
- const child = this.store.get("SELECT * FROM children WHERE run_id=? AND child_index=?", runId, index);
37
- if (!child)
38
- throw new HistoryIndexError("OWNERSHIP", "Run/child is not in the current owner's admitted handles.");
39
- return { source: child.source_id ? this.store.source(child.source_id) : undefined, agent: child.agent };
22
+ sourcesRemoved() {
23
+ this.scope.sourcesRemoved();
40
24
  }
41
25
  listRuns(options) {
42
- const limit = pageLimit(options.limit, 20), offset = integer(options.offset, 0), sort = options.sort ?? "attention";
43
- if (!["attention", "newest", "oldest"].includes(sort))
44
- throw new HistoryIndexError("INVALID", "Unsupported run sort.");
45
- if (options.state && !["live", "completed", "paused", "blocked", "failed", "unknown"].includes(options.state))
46
- throw new HistoryIndexError("INVALID", "Unsupported run state.");
47
- if ((options.text?.length ?? 0) > 256 || (options.agent?.length ?? 0) > 256 || options.latestTasksOnly !== undefined && typeof options.latestTasksOnly !== "boolean")
48
- throw new HistoryIndexError("INVALID", "Invalid run filter.");
49
- const query = hash(JSON.stringify(["runs", sort, options.agent ?? null, options.state ?? null, options.text?.toLowerCase() ?? null, options.latestTasksOnly ?? false]));
50
- const cursor = this.parseCursor(options.cursor, query);
51
- if (cursor && options.offset !== undefined)
52
- throw new HistoryIndexError("INVALID", "Use cursor or offset, not both.");
53
- const clauses = [], params = [], childClauses = [], childParams = [];
54
- if (options.agent) {
55
- childClauses.push("c.agent=?");
56
- childParams.push(options.agent);
57
- }
58
- if (options.text) {
59
- childClauses.push("instr(c.filter_text,?)>0");
60
- childParams.push(options.text.toLowerCase());
61
- }
62
- if (options.latestTasksOnly)
63
- childClauses.push("NOT EXISTS(SELECT 1 FROM runs successor WHERE successor.predecessor_id=c.run_id AND successor.predecessor_index=c.child_index)");
64
- const childWhere = childClauses.length ? ` AND ${childClauses.join(" AND ")}` : "";
65
- if (childWhere) {
66
- clauses.push(`EXISTS(SELECT 1 FROM children c WHERE c.run_id=r.id${childWhere})`);
67
- params.push(...childParams);
68
- }
69
- if (options.state) {
70
- clauses.push("r.state=?");
71
- params.push(options.state);
72
- }
73
- const total = Number(this.store.get(`SELECT COUNT(*) AS total FROM runs r ${clauses.length ? `WHERE ${clauses.join(" AND ")}` : ""}`, ...params).total);
74
- const columns = sort === "attention" ? ["attention", "negative_updated", "id"] : [sort === "newest" ? "negative_updated" : "updated", "id"];
75
- if (cursor) {
76
- if (cursor.keys.length !== columns.length)
77
- throw new HistoryIndexError("INVALID_CURSOR", "Invalid run seek tuple.");
78
- clauses.push(`(${columns.map((column) => `r.${column}`).join(",")}) > (${columns.map(() => "?").join(",")})`);
79
- params.push(...cursor.keys);
80
- }
81
- const rows = this.store.all(`SELECT * FROM runs r ${clauses.length ? `WHERE ${clauses.join(" AND ")}` : ""} ORDER BY ${columns.map((column) => `r.${column}`).join(",")} LIMIT ? OFFSET ?`, ...params, limit + 1, cursor ? 0 : offset);
82
- const more = rows.length > limit, page = rows.slice(0, limit), position = cursor?.offset ?? offset;
83
- const views = page.map((row) => {
84
- const view = JSON.parse(row.view);
85
- if (childWhere)
86
- view.matchedChildIndexes = this.store.all(`SELECT child_index FROM children c WHERE c.run_id=?${childWhere} ORDER BY child_index`, view.runId, ...childParams).map((child) => child.child_index);
87
- for (const child of view.children) {
88
- const { source } = this.child(view.runId, child.index);
89
- if (source)
90
- child.nativeConfiguration = this.configuration(source, child.state === "live" ? {} : { terminalEntryId: child.result?.terminalEntryId, endedAt: child.result?.terminalEntryId ? undefined : view.updatedAt, leaf: child.result?.terminalLeafId }, false);
91
- }
92
- return view;
93
- });
94
- return { rows: views, total, offset: position, ...this.info(), ...(more ? { nextOffset: position + page.length, nextCursor: this.cursor(query, columns.map((column) => page.at(-1)[column]), position + page.length) } : {}) };
95
- }
96
- boundary(source, input, alias = "") {
97
- const column = alias ? `${alias}.` : "", clauses = [`${column}source_id=?`, `${column}generation=?`, `${column}published=1`], params = [source.id, source.generation];
98
- let terminalSequence;
99
- if (input.terminalEntryId) {
100
- const terminal = this.store.get("SELECT start FROM entries WHERE source_id=? AND generation=? AND id=? AND published=1", source.id, source.generation, input.terminalEntryId);
101
- if (!terminal)
102
- throw new HistoryIndexError("BOUNDARY_UNAVAILABLE", "Saved terminal entry is not available in the current indexed generation.");
103
- terminalSequence = terminal.start;
104
- clauses.push(`${column}start<=?`);
105
- params.push(terminal.start);
106
- }
107
- else if (input.endedAt !== undefined) {
108
- clauses.push(`${column}timestamp<=?`);
109
- params.push(integer(input.endedAt, 0));
110
- }
111
- return { clauses, params, terminalSequence };
112
- }
113
- configuration(source, input, strict = true) {
114
- if (input.leaf === null)
115
- return {};
116
- let boundary;
117
- try {
118
- boundary = this.boundary(source, input);
119
- }
120
- catch (error) {
121
- if (!strict && error instanceof HistoryIndexError && error.code === "BOUNDARY_UNAVAILABLE")
122
- return {};
123
- throw error;
124
- }
125
- // Only committed source changes invalidate ancestry, not unrelated run projections.
126
- // Validate boundaries before reuse; explicit null leaves returned above remain empty.
127
- const leafKey = input.leaf === undefined ? null : [typeof input.leaf, String(input.leaf)];
128
- const key = JSON.stringify([source.id, source.generation, source.cursor, source.stamp, input.terminalEntryId || null, input.terminalEntryId ? null : input.endedAt ?? null, leafKey, strict]);
129
- const cached = this.configurations.get(key);
130
- if (cached)
131
- return { ...cached };
132
- const leaf = input.leaf ?? this.store.get(`SELECT id FROM entries WHERE ${boundary.clauses.join(" AND ")} ORDER BY start DESC LIMIT 1`, ...boundary.params)?.id;
133
- if (!leaf)
134
- return {};
135
- // Fix recursive loop order: look up each child's parent by ID instead of
136
- // rescanning the eligible transcript at every ancestry step.
137
- const ancestry = `WITH RECURSIVE eligible AS (SELECT rowid,id,parent_id,start,timestamp,configuration_model,configuration_thinking FROM entries WHERE ${boundary.clauses.join(" AND ")}), branch AS (SELECT * FROM eligible WHERE id=? UNION SELECT parent.* FROM branch child CROSS JOIN eligible parent WHERE parent.id=child.parent_id)`;
138
- const facts = this.store.get(`${ancestry} SELECT
139
- (SELECT COUNT(*) FROM branch) AS count,
140
- EXISTS(SELECT 1 FROM branch child WHERE parent_id IS NULL OR NOT EXISTS(SELECT 1 FROM eligible parent WHERE parent.id=child.parent_id)) AS rooted,
141
- (SELECT configuration_model FROM branch WHERE configuration_model IS NOT NULL ORDER BY start DESC LIMIT 1) AS model,
142
- (SELECT timestamp FROM branch WHERE configuration_model IS NOT NULL ORDER BY start DESC LIMIT 1) AS recorded_at,
143
- (SELECT configuration_thinking FROM branch WHERE configuration_thinking IS NOT NULL ORDER BY start DESC LIMIT 1) AS thinking`, ...boundary.params, leaf);
144
- if (!facts.count && input.leaf && strict)
145
- throw new HistoryIndexError("BOUNDARY_UNAVAILABLE", "Selected configuration leaf is not available in the indexed generation.");
146
- if (facts.count && !facts.rooted) {
147
- if (!strict)
148
- return {};
149
- throw new HistoryIndexError("MALFORMED_ANCESTRY", "Selected native configuration has cyclic ancestry.");
150
- }
151
- const configuration = { ...(facts.model ? { model: facts.model, modelRecordedAt: facts.recorded_at ?? undefined } : {}), ...(facts.thinking ? { thinking: facts.thinking } : {}) };
152
- // Bound memory across arbitrary sources, boundaries and pages. Eviction only recomputes.
153
- if (this.configurations.size >= 128)
154
- this.configurations.delete(this.configurations.keys().next().value);
155
- this.configurations.set(key, configuration);
156
- return { ...configuration };
26
+ return this.runs.list(options);
157
27
  }
158
28
  historyPage(input) {
159
- const { source } = this.child(input.runId, input.index);
160
- const info = this.info(), limit = pageLimit(input.limit);
161
- if (input.before !== undefined && input.after !== undefined)
162
- throw new HistoryIndexError("INVALID", "Use before or after, not both.");
163
- if (input.messageIds && (!Array.isArray(input.messageIds) || input.messageIds.length > 1000 || input.messageIds.some((id) => typeof id !== "string" || id.length > 512)) || input.readThrough !== undefined && input.readThrough !== null && (typeof input.readThrough !== "string" || input.readThrough.length > 1024))
164
- throw new HistoryIndexError("INVALID", "Invalid scoped message/read metadata request.");
165
- const empty = { ...info, entries: [], count: 0, hasMore: false, sourceId: source?.id ?? null, generation: source?.generation ?? null, sessionId: source?.session_id ?? null, sourceState: source?.state ?? "unlinked", configuration: {}, deliveredMessages: [], latestEntryId: null };
166
- if (!source)
167
- return { ...empty, unavailable: "This legacy or unstarted child has no linked native conversation." };
168
- const query = hash(JSON.stringify(["history", input.runId, input.index, source.id, source.generation, input.terminalEntryId ?? null, input.endedAt ?? null, input.leaf ?? null]));
169
- const cursor = this.parseCursor(input.cursor, query);
170
- if (cursor && (input.before !== undefined || input.after !== undefined))
171
- throw new HistoryIndexError("INVALID", "Use cursor or sequence, not both.");
172
- const { clauses, params, terminalSequence } = this.boundary(source, input);
173
- const metadataWhere = clauses.join(" AND "), metadataParams = params.slice();
174
- const latest = this.store.get(`SELECT start,visible_id FROM entries WHERE ${metadataWhere} AND visible_id IS NOT NULL ORDER BY start DESC LIMIT 1`, ...metadataParams);
175
- const marker = typeof input.readThrough === "string" ? this.store.get(`SELECT start,visible_id FROM entries WHERE ${metadataWhere} AND id=?`, ...metadataParams, input.readThrough.replace(/:(?:\d+|error)$/, "")) : undefined;
176
- const deliveredMessages = [];
177
- for (const id of new Set(input.messageIds ?? [])) {
178
- const human = this.store.get(`SELECT start FROM entries WHERE ${metadataWhere} AND human_id=? ORDER BY start DESC LIMIT 1`, ...metadataParams, id);
179
- if (human)
180
- deliveredMessages.push([id, Boolean(this.store.get(`SELECT rowid FROM entries WHERE ${metadataWhere} AND start>? AND assistant_text=1 LIMIT 1`, ...metadataParams, human.start))]);
181
- }
182
- const outputDigest = this.store.get("SELECT output_digest FROM children WHERE run_id=? AND child_index=?", input.runId, input.index)?.output_digest;
183
- const configuration = this.configuration(source, input);
184
- const count = Number(this.store.get(`SELECT COUNT(*) AS count FROM entries WHERE ${metadataWhere}`, ...metadataParams).count);
185
- const before = cursor ? integer(cursor.keys[0], 0) : input.before;
186
- if (before !== undefined) {
187
- clauses.push("start<?");
188
- params.push(integer(before, 0));
189
- }
190
- if (input.after !== undefined) {
191
- clauses.push("start>?");
192
- params.push(integer(input.after, 0));
193
- }
194
- const forward = input.after !== undefined;
195
- const fetched = this.store.all(`SELECT * FROM entries WHERE ${clauses.join(" AND ")} ORDER BY start ${forward ? "ASC" : "DESC"} LIMIT ?`, ...params, limit + 1);
196
- const more = fetched.length > limit, rows = fetched.slice(0, limit);
197
- if (!forward)
198
- rows.reverse();
199
- const first = rows[0], last = rows.at(-1);
200
- const finalResultId = outputDigest ? this.finalResultId(input, outputDigest, first && last ? [first.start, last.start] : undefined) : undefined;
201
- const previous = first && this.store.get(`SELECT start FROM entries WHERE ${metadataWhere} AND start<? LIMIT 1`, ...metadataParams, first.start);
202
- return { ...empty, configuration, deliveredMessages, latestEntryId: latest?.visible_id ?? null, terminalSequence,
203
- ...(input.readThrough !== undefined ? { unreadAfter: Boolean(latest && (!marker || latest.start > marker.start || latest.start === marker.start && latest.visible_id !== input.readThrough)) } : {}),
204
- ...(finalResultId ? { finalResultId } : {}), entries: rows.map((row) => this.store.entry(row)), count, hasMore: forward ? more : Boolean(previous),
205
- ...(previous && first ? { previousBefore: first.start, previousCursor: this.cursor(query, [first.start], 0) } : {}),
206
- ...(last ? { nextAfter: last.start } : {}), ...(source.error ? { unavailable: source.error } : source.state === "missing" ? { unavailable: "Linked conversation is missing." } : {}) };
29
+ return this.transcript.page(input);
207
30
  }
208
31
  search(input) {
209
- const expression = searchGrammar(input.query), limit = pageLimit(input.limit, 20), sort = input.sort ?? "relevance";
210
- if (!["relevance", "newest"].includes(sort))
211
- throw new HistoryIndexError("INVALID", "Unsupported search sort.");
212
- if (input.index !== undefined && !input.runId)
213
- throw new HistoryIndexError("INVALID", "A child search requires runId.");
214
- if (input.runId && !this.store.get("SELECT id FROM runs WHERE id=?", input.runId))
215
- throw new HistoryIndexError("OWNERSHIP", "Run is not admitted by the current owner.");
216
- if (input.index !== undefined)
217
- this.child(input.runId, input.index);
218
- if (input.runId)
219
- for (const child of this.store.all(`SELECT source_id,terminal_entry_id,ended_at FROM children WHERE run_id=?${input.index !== undefined ? " AND child_index=?" : ""}`, input.runId, ...(input.index !== undefined ? [input.index] : []))) {
220
- const source = child.source_id && this.store.source(child.source_id);
221
- if (source)
222
- this.boundary(source, { terminalEntryId: child.terminal_entry_id ?? undefined, endedAt: child.ended_at ?? undefined });
223
- }
224
- const query = hash(JSON.stringify(["search", expression, sort, input.runId ?? null, input.index ?? null, input.agent ?? null]));
225
- const cursor = this.parseCursor(input.cursor, query);
226
- const clauses = ["e.published=1", "e.generation=s.generation", "(c.terminal_entry_id IS NULL OR e.start<=terminal.start)", "(c.ended_at IS NULL OR e.timestamp<=c.ended_at)"], params = [...expression];
227
- if (input.runId) {
228
- clauses.push("c.run_id=?");
229
- params.push(input.runId);
230
- }
231
- if (input.index !== undefined) {
232
- clauses.push("c.child_index=?");
233
- params.push(input.index);
234
- }
235
- if (input.agent) {
236
- if (input.agent.length > 256)
237
- throw new HistoryIndexError("INVALID", "Agent filter is too long.");
238
- clauses.push("c.agent=?");
239
- params.push(input.agent);
240
- }
241
- const joins = "JOIN sources s ON s.id=e.source_id JOIN children c ON c.source_id=s.id LEFT JOIN entries terminal ON terminal.source_id=s.id AND terminal.generation=s.generation AND terminal.id=c.terminal_entry_id AND terminal.published=1";
242
- // Scope records before materializing/grouping hits, while bm25 still sees the
243
- // complete corpus. Scan each linked source once, not once per continuation.
244
- // Attempt boundaries, attribution and seek filtering remain in the final join.
245
- const eligible = input.runId || input.agent ? `eligible AS MATERIALIZED (SELECT e.rowid FROM entries e JOIN sources s ON s.id=e.source_id WHERE e.published=1 AND e.generation=s.generation AND e.source_id IN (SELECT c.source_id FROM children c WHERE ${clauses.slice(4).join(" AND ")})),` : "";
246
- const eligibleParams = eligible ? params.slice(expression.length) : [];
247
- // Ordering/filters apply globally before limit. Common terms may still require an FTS
248
- // scan/sort; the parent enforces a hard deadline by killing this separate process.
249
- const score = sort === "relevance" ? "matched.score" : "-COALESCE(e.timestamp,0)";
250
- if (cursor) {
251
- if (cursor.keys.length !== 4)
252
- throw new HistoryIndexError("INVALID_CURSOR", "Invalid search seek tuple.");
253
- clauses.push(`(${score},d.id,c.run_id,c.child_index)>(?,?,?,?)`);
254
- params.push(...cursor.keys);
255
- }
256
- // Match words across all visible fields/windows of one native record. A quoted
257
- // phrase remains a single FTS expression, so it cannot span unrelated fields.
258
- const rows = this.store.all(`WITH ${eligible} hits AS MATERIALIZED (
259
- ${expression.map((_term, index) => `SELECT d.id,d.entry_rowid,bm25(corpus) AS score,${index} AS term FROM corpus JOIN documents d ON d.id=corpus.rowid WHERE corpus MATCH ?${eligible ? " AND d.entry_rowid IN (SELECT rowid FROM eligible)" : ""}`).join(" UNION ALL ")}
260
- ), terms AS (
261
- SELECT entry_rowid,term,MIN(score) AS score FROM hits GROUP BY entry_rowid,term
262
- ), matched AS (
263
- SELECT entry_rowid,SUM(score) AS score FROM terms GROUP BY entry_rowid HAVING COUNT(*)=${expression.length}
264
- ), excerpts AS (
265
- SELECT id,entry_rowid,ROW_NUMBER() OVER (PARTITION BY entry_rowid ORDER BY score,id) AS choice FROM hits
266
- )
267
- SELECT d.*,${score} AS score,e.*,d.id AS document_id,s.path,s.session_id,c.run_id,c.child_index,c.agent,d.preview AS document_preview FROM matched JOIN excerpts ON excerpts.entry_rowid=matched.entry_rowid AND excerpts.choice=1 JOIN documents d ON d.id=excerpts.id JOIN entries e ON e.rowid=matched.entry_rowid ${joins} WHERE ${clauses.join(" AND ")} ORDER BY score,d.id,c.run_id,c.child_index LIMIT ?`, ...eligibleParams, ...params, limit + 1);
268
- const more = rows.length > limit, page = rows.slice(0, limit);
269
- return { ...this.info(), matches: page.map((row) => ({ id: row.document_id, runId: row.run_id, index: row.child_index, agent: row.agent, entryId: row.id, nativeId: row.native_id, sessionId: row.session_id, sessionFile: row.path, timestamp: row.timestamp,
270
- ref: { sourceId: row.source_id, generation: row.generation, start: row.start, end: row.end, digest: row.digest }, preview: row.document_preview, field: row.field, textStart: row.text_start, textEnd: row.text_end, score: row.score })),
271
- ...(more ? { nextCursor: this.cursor(query, [page.at(-1).score, page.at(-1).document_id, page.at(-1).run_id, page.at(-1).child_index], (cursor?.offset ?? 0) + page.length) } : {}) };
32
+ return this.searchQueries.search(input);
272
33
  }
273
34
  finalResultId(input, digest, preferredRange) {
274
- const { source } = this.child(input.runId, input.index);
275
- if (!source)
276
- return;
277
- const boundary = this.boundary(source, input, "e");
278
- return this.store.get(`SELECT a.item_id FROM answers a JOIN entries e ON e.rowid=a.entry_rowid WHERE a.digest=? AND ${boundary.clauses.join(" AND ")} ORDER BY ${preferredRange ? "(e.start BETWEEN ? AND ?) DESC," : ""}e.start DESC LIMIT 1`, digest, ...boundary.params, ...(preferredRange ?? []))?.item_id;
35
+ return this.scope.finalResultId(input, digest, preferredRange);
279
36
  }
280
- selected(input, full) {
281
- const { source } = this.child(input.runId, input.index);
282
- if (!source)
283
- return null;
284
- const boundary = this.boundary(source, input), where = boundary.clauses.join(" AND ");
37
+ selectedRow(input) {
38
+ const { source } = this.scope.child(input.runId, input.index);
39
+ if (!source) {
40
+ return;
41
+ }
42
+ const boundary = this.scope.boundary(source, input);
43
+ const where = boundary.clauses.join(" AND ");
285
44
  let row;
286
45
  if (input.ref) {
287
- if (input.ref.sourceId !== source.id || input.ref.generation !== source.generation)
288
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected source generation changed; refresh history.");
289
- row = this.store.get(`SELECT * FROM entries WHERE ${where} AND start=? AND end=? AND digest=?`, ...boundary.params, input.ref.start, input.ref.end, input.ref.digest);
290
- if (!row)
291
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected byte reference is no longer indexed.");
46
+ return this.referenceRow(input.ref, source, boundary);
47
+ }
48
+ else if (hasText(input.entryId)) {
49
+ row = this.scope.store.get(`SELECT * FROM entries WHERE ${where} AND id=?`, ...boundary.params, input.entryId);
50
+ }
51
+ else if (hasText(input.toolCallId) && ["call", "result"].includes(input.kind ?? "call")) {
52
+ const toolBoundary = this.scope.boundary(source, input, "e");
53
+ row = this.scope.store.get(`SELECT e.* FROM tools t JOIN entries e ON e.rowid=t.entry_rowid WHERE ${toolBoundary.clauses.join(" AND ")} AND t.call_id=? AND t.kind=? ORDER BY e.start DESC LIMIT 1`, ...toolBoundary.params, input.toolCallId, input.kind ?? "call");
292
54
  }
293
- else if (input.entryId)
294
- row = this.store.get(`SELECT * FROM entries WHERE ${where} AND id=?`, ...boundary.params, input.entryId);
295
- else if (input.toolCallId && ["call", "result"].includes(input.kind ?? "call"))
296
- row = this.store.get(`SELECT e.* FROM tools t JOIN entries e ON e.rowid=t.entry_rowid WHERE ${this.boundary(source, input, "e").clauses.join(" AND ")} AND t.call_id=? AND t.kind=? ORDER BY e.start DESC LIMIT 1`, ...boundary.params, input.toolCallId, input.kind ?? "call");
297
- else
55
+ else {
298
56
  throw new HistoryIndexError("INVALID", "Select an entry ID, byte reference, or tool call/result ID.");
299
- if (!row)
300
- return null;
301
- const body = validateRecord(source, row, full);
302
- return full ? body : { ...this.store.entry(row), entry: { ...compactEntry(body), id: row.id } };
57
+ }
58
+ return row ? entryRow(row) : undefined;
303
59
  }
304
- }
305
- function searchGrammar(query) {
306
- if (typeof query !== "string" || !query.trim() || query.length > 1024)
307
- throw new HistoryIndexError("INVALID_QUERY", "Search requires 1–12 tokens or one quoted phrase.");
308
- const text = query.trim(), phrase = text.startsWith('"') && text.endsWith('"');
309
- const words = (phrase ? text.slice(1, -1) : text).trim().split(/\s+/u);
310
- if (!words.length || words.length > 12 || words.some((word) => !/^[\p{L}\p{N}\p{M}]{1,64}$/u.test(word) || !phrase && ["AND", "OR", "NOT", "NEAR"].includes(word)))
311
- throw new HistoryIndexError("INVALID_QUERY", "Only lexical tokens or one quoted phrase are supported; operators, punctuation, and prefixes are not allowed.");
312
- return phrase ? [`"${words.join(" ")}"`] : [...new Set(words.map((word) => `"${word}"`))];
313
- }
314
- function validateRecord(source, row, full) {
315
- let fd;
316
- try {
317
- fd = fs.openSync(source.path, "r");
318
- const stat = fs.fstatSync(fd, { bigint: true });
319
- if (identity(stat) !== source.identity || Number(stat.size) < row.end)
320
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected conversation was replaced or truncated.");
321
- const headerHash = createHash("sha256"), headerBytes = Buffer.allocUnsafe(64 * 1024);
322
- let headerPosition = 0, headerComplete = false;
323
- while (!headerComplete && headerPosition < 1024 * 1024) {
324
- const count = fs.readSync(fd, headerBytes, 0, headerBytes.length, headerPosition);
325
- if (!count)
326
- break;
327
- const newline = headerBytes.subarray(0, count).indexOf(10);
328
- const length = newline < 0 ? count : newline + 1;
329
- headerHash.update(headerBytes.subarray(0, length));
330
- headerPosition += length;
331
- headerComplete = newline >= 0;
60
+ referenceRow(ref, source, boundary) {
61
+ if (ref.sourceId !== source.id || ref.generation !== source.generation) {
62
+ throw new HistoryIndexError("SOURCE_CHANGED", "Selected source generation changed; refresh history.");
332
63
  }
333
- if (!headerComplete || headerHash.digest("hex") !== source.header_digest)
334
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected native header changed or exceeds its validation budget.");
335
- if (full && row.end - row.start > 16 * 1024 * 1024)
336
- throw new HistoryIndexError("RECORD_TOO_LARGE", "Selected full record exceeds the 16 MiB detail budget; use its validated preview.");
337
- const digest = createHash("sha256"), parser = new JsonProjection(previewProjection, undefined, undefined, previewLimits), bytes = Buffer.allocUnsafe(64 * 1024), body = [];
338
- const decoder = new TextDecoder("utf8", { fatal: true });
339
- for (let position = row.start; position < row.end;) {
340
- const count = fs.readSync(fd, bytes, 0, Math.min(bytes.length, row.end - position), position);
341
- if (!count)
342
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected record was truncated during validation.");
343
- const chunk = bytes.subarray(0, count);
344
- digest.update(chunk);
345
- if (full)
346
- body.push(Buffer.from(chunk));
347
- else
348
- parser.write(decoder.decode(chunk, { stream: true }));
349
- position += count;
64
+ const row = this.scope.store.get(`SELECT * FROM entries WHERE ${boundary.clauses.join(" AND ")} AND start=? AND end=? AND digest=?`, ...boundary.params, ref.start, ref.end, ref.digest);
65
+ if (!row) {
66
+ throw new HistoryIndexError("SOURCE_CHANGED", "Selected byte reference is no longer indexed.");
350
67
  }
351
- const current = fs.statSync(source.path, { bigint: true }), after = fs.fstatSync(fd, { bigint: true });
352
- if (identity(current) !== identity(stat) || current.size < BigInt(row.end) || after.ctimeNs !== stat.ctimeNs && after.size <= stat.size || digest.digest("hex") !== row.digest)
353
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected record changed; refresh history before reading details.");
354
- if (full)
355
- return JSON.parse(new TextDecoder("utf8", { fatal: true }).decode(Buffer.concat(body)));
356
- parser.write(decoder.decode());
357
- return parser.finish();
68
+ return entryRow(row);
358
69
  }
359
- catch (error) {
360
- if (error.code === "ENOENT")
361
- throw new HistoryIndexError("SOURCE_CHANGED", "Selected conversation is missing.");
362
- if (error instanceof RangeError)
363
- throw new HistoryIndexError("RECORD_COMPLEXITY", "Selected preview exceeds bounded history structure budgets.");
364
- throw error;
365
- }
366
- finally {
367
- if (fd !== undefined)
368
- fs.closeSync(fd);
70
+ selected(input, full) {
71
+ const { source } = this.scope.child(input.runId, input.index);
72
+ const row = this.selectedRow(input);
73
+ if (!source || !row) {
74
+ return null;
75
+ }
76
+ const body = validateRecord(source, row, full);
77
+ return full
78
+ ? body
79
+ : { ...this.scope.store.entry(row), entry: { ...compactEntry(body), id: row.id } };
369
80
  }
370
81
  }
@@ -0,0 +1,190 @@
1
+ import { Type, Check } from "../shared/native-typebox.js";
2
+ import { HistoryIndexError, } from "./types.js";
3
+ import { hash, integer, hasText } from "./text.js";
4
+ import { number, nullableText, nullableNumber, text, } from "./rows.js";
5
+ import { parseObject } from "./values.js";
6
+ const cursorSchema = Type.Object({
7
+ generation: Type.String(),
8
+ owner: Type.String(),
9
+ version: Type.Number(),
10
+ query: Type.String(),
11
+ keys: Type.Array(Type.Union([Type.Number(), Type.String()]), { maxItems: 8 }),
12
+ offset: Type.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER }),
13
+ });
14
+ function configurationValues(facts, leaf, strict) {
15
+ const count = number(facts, "count");
16
+ if (count === 0 && hasText(leaf) && strict) {
17
+ throw new HistoryIndexError("BOUNDARY_UNAVAILABLE", "Selected configuration leaf is not available in the indexed generation.");
18
+ }
19
+ if (count > 0 && number(facts, "rooted") === 0) {
20
+ if (!strict) {
21
+ return {};
22
+ }
23
+ throw new HistoryIndexError("MALFORMED_ANCESTRY", "Selected native configuration has cyclic ancestry.");
24
+ }
25
+ const model = nullableText(facts, "model");
26
+ const thinking = nullableText(facts, "thinking");
27
+ return {
28
+ ...(model === null
29
+ ? {}
30
+ : { model, modelRecordedAt: nullableNumber(facts, "recorded_at") ?? undefined }),
31
+ ...(thinking === null ? {} : { thinking }),
32
+ };
33
+ }
34
+ function configurationKey(source, input, strict) {
35
+ const terminal = hasText(input.terminalEntryId);
36
+ return JSON.stringify([
37
+ source.id,
38
+ source.generation,
39
+ source.cursor,
40
+ source.stamp,
41
+ terminal ? input.terminalEntryId : null,
42
+ terminal ? null : (input.endedAt ?? null),
43
+ input.leaf === undefined ? null : [typeof input.leaf, input.leaf],
44
+ strict,
45
+ ]);
46
+ }
47
+ export class QueryScope {
48
+ store;
49
+ info;
50
+ configurations = new Map();
51
+ constructor(store, info) {
52
+ this.store = store;
53
+ this.info = info;
54
+ }
55
+ sourcesRemoved() {
56
+ this.configurations.clear();
57
+ }
58
+ key(parts) {
59
+ return hash(JSON.stringify(parts));
60
+ }
61
+ cursor(query, keys, offset) {
62
+ return Buffer.from(JSON.stringify({
63
+ generation: this.store.generation,
64
+ owner: this.store.owner,
65
+ version: this.info().version,
66
+ query,
67
+ keys,
68
+ offset,
69
+ })).toString("base64url");
70
+ }
71
+ parseCursor(value, query) {
72
+ if (value === undefined) {
73
+ return;
74
+ }
75
+ let cursor;
76
+ try {
77
+ if (value.length > 4096) {
78
+ throw new Error("Cursor exceeds budget.");
79
+ }
80
+ const parsed = parseObject(Buffer.from(value, "base64url").toString("utf8"));
81
+ if (!Check(cursorSchema, parsed)) {
82
+ throw new Error("Invalid cursor fields.");
83
+ }
84
+ cursor = parsed;
85
+ }
86
+ catch {
87
+ throw new HistoryIndexError("INVALID_CURSOR", "Invalid history cursor.");
88
+ }
89
+ if (cursor.generation !== this.store.generation ||
90
+ cursor.owner !== this.store.owner ||
91
+ cursor.query !== query ||
92
+ cursor.version !== this.info().version) {
93
+ throw new HistoryIndexError("STALE_CURSOR", "History changed or this cursor belongs to a different query/source; restart paging.");
94
+ }
95
+ return cursor;
96
+ }
97
+ child(runId, index) {
98
+ integer(index, 0);
99
+ const child = this.store.get("SELECT * FROM children WHERE run_id=? AND child_index=?", runId, index);
100
+ if (!child) {
101
+ throw new HistoryIndexError("OWNERSHIP", "Run/child is not in the current owner's admitted handles.");
102
+ }
103
+ const sourceId = nullableText(child, "source_id");
104
+ return {
105
+ source: sourceId === null ? undefined : this.store.source(sourceId),
106
+ agent: text(child, "agent"),
107
+ };
108
+ }
109
+ boundary(source, input, alias = "") {
110
+ const column = alias.length > 0 ? `${alias}.` : "";
111
+ const clauses = [`${column}source_id=?`, `${column}generation=?`, `${column}published=1`];
112
+ const params = [source.id, source.generation];
113
+ let terminalSequence;
114
+ if (input.terminalEntryId !== undefined && input.terminalEntryId.length > 0) {
115
+ const terminal = this.store.get("SELECT start FROM entries WHERE source_id=? AND generation=? AND id=? AND published=1", source.id, source.generation, input.terminalEntryId);
116
+ if (!terminal) {
117
+ throw new HistoryIndexError("BOUNDARY_UNAVAILABLE", "Saved terminal entry is not available in the current indexed generation.");
118
+ }
119
+ terminalSequence = number(terminal, "start");
120
+ clauses.push(`${column}start<=?`);
121
+ params.push(terminalSequence);
122
+ }
123
+ else if (input.endedAt !== undefined) {
124
+ clauses.push(`${column}timestamp<=?`);
125
+ params.push(integer(input.endedAt, 0));
126
+ }
127
+ return { clauses, params, terminalSequence };
128
+ }
129
+ configurationFacts(source, input) {
130
+ const boundary = this.boundary(source, input);
131
+ const latest = this.store.get(`SELECT id FROM entries WHERE ${boundary.clauses.join(" AND ")} ORDER BY start DESC LIMIT 1`, ...boundary.params);
132
+ const leaf = input.leaf ?? (latest ? text(latest, "id") : undefined);
133
+ if (leaf === undefined || leaf.length === 0) {
134
+ return;
135
+ }
136
+ // Resolve each child's parent by its indexed ID, not a transcript rescan at every step.
137
+ const ancestry = `WITH RECURSIVE eligible AS (SELECT rowid,id,parent_id,start,timestamp,configuration_model,configuration_thinking FROM entries WHERE ${boundary.clauses.join(" AND ")}), branch AS (SELECT * FROM eligible WHERE id=? UNION SELECT parent.* FROM branch child CROSS JOIN eligible parent WHERE parent.id=child.parent_id)`;
138
+ return this.store.require(`${ancestry} SELECT
139
+ (SELECT COUNT(*) FROM branch) AS count,
140
+ EXISTS(SELECT 1 FROM branch child WHERE parent_id IS NULL OR NOT EXISTS(SELECT 1 FROM eligible parent WHERE parent.id=child.parent_id)) AS rooted,
141
+ (SELECT configuration_model FROM branch WHERE configuration_model IS NOT NULL ORDER BY start DESC LIMIT 1) AS model,
142
+ (SELECT timestamp FROM branch WHERE configuration_model IS NOT NULL ORDER BY start DESC LIMIT 1) AS recorded_at,
143
+ (SELECT configuration_thinking FROM branch WHERE configuration_thinking IS NOT NULL ORDER BY start DESC LIMIT 1) AS thinking`, ...boundary.params, leaf);
144
+ }
145
+ configuration(source, input, strict = true) {
146
+ if (input.leaf === null) {
147
+ return {};
148
+ }
149
+ try {
150
+ // Validate boundaries before reuse. Only committed source changes invalidate ancestry.
151
+ this.boundary(source, input);
152
+ const key = configurationKey(source, input, strict);
153
+ const cached = this.configurations.get(key);
154
+ if (cached) {
155
+ return { ...cached };
156
+ }
157
+ const facts = this.configurationFacts(source, input);
158
+ if (!facts) {
159
+ return {};
160
+ }
161
+ const configuration = configurationValues(facts, input.leaf, strict);
162
+ this.cacheConfiguration(key, configuration);
163
+ return { ...configuration };
164
+ }
165
+ catch (error) {
166
+ if (!strict && error instanceof HistoryIndexError && error.code === "BOUNDARY_UNAVAILABLE") {
167
+ return {};
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ cacheConfiguration(key, configuration) {
173
+ if (this.configurations.size >= 128) {
174
+ const oldest = this.configurations.keys().next().value;
175
+ if (oldest !== undefined) {
176
+ this.configurations.delete(oldest);
177
+ }
178
+ }
179
+ this.configurations.set(key, configuration);
180
+ }
181
+ finalResultId(input, digest, preferredRange) {
182
+ const { source } = this.child(input.runId, input.index);
183
+ if (!source) {
184
+ return;
185
+ }
186
+ const boundary = this.boundary(source, input, "e");
187
+ const row = this.store.get(`SELECT a.item_id FROM answers a JOIN entries e ON e.rowid=a.entry_rowid WHERE a.digest=? AND ${boundary.clauses.join(" AND ")} ORDER BY ${preferredRange ? "(e.start BETWEEN ? AND ?) DESC," : ""}e.start DESC LIMIT 1`, digest, ...boundary.params, ...(preferredRange ?? []));
188
+ return row ? text(row, "item_id") : undefined;
189
+ }
190
+ }