tumwater 0.2.0 → 0.3.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 (446) hide show
  1. package/README.md +27 -13
  2. package/dist/build-info.json +2 -2
  3. package/dist/src/agent/agent-args.js +8 -9
  4. package/dist/src/agent/agent-backend.js +8 -6
  5. package/dist/src/agent/backend-traits.js +42 -25
  6. package/dist/src/agent/claude/claude-account.js +71 -204
  7. package/dist/src/agent/claude/claude-args.js +9 -6
  8. package/dist/src/agent/claude/claude-backend.js +10 -10
  9. package/dist/src/agent/claude/claude-env.js +1 -1
  10. package/dist/src/agent/claude/claude-stream.js +4 -3
  11. package/dist/src/agent/claude/claude-write-guard.js +49 -0
  12. package/dist/src/agent/claude/write-guard-hook.js +13 -0
  13. package/dist/src/agent/codex/codex-account.js +119 -0
  14. package/dist/src/agent/codex/codex-args.js +181 -0
  15. package/dist/src/agent/codex/codex-backend.js +279 -0
  16. package/dist/src/agent/codex/codex-catalog.js +97 -0
  17. package/dist/src/agent/codex/codex-env.js +18 -0
  18. package/dist/src/agent/codex/codex-hook-trust.js +246 -0
  19. package/dist/src/agent/codex/codex-patch-paths.js +36 -0
  20. package/dist/src/agent/codex/codex-rollout.js +179 -0
  21. package/dist/src/agent/codex/codex-stream.js +402 -0
  22. package/dist/src/agent/codex/codex-write-guard.js +86 -0
  23. package/dist/src/agent/codex/write-guard-hook.js +12 -0
  24. package/dist/src/agent/image-input.js +29 -24
  25. package/dist/src/agent/notes-shim.js +11 -12
  26. package/dist/src/agent/opencode/opencode-args.js +0 -1
  27. package/dist/src/agent/opencode/opencode-backend.js +12 -9
  28. package/dist/src/agent/opencode/opencode-catalog.js +55 -29
  29. package/dist/src/agent/opencode/opencode-config.js +19 -14
  30. package/dist/src/agent/opencode/opencode-env.js +0 -1
  31. package/dist/src/agent/opencode/opencode-session-id.js +1 -2
  32. package/dist/src/agent/opencode/opencode-stream.js +1 -1
  33. package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
  34. package/dist/src/agent/role-notes-write.js +5 -6
  35. package/dist/src/agent/run-agent.js +465 -448
  36. package/dist/src/agent/run-settle.js +214 -0
  37. package/dist/src/agent/session-ledger.js +86 -16
  38. package/dist/src/agent/subscription-account.js +220 -0
  39. package/dist/src/agent/write-guard.js +148 -0
  40. package/dist/src/backlog/backlog-archive.js +246 -55
  41. package/dist/src/backlog/backlog-cli-view.js +280 -0
  42. package/dist/src/backlog/backlog-cli.js +85 -274
  43. package/dist/src/backlog/backlog-commit.js +4 -5
  44. package/dist/src/backlog/backlog-eligibility.js +175 -242
  45. package/dist/src/backlog/backlog-index.js +149 -0
  46. package/dist/src/backlog/backlog-md.js +54 -6
  47. package/dist/src/backlog/backlog-parts.js +254 -0
  48. package/dist/src/backlog/backlog-release.js +117 -0
  49. package/dist/src/backlog/backlog-render.js +27 -0
  50. package/dist/src/backlog/backlog-structure.js +99 -156
  51. package/dist/src/backlog/backlog-write.js +40 -127
  52. package/dist/src/backlog/backlog.js +21 -6
  53. package/dist/src/backlog/question-answer.js +21 -15
  54. package/dist/src/baseline/main-baseline.js +89 -20
  55. package/dist/src/baseline/main-culprit.js +94 -0
  56. package/dist/src/baseline/main-guardrails.js +209 -0
  57. package/dist/src/baseline/main-red.js +139 -32
  58. package/dist/src/baseline/main-revert.js +133 -0
  59. package/dist/src/baseline/main-watch.js +184 -0
  60. package/dist/src/brief/brief-edit.js +9 -11
  61. package/dist/src/brief/brief.js +31 -51
  62. package/dist/src/budget/budget.js +30 -20
  63. package/dist/src/budget/fallback-breaker.js +26 -27
  64. package/dist/src/build/build-check-detect.js +23 -15
  65. package/dist/src/build/build-check-events.js +25 -6
  66. package/dist/src/build/build-check-report.js +49 -9
  67. package/dist/src/build/build-check-scoped.js +18 -11
  68. package/dist/src/build/build-check.js +2 -1
  69. package/dist/src/build/build-info.js +2 -3
  70. package/dist/src/build/build-stage.js +9 -14
  71. package/dist/src/build/dep-install.js +1 -2
  72. package/dist/src/change/change-data.js +123 -31
  73. package/dist/src/change/change-render.js +46 -5
  74. package/dist/src/cli/brief-command.js +3 -4
  75. package/dist/src/cli/cli-args.js +118 -33
  76. package/dist/src/cli/cli-command-args.js +46 -18
  77. package/dist/src/cli/cli-editor.js +2 -1
  78. package/dist/src/cli/cli-flag-specs.js +150 -45
  79. package/dist/src/cli/cli-init.js +51 -0
  80. package/dist/src/cli/cli-marker-commands.js +4 -4
  81. package/dist/src/cli/cli-query-commands.js +308 -590
  82. package/dist/src/cli/cli-repo-gate.js +11 -1
  83. package/dist/src/cli/cli-role-command.js +420 -0
  84. package/dist/src/cli/cli-start-summary.js +160 -0
  85. package/dist/src/cli/{cli-run.js → cli-start.js} +165 -237
  86. package/dist/src/cli/completion.js +149 -22
  87. package/dist/src/cli/config-commands.js +41 -18
  88. package/dist/src/cli/follow-view.js +75 -0
  89. package/dist/src/cli/help.js +189 -73
  90. package/dist/src/cli/log-commands.js +148 -82
  91. package/dist/src/cli/question-commands.js +205 -10
  92. package/dist/src/cli.js +106 -43
  93. package/dist/src/collections.js +26 -10
  94. package/dist/src/concurrency/check-permit.js +20 -3
  95. package/dist/src/concurrency/lock.js +17 -13
  96. package/dist/src/concurrency/provider-lease.js +44 -8
  97. package/dist/src/concurrency/semaphore.js +102 -26
  98. package/dist/src/config/config-aliases.js +0 -1
  99. package/dist/src/config/config-editable-keys.js +13 -1
  100. package/dist/src/config/config-fallbacks.js +93 -56
  101. package/dist/src/config/config-field-checks.js +40 -10
  102. package/dist/src/config/config-keys.js +30 -8
  103. package/dist/src/config/config-live.js +63 -24
  104. package/dist/src/config/config-request.js +137 -0
  105. package/dist/src/config/config-schema.js +31 -24
  106. package/dist/src/config/config-selector-checks.js +123 -0
  107. package/dist/src/config/config-validation-agents.js +80 -0
  108. package/dist/src/config/config-validation-model.js +116 -0
  109. package/dist/src/config/config-validation.js +141 -199
  110. package/dist/src/config/config-views.js +127 -53
  111. package/dist/src/config/config-write-core.js +135 -0
  112. package/dist/src/config/config-write.js +131 -264
  113. package/dist/src/config/config.js +48 -38
  114. package/dist/src/config/model-selector.js +34 -34
  115. package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
  116. package/dist/src/doctor/doctor-backlog-plans.js +263 -0
  117. package/dist/src/doctor/doctor-backlog.js +100 -496
  118. package/dist/src/doctor/doctor-check-names.js +81 -0
  119. package/dist/src/doctor/doctor-checks.js +52 -579
  120. package/dist/src/doctor/doctor-claude-checks.js +145 -0
  121. package/dist/src/doctor/doctor-codex-checks.js +279 -0
  122. package/dist/src/doctor/doctor-contention-checks.js +380 -0
  123. package/dist/src/doctor/doctor-holds.js +82 -66
  124. package/dist/src/doctor/doctor-install.js +2 -1
  125. package/dist/src/doctor/doctor-model-checks.js +236 -402
  126. package/dist/src/doctor/doctor-notify.js +3 -4
  127. package/dist/src/doctor/doctor-opencode-checks.js +162 -0
  128. package/dist/src/doctor/doctor-orphans.js +182 -63
  129. package/dist/src/doctor/doctor-project-checks.js +303 -0
  130. package/dist/src/doctor/doctor.js +121 -77
  131. package/dist/src/doctor/image-probe.js +21 -8
  132. package/dist/src/doctor/outcome.js +3 -2
  133. package/dist/src/doctor/probe.js +7 -11
  134. package/dist/src/events/event-format.js +181 -86
  135. package/dist/src/events/event-labels.js +12 -5
  136. package/dist/src/events/event-legacy.js +39 -15
  137. package/dist/src/events/event-read.js +39 -39
  138. package/dist/src/events/event-tally.js +16 -0
  139. package/dist/src/events/event-window.js +60 -24
  140. package/dist/src/events/events.js +30 -13
  141. package/dist/src/events/notify.js +7 -7
  142. package/dist/src/failure/failure-cluster.js +12 -12
  143. package/dist/src/failure/failure-data.js +56 -56
  144. package/dist/src/failure/failure-render.js +31 -23
  145. package/dist/src/failure/failure-state-change.js +17 -12
  146. package/dist/src/failure/time-spend.js +99 -35
  147. package/dist/src/files/files.js +111 -35
  148. package/dist/src/files/json-files.js +3 -3
  149. package/dist/src/files/json-object.js +28 -7
  150. package/dist/src/files/stat-cache.js +2 -2
  151. package/dist/src/files/tail.js +1 -1
  152. package/dist/src/gates/bootstrap-gates.js +4 -5
  153. package/dist/src/gates/budget-gates.js +177 -84
  154. package/dist/src/gates/disk-gate.js +1 -1
  155. package/dist/src/gates/gate-polls.js +108 -58
  156. package/dist/src/gates/maintenance-quota.js +10 -13
  157. package/dist/src/gates/pause-gates.js +28 -10
  158. package/dist/src/gates/readiness.js +7 -3
  159. package/dist/src/gates/role-cap-gates.js +8 -8
  160. package/dist/src/gates/startup-gate.js +94 -32
  161. package/dist/src/gates/streak-gate.js +54 -30
  162. package/dist/src/git/commit-message.js +77 -46
  163. package/dist/src/git/git-diff.js +16 -10
  164. package/dist/src/git/git-run.js +79 -38
  165. package/dist/src/git/git.js +36 -34
  166. package/dist/src/git/slot-salvage.js +253 -0
  167. package/dist/src/git/slots-state.js +6 -6
  168. package/dist/src/git/worktree-pool.js +132 -38
  169. package/dist/src/git/worktree-use.js +4 -11
  170. package/dist/src/git/worktree.js +94 -37
  171. package/dist/src/gui/gui-admin-endpoints.js +96 -0
  172. package/dist/src/gui/gui-args.js +79 -8
  173. package/dist/src/gui/gui-backlog-commands.js +42 -18
  174. package/dist/src/gui/gui-backlog-endpoints.js +155 -0
  175. package/dist/src/gui/gui-command.js +8 -13
  176. package/dist/src/gui/gui-config-commands.js +94 -72
  177. package/dist/src/gui/gui-endpoint-commands.js +4 -3
  178. package/dist/src/gui/gui-endpoint-common.js +4 -4
  179. package/dist/src/gui/gui-endpoints.js +11 -380
  180. package/dist/src/gui/gui-history-endpoints.js +301 -0
  181. package/dist/src/gui/gui-operator-commands.js +108 -13
  182. package/dist/src/gui/gui-prompt-commands.js +11 -14
  183. package/dist/src/gui/gui-report-endpoints.js +90 -0
  184. package/dist/src/gui/gui-server.js +11 -4
  185. package/dist/src/gui/gui-team-commands.js +7 -7
  186. package/dist/src/gui/http-body.js +1 -1
  187. package/dist/src/history/history-data.js +122 -234
  188. package/dist/src/history/history-queued.js +147 -0
  189. package/dist/src/history/history-render.js +42 -22
  190. package/dist/src/history/history.js +193 -49
  191. package/dist/src/inbox/inbox-attachments.js +29 -30
  192. package/dist/src/inbox/inbox-cancel.js +10 -11
  193. package/dist/src/inbox/inbox-edit.js +9 -9
  194. package/dist/src/inbox/inbox-submit.js +18 -18
  195. package/dist/src/inbox/inbox-top.js +25 -9
  196. package/dist/src/inbox/inbox.js +42 -43
  197. package/dist/src/inbox/pending-prompt.js +26 -26
  198. package/dist/src/inbox/prompt-commands.js +62 -49
  199. package/dist/src/inbox/prompt-not-before.js +2 -2
  200. package/dist/src/init/init-templates.js +1 -2
  201. package/dist/src/init/init.js +28 -23
  202. package/dist/src/merge/backlog-conflicts.js +215 -35
  203. package/dist/src/merge/merge-batch.js +14 -8
  204. package/dist/src/merge/merge-cancel.js +53 -0
  205. package/dist/src/merge/merge-check-failures.js +44 -22
  206. package/dist/src/merge/merge-core.js +81 -32
  207. package/dist/src/merge/merge-diff.js +5 -4
  208. package/dist/src/merge/merge-drain.js +150 -11
  209. package/dist/src/merge/merge-git.js +177 -38
  210. package/dist/src/merge/merge-pipeline.js +15 -3
  211. package/dist/src/merge/merge-post-review.js +90 -0
  212. package/dist/src/merge/merge-queue-render.js +49 -11
  213. package/dist/src/merge/merge-queue.js +31 -15
  214. package/dist/src/merge/merge-requeue.js +102 -0
  215. package/dist/src/merge/merge-slot.js +61 -16
  216. package/dist/src/merge/merge-stack.js +33 -11
  217. package/dist/src/merge/merge-to-main.js +86 -27
  218. package/dist/src/merge/merge-top.js +69 -0
  219. package/dist/src/merge/merge-verify.js +12 -10
  220. package/dist/src/notes-cli.js +30 -15
  221. package/dist/src/operator/operator-commands.js +192 -81
  222. package/dist/src/operator/operator-intent.js +69 -42
  223. package/dist/src/operator/operator-requests.js +35 -40
  224. package/dist/src/operator/retire.js +5 -6
  225. package/dist/src/operator/revert.js +142 -34
  226. package/dist/src/orchestrator/orchestrator-launch.js +60 -52
  227. package/dist/src/orchestrator/orchestrator-poll.js +320 -0
  228. package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
  229. package/dist/src/orchestrator/orchestrator.js +128 -369
  230. package/dist/src/orchestrator/retention.js +42 -8
  231. package/dist/src/orchestrator/scheduling-admit.js +116 -0
  232. package/dist/src/orchestrator/scheduling-scope.js +201 -0
  233. package/dist/src/orchestrator/scheduling-skip.js +185 -0
  234. package/dist/src/orchestrator/startup-migration-steps.js +8 -7
  235. package/dist/src/orchestrator/startup-migrations.js +11 -7
  236. package/dist/src/paths.js +116 -49
  237. package/dist/src/pi/command-shape.js +12 -1
  238. package/dist/src/pi/pi-args.js +6 -4
  239. package/dist/src/pi/pi-backend.js +6 -6
  240. package/dist/src/pi/pi-bin.js +54 -63
  241. package/dist/src/pi/pi-listed-models.js +53 -0
  242. package/dist/src/pi/pi-models.js +63 -30
  243. package/dist/src/pi/pi-run-result.js +1 -1
  244. package/dist/src/pi/pi-settings.js +1 -1
  245. package/dist/src/pi/pi-stream.js +6 -6
  246. package/dist/src/pi/pi-watchdogs.js +18 -19
  247. package/dist/src/pi-extension/bounded-output.js +5 -3
  248. package/dist/src/pi-extension/context-budget.js +2 -2
  249. package/dist/src/pi-extension/context-shake.js +1 -1
  250. package/dist/src/pi-extension/context-window.js +0 -1
  251. package/dist/src/pi-extension/full-output.js +3 -10
  252. package/dist/src/pi-extension/strip-thinking.js +0 -1
  253. package/dist/src/pi-extension/trim-system-prompt.js +3 -4
  254. package/dist/src/pi-extension/write-guard.js +37 -0
  255. package/dist/src/process/launch-services.js +1 -2
  256. package/dist/src/process/node-bin.js +16 -0
  257. package/dist/src/process/process-group.js +8 -18
  258. package/dist/src/process/process.js +41 -2
  259. package/dist/src/process/run-marker.js +18 -6
  260. package/dist/src/process/supervisor.js +22 -21
  261. package/dist/src/prompt/dialect.js +17 -2
  262. package/dist/src/prompt/gate-notes.js +203 -0
  263. package/dist/src/prompt/gate-prompts.js +121 -211
  264. package/dist/src/prompt/notes-backlog.js +181 -0
  265. package/dist/src/prompt/principles.js +1 -2
  266. package/dist/src/prompt/prompt-followup.js +19 -14
  267. package/dist/src/prompt/prompt.js +129 -204
  268. package/dist/src/redeploy/redeploy-policy.js +24 -19
  269. package/dist/src/redeploy/redeploy-probes.js +1 -1
  270. package/dist/src/redeploy/redeploy.js +2 -3
  271. package/dist/src/redeploy/redeployer.js +20 -20
  272. package/dist/src/redeploy/self-reload.js +2 -1
  273. package/dist/src/report/report-data.js +161 -77
  274. package/dist/src/report/report-fold.js +129 -52
  275. package/dist/src/report/report-main-red.js +55 -0
  276. package/dist/src/report/report-render.js +133 -64
  277. package/dist/src/report/report-types.js +1 -0
  278. package/dist/src/report/report.js +26 -9
  279. package/dist/src/request-timeouts.js +2 -2
  280. package/dist/src/review/known-flakes.js +100 -1
  281. package/dist/src/review/post-merge-review.js +106 -0
  282. package/dist/src/review/protected-edits.js +4 -5
  283. package/dist/src/review/review-followup.js +8 -8
  284. package/dist/src/review/review-precheck.js +66 -23
  285. package/dist/src/review/review-run.js +395 -0
  286. package/dist/src/review/review-verdict.js +2 -2
  287. package/dist/src/review/review.js +155 -360
  288. package/dist/src/review/secret-leaks.js +36 -16
  289. package/dist/src/review/suite-rerun.js +0 -1
  290. package/dist/src/roles/legacy-role-migration.js +80 -7
  291. package/dist/src/roles/role-catalog.js +69 -12
  292. package/dist/src/roles/role-guidance.js +55 -20
  293. package/dist/src/roles/role-labels.js +1 -2
  294. package/dist/src/roles/role-render.js +67 -50
  295. package/dist/src/roles/role-view.js +59 -47
  296. package/dist/src/roles/roles.js +31 -31
  297. package/dist/src/roles/runner-ids.js +97 -0
  298. package/dist/src/run/acceptance-greps.js +119 -0
  299. package/dist/src/run/feedback-filings.js +51 -0
  300. package/dist/src/{tick → run}/qa-coverage.js +9 -27
  301. package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
  302. package/dist/src/run/run-detail-data.js +179 -0
  303. package/dist/src/run/run-detail-render.js +98 -0
  304. package/dist/src/run/run-detail.js +168 -0
  305. package/dist/src/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
  306. package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
  307. package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
  308. package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
  309. package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
  310. package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
  311. package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
  312. package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
  313. package/dist/src/run/selfcheck-digest.js +40 -0
  314. package/dist/src/run/stage-backlog-checks.js +249 -0
  315. package/dist/src/run/stage-check.js +172 -0
  316. package/dist/src/run/stage-path-checks.js +730 -0
  317. package/dist/src/{loop → runner}/leftover.js +80 -25
  318. package/dist/src/{loop → runner}/model-fallback.js +12 -12
  319. package/dist/src/runner/revision.js +202 -0
  320. package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
  321. package/dist/src/runner/runner-outcome.js +107 -0
  322. package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
  323. package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
  324. package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
  325. package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
  326. package/dist/src/runner/runner-state.js +102 -0
  327. package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
  328. package/dist/src/scheduling/backoff.js +38 -38
  329. package/dist/src/scheduling/claims.js +35 -22
  330. package/dist/src/scheduling/once-round.js +22 -22
  331. package/dist/src/scheduling/quiet-hours.js +5 -6
  332. package/dist/src/scheduling/scheduling.js +74 -64
  333. package/dist/src/scheduling/work-merged-cache.js +2 -2
  334. package/dist/src/status/status-budget.js +55 -47
  335. package/dist/src/status/status-data.js +43 -16
  336. package/dist/src/status/status-inbox.js +2 -2
  337. package/dist/src/status/status-install.js +0 -1
  338. package/dist/src/status/status-polls.js +28 -28
  339. package/dist/src/status/status-roles.js +39 -13
  340. package/dist/src/team/error-storm.js +8 -8
  341. package/dist/src/team/failure-spread.js +2 -2
  342. package/dist/src/team/orchestrator-info.js +15 -9
  343. package/dist/src/team/reclaim.js +5 -6
  344. package/dist/src/team/team-hold.js +7 -7
  345. package/dist/src/team/team-polls.js +91 -50
  346. package/dist/src/team/team-state.js +8 -2
  347. package/dist/src/text/datetime.js +63 -17
  348. package/dist/src/text/format.js +48 -11
  349. package/dist/src/text/markdown.js +8 -5
  350. package/dist/src/text/phrases.js +57 -23
  351. package/dist/src/text/suggest.js +1 -1
  352. package/dist/src/text/text.js +67 -13
  353. package/dist/src/ui/badges.js +65 -37
  354. package/dist/src/ui/gui/gui-client-backlog.js +292 -0
  355. package/dist/src/ui/gui/gui-client-boot.js +32 -26
  356. package/dist/src/ui/gui/gui-client-composer.js +8 -9
  357. package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
  358. package/dist/src/ui/gui/gui-client-drawer.js +126 -285
  359. package/dist/src/ui/gui/gui-client-feed.js +271 -0
  360. package/dist/src/ui/gui/gui-client-history.js +434 -67
  361. package/dist/src/ui/gui/gui-client-labels.js +1 -2
  362. package/dist/src/ui/gui/gui-client-markdown.js +1 -2
  363. package/dist/src/ui/gui/gui-client-model.js +27 -36
  364. package/dist/src/ui/gui/gui-client-operator.js +55 -51
  365. package/dist/src/ui/gui/gui-client-pending.js +227 -23
  366. package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
  367. package/dist/src/ui/gui/gui-client-report.js +87 -427
  368. package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
  369. package/dist/src/ui/gui/gui-client-settings.js +56 -4
  370. package/dist/src/ui/gui/gui-client-team.js +25 -465
  371. package/dist/src/ui/gui/gui-client.js +27 -25
  372. package/dist/src/ui/gui/gui-icons.js +0 -2
  373. package/dist/src/ui/gui/gui-page.js +9 -7
  374. package/dist/src/ui/gui/gui-styles.js +34 -34
  375. package/dist/src/ui/progress-data.js +9 -11
  376. package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
  377. package/dist/src/ui/status-model.js +133 -125
  378. package/dist/src/ui/status-payload.js +136 -49
  379. package/dist/src/ui/status-render.js +101 -82
  380. package/dist/src/ui/team-alerts.js +248 -57
  381. package/dist/src/ui/tone.js +5 -6
  382. package/dist/src/ui/transcript-run.js +58 -0
  383. package/dist/src/ui/transcript-tail.js +28 -20
  384. package/dist/src/ui/transcript.js +63 -23
  385. package/dist/src/ui/tui/tui-app.js +0 -1
  386. package/dist/src/ui/tui/tui-backlog.js +51 -14
  387. package/dist/src/ui/tui/tui-editor-answer.js +27 -31
  388. package/dist/src/ui/tui/tui-editor-attach.js +3 -5
  389. package/dist/src/ui/tui/tui-editor-backends.js +132 -0
  390. package/dist/src/ui/tui/tui-editor-brief.js +10 -12
  391. package/dist/src/ui/tui/tui-editor-budget.js +35 -31
  392. package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
  393. package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
  394. package/dist/src/ui/tui/tui-editor-config.js +86 -0
  395. package/dist/src/ui/tui/tui-editor-entry.js +36 -47
  396. package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
  397. package/dist/src/ui/tui/tui-editor-instances.js +37 -0
  398. package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
  399. package/dist/src/ui/tui/tui-editor-interval.js +11 -9
  400. package/dist/src/ui/tui/tui-editor-model.js +29 -0
  401. package/dist/src/ui/tui/tui-editor-notes.js +3 -5
  402. package/dist/src/ui/tui/tui-editor-notify.js +25 -0
  403. package/dist/src/ui/tui/tui-editor-pause.js +48 -59
  404. package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
  405. package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
  406. package/dist/src/ui/tui/tui-editor-queued.js +5 -9
  407. package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
  408. package/dist/src/ui/tui/tui-editor-retire.js +4 -6
  409. package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
  410. package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
  411. package/dist/src/ui/tui/tui-editor-search.js +45 -33
  412. package/dist/src/ui/tui/tui-editor-session.js +60 -34
  413. package/dist/src/ui/tui/tui-editor-slots.js +23 -0
  414. package/dist/src/ui/tui/tui-editors.js +35 -2
  415. package/dist/src/ui/tui/tui-frame.js +56 -11
  416. package/dist/src/ui/tui/tui-history.js +8 -9
  417. package/dist/src/ui/tui/tui-input.js +13 -11
  418. package/dist/src/ui/tui/tui-keymap.js +0 -1
  419. package/dist/src/ui/tui/tui-keys.js +24 -9
  420. package/dist/src/ui/tui/tui-pane-activity.js +14 -11
  421. package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
  422. package/dist/src/ui/tui/tui-pane-brief.js +0 -1
  423. package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
  424. package/dist/src/ui/tui/tui-pane-health.js +58 -8
  425. package/dist/src/ui/tui/tui-pane-history.js +402 -82
  426. package/dist/src/ui/tui/tui-pane-pending.js +308 -13
  427. package/dist/src/ui/tui/tui-pane-queued.js +71 -55
  428. package/dist/src/ui/tui/tui-pane-report.js +46 -14
  429. package/dist/src/ui/tui/tui-pane-session.js +25 -6
  430. package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
  431. package/dist/src/ui/tui/tui-pane.js +80 -23
  432. package/dist/src/ui/tui/tui-panes.js +100 -30
  433. package/dist/src/ui/tui/tui.js +14 -14
  434. package/dist/src/verdict/fix-claim.js +82 -32
  435. package/dist/src/verdict/no-change.js +4 -4
  436. package/dist/src/verdict/refusal.js +40 -16
  437. package/dist/src/verdict/reply-contract.js +42 -31
  438. package/dist/src/version.js +0 -1
  439. package/package.json +2 -1
  440. package/dist/src/loop/revision.js +0 -70
  441. package/dist/src/roles/loop-ids.js +0 -77
  442. package/dist/src/tick/stage-check.js +0 -419
  443. package/dist/src/tick/telemetry-digest.js +0 -25
  444. package/dist/src/tick/tick-detail-data.js +0 -108
  445. package/dist/src/tick/tick-detail.js +0 -163
  446. package/dist/src/ui/gui/gui-client-sound.js +0 -53
@@ -1,21 +1,25 @@
1
- // vocabulary: plain
2
1
  import { fail, say, sayJsonLine } from "./cli-output.js";
3
- import { durationLabel, failRivalShapes, flagValue, parseCountFlag, parseGrepFlag, parseRoleScope, parseSinceFlag, parseTypeFlag } from "./cli-args.js";
2
+ import { hasFollowFlag, LOGS_TRANSCRIPT_VALUE_ERROR } from "./cli-flag-specs.js";
3
+ import { durationLabel, failRivalShapes, flagValue, parseCountFlag, parseGrepFlag, parseRoleListScope, parseRoleScope, parseSinceFlag, parseTypeFlag } from "./cli-args.js";
4
4
  import { LOGS_SINCE_MAX_MS, readEventsSince, SPARSE_WINDOW_NOTE, } from "../events/event-window.js";
5
- import { compareCodeUnits, increment, sortByValueDesc } from "../collections.js";
6
- import { parseEventLine, readEventsTailWithEnd } from "../events/event-read.js";
5
+ import { compareCodeUnits, increment } from "../collections.js";
6
+ import { eventTypeTally } from "../events/event-tally.js";
7
+ import { eventRole, parseEventLine, readEventsTailWithEnd } from "../events/event-read.js";
7
8
  import { eventMatchesGrep, formatEvent } from "../events/event-format.js";
8
9
  import { canonicalEventType } from "../events/event-legacy.js";
9
10
  import { EVENT_TYPE_LABELS, eventTypeLabel } from "../events/event-labels.js";
10
11
  import { followFile } from "../files/tail.js";
12
+ import { roleInScope } from "../roles/runner-ids.js";
13
+ import { startParentDeathWatch } from "../process/supervisor.js";
11
14
  import { typoSuffix } from "../text/suggest.js";
12
- import { createTranscriptRenderer } from "../ui/transcript.js";
15
+ import { splitCommaList } from "../text/text.js";
16
+ import { createTranscriptFilter, createTranscriptRenderer } from "../ui/transcript.js";
13
17
  import { readTranscriptTail } from "../ui/transcript-tail.js";
14
18
  import { eventsLogPath, piLogPath } from "../paths.js";
15
19
  /** The read-only observing half of the CLI's non-dispatch commands: `tumwater logs` and its
16
- * `--role` transcript view, split out of cli.ts so the entry point stays a dispatch table. Unlike
20
+ * `--transcript` view, split out of cli.ts so the entry point stays a dispatch table. Unlike
17
21
  * operator/operator-commands.ts these write nothing but stdout — they only read the event log and
18
- * each loop's agent transcript. It lives beside the other CLI command bodies (history.ts,
22
+ * each role's agent transcript. It lives beside the other CLI command bodies (history.ts,
19
23
  * cli/cli-query-commands.ts, operator/operator-commands.ts), which may import the ui/ rendering
20
24
  * layer they drive — here the event formatter and the transcript renderer — while src/ui/
21
25
  * itself stays off src/ module boundaries it does not own. */
@@ -36,28 +40,30 @@ function sayEventLine(e, json) {
36
40
  * In `--json` mode it emits one compact object mapping `type → count` with keys sorted, the
37
41
  * machine-readable twin of the prose tally. */
38
42
  function sayTally(events, json) {
39
- const counts = new Map();
40
- for (const e of events)
41
- increment(counts, e.type);
42
43
  if (json) {
44
+ // The machine-readable twin keeps its own alphabetical key sort; the prose tally uses the
45
+ // shared eventTypeTally ordering.
46
+ const counts = new Map();
47
+ for (const e of events)
48
+ increment(counts, e.type);
43
49
  const obj = {};
44
50
  for (const [type] of [...counts.entries()].sort((a, b) => compareCodeUnits(a[0], b[0])))
45
51
  obj[type] = counts.get(type);
46
52
  sayJsonLine(obj);
47
53
  return;
48
54
  }
49
- for (const [type, n] of sortByValueDesc([...counts.entries()]))
55
+ for (const [type, n] of eventTypeTally(events))
50
56
  say(`${n} × ${eventTypeLabel(type)} (${type})`);
51
57
  }
52
58
  /** The shared rival-shape guard for logs flags that only apply to the event-log feed:
53
- * `--role` swaps in a transcript rather than the event log (--prompt requires --role, so
54
- * it is excluded with it), so each such flag fails naming both. One wording for all of them —
55
- * the --json, --grep, and --since checks cannot drift apart in what they say or why. `active`
56
- * is the flag's own presence — --json alone composes with nothing here but --role, while
57
- * --grep and --since call from inside their own presence guards. */
58
- function rejectRoleViewRival(rest, flag, active) {
59
- if (active && (rest.includes("--role") || rest.includes("--prompt")))
60
- failRivalShapes(`logs ${flag}`, "--role", "the --role view is the role's agent transcript, not the event log; --prompt requires --role");
59
+ * `--transcript` swaps in a transcript rather than the event log (--prompt requires
60
+ * --transcript, so it is excluded with it), so each such flag fails naming both. One wording
61
+ * for all of them — the --json, --count, --type and --since checks cannot drift apart in what
62
+ * they say or why. `active` is the flag's own presence. --grep is not one of them: it also
63
+ * filters the transcript view. */
64
+ function rejectTranscriptViewRival(rest, flag, active) {
65
+ if (active && (rest.includes("--transcript") || rest.includes("--prompt")))
66
+ failRivalShapes(`logs ${flag}`, "--transcript", "the --transcript view is the role's agent transcript, not the event log; --prompt requires --transcript");
61
67
  }
62
68
  /** The missing-pattern error cmdLogs prints for a valueless `--grep`, exported so cli.ts's
63
69
  * rejectUnknownArgs spec for --grep can fail a trailing `logs --grep` with the same wording
@@ -73,97 +79,140 @@ export const TYPE_VALUE_ERROR = "logs --type needs an event type";
73
79
  * completion already offers the valid codes, so a bare no-match for a near-miss reads as a silent
74
80
  * mystery) — while a VALID type that merely has no matching events stays hint-free, a legitimate
75
81
  * empty result rather than a typo. A valid type or a legacy alias (canonicalEventType maps the
76
- * alias onto the current code, which is a label key) never hints. */
77
- function typeEmpty(type) {
78
- const canonical = canonicalEventType(type);
79
- const hint = Object.hasOwn(EVENT_TYPE_LABELS, canonical)
80
- ? ""
81
- : typoSuffix(type, Object.keys(EVENT_TYPE_LABELS));
82
+ * alias onto the current code, which is a label key) never hints. For a comma list the hint
83
+ * is for the first piece that is not a valid type; the prose names the value as typed. */
84
+ function typeEmpty(type, pieces) {
85
+ const bad = pieces.find((p) => !Object.hasOwn(EVENT_TYPE_LABELS, canonicalEventType(p)));
86
+ const hint = bad === undefined ? "" : typoSuffix(bad, Object.keys(EVENT_TYPE_LABELS));
82
87
  return `no events matching "${type}"${hint}`;
83
88
  }
84
- /** `tumwater logs [-f] [-n <count>] [--since <duration>] [--role <id>] [--prompt]`: follow or
85
- * dump the harness event log, with `--since` a bounded past window of it, or with `--role` one
86
- * loop's agent transcript (see cmdLogsTranscript). */
87
- export async function cmdLogs(root, args) {
88
- // `--grep <text>` filters the event feed (the -n view and its follow): case-insensitive
89
+ /** Follow `file` until Ctrl+C or until the process that started this one is gone (the shape of
90
+ * a `logs -f` leaked to PPID 1 that polled for days). Resolves once the parent is gone. */
91
+ export function followUntilParentGone(file, offset, onLines, seams) {
92
+ const stop = followFile(file, offset, onLines);
93
+ return new Promise((resolve) => {
94
+ startParentDeathWatch(() => {
95
+ stop();
96
+ resolve();
97
+ }, {
98
+ ...(seams.ppid ? { ppid: seams.ppid } : {}),
99
+ ...(seams.intervalMs !== undefined ? { intervalMs: seams.intervalMs } : {}),
100
+ });
101
+ });
102
+ }
103
+ /** `tumwater logs [-f] [-n <count>] [--since <duration>] [--until <duration>]
104
+ * [--role <id>[,<id>...]] [--transcript <id>] [--prompt]
105
+ * [--grep <text>]`: follow or dump the harness event log, with `--since` a bounded
106
+ * past window of it and `--role` keeping only the listed roles' events (a base role id takes its
107
+ * instances too),
108
+ * or with `--transcript` one role's agent transcript (see cmdLogsTranscript). `-f` follows until
109
+ * Ctrl+C or until its parent process exits. */
110
+ export async function cmdLogs(root, args, seams = {}) {
111
+ // `--grep <text>` filters the event feed (the -n view and its follow) and, with --transcript,
112
+ // the transcript's entries (see cmdLogsTranscript): case-insensitive
89
113
  // substring against `${e.type} ${formatEvent(e)}` — the rendered line is what the operator
90
114
  // would otherwise read (WYSIWYG), and prefixing the raw type id lets stable ids
91
115
  // (review_rejected, merge_failed) be filtered even where the rendering paraphrases them.
92
- // Rival shapes stay exclusive — --role swaps in a transcript rather than this event log
93
- // (--prompt requires --role, so it is excluded with it), and --since is a filter of its own
94
- // rather than a window to filter — and each failure names both flags. The flag scan itself
116
+ // --grep filters the --since window like --type and --role do. The flag scan itself
95
117
  // (the value lookup, the `rest` construction that keeps a flag-shaped pattern from
96
118
  // impersonating a rival flag, and the empty-value fail) is cli/cli-args.ts's parseGrepFlag,
97
119
  // the one home shared with history's identical preamble.
98
120
  const { rest: grepped, pattern: grepPattern } = parseGrepFlag(args, GREP_VALUE_ERROR);
99
121
  // `--type <eventType>` filters the event feed by EXACT event type (e.type === value):
100
122
  // deterministic where --grep substrings — a merge_failed whose rendered line quotes
101
- // "merge_failed" matches --grep but not --type. Unlike --grep it composes with --since
102
- // (the window, not a filter of its own) as well as -n, -f, --json, and --grep; like --json
103
- // it is a rival only of the --role/--prompt transcript view. The value scan (keeping a
123
+ // "merge_failed" matches --grep but not --type. It composes with --since
124
+ // (the window it filters) as well as -n, -f, --json, and --grep; like --json
125
+ // it is a rival only of the --transcript/--prompt transcript view. A comma list matches any
126
+ // of its types. The value scan (keeping a
104
127
  // flag-shaped value like --since as text, the empty-value fail) mirrors parseGrepFlag via
105
128
  // parseTypeFlag, so the two cannot drift; `rest` here has both flags' value tokens removed.
106
129
  const { rest, type } = parseTypeFlag(grepped, TYPE_VALUE_ERROR);
107
130
  const grepLower = grepPattern === null ? null : grepPattern.toLowerCase();
108
131
  // An older event name matches its current one; the empty-match prose still names what was typed.
109
- const wantedType = type === null ? null : canonicalEventType(type);
110
- const typeFilter = type === null ? null : (e) => e.type === wantedType;
132
+ const typePieces = type === null ? [] : splitCommaList(type);
133
+ if (type !== null && typePieces.length === 0)
134
+ fail(TYPE_VALUE_ERROR);
135
+ const wantedTypes = new Set(typePieces.map(canonicalEventType));
136
+ const typeFilter = type === null ? null : (e) => wantedTypes.has(e.type);
111
137
  // The composed feed rule every emit site threads today's grep-only predicate through:
112
138
  // an event passes when it is absent-or-matches the --grep substring AND absent-or-equals
113
139
  // the --type id, held in one place so the --since window, the -n tally, and the follow
114
140
  // cannot drift on what an event must satisfy.
141
+ // `--role <id>[,<id>...]` joins them: an event passes when the role it was filed under, or
142
+ // that role's base id (so `--role feature` takes `feature-2` too), is listed. It
143
+ // composes with --since. The value scan reads `rest`, which holds no --grep/--type value tokens.
144
+ const roles = parseRoleListScope(root, rest);
145
+ const roleFilter = roles === null
146
+ ? null
147
+ : (e) => roleInScope(eventRole(e), roles);
148
+ if (roles !== null)
149
+ rejectTranscriptViewRival(rest, "--role", true);
115
150
  const feedFilter = (e) => (grepLower === null || eventMatchesGrep(e, grepLower)) &&
116
- (typeFilter === null || typeFilter(e));
117
- const follow = rest.includes("-f") || rest.includes("--follow");
151
+ (typeFilter === null || typeFilter(e)) &&
152
+ (roleFilter === null || roleFilter(e));
153
+ // Empty-match prose: grep/type keep their wording when given; a role-only filter names the roles.
154
+ const roleEmpty = roles === null ? "" : `no events from ${roles.join(", ")}`;
155
+ const filtersGrepOrType = grepPattern !== null || type !== null;
156
+ const follow = hasFollowFlag(rest);
118
157
  // `--json` switches the feed from formatEvent rendering to one JSON.stringify(e) per line —
119
158
  // the raw HarnessEvent objects exactly as stored in the log — so scripts read the canonical
120
159
  // schema instead of parsing a rendering that changes shape whenever the formatter evolves.
121
160
  // It composes with -n, --grep, --since and -f (the grep filter applies before serialization,
122
- // the follow streams JSON lines as events land), but not with --role: that view is a pi
123
- // transcript of human-oriented prose, not the event log (--prompt requires --role, so it is
161
+ // the follow streams JSON lines as events land), but not with --transcript: that view is a pi
162
+ // transcript of human-oriented prose, not the event log (--prompt requires --transcript, so it is
124
163
  // excluded with it).
125
164
  const json = rest.includes("--json");
126
- rejectRoleViewRival(rest, "--json", json);
165
+ rejectTranscriptViewRival(rest, "--json", json);
127
166
  // `--count` tallies the window's present event types instead of printing the rows. Rival
128
167
  // shapes stay exclusive: counting implies a bounded window (so -f/--follow is rejected)
129
- // and the --role view is the role's agent transcript rather than the event log.
168
+ // and the --transcript view is the role's agent transcript rather than the event log.
130
169
  const count = rest.includes("--count");
131
- rejectRoleViewRival(rest, "--count", count);
170
+ rejectTranscriptViewRival(rest, "--count", count);
132
171
  if (count && follow)
133
172
  failRivalShapes("logs --count", "-f/--follow", "counting implies a bounded window; -f follows from now on");
134
173
  if (type !== null)
135
- rejectRoleViewRival(rest, "--type", true);
136
- if (grepPattern !== null) {
137
- rejectRoleViewRival(rest, "--grep", true);
138
- if (rest.includes("--since"))
139
- failRivalShapes("logs --grep", "--since", "--since is a filter of its own; --grep filters the -n view and its follow");
140
- }
174
+ rejectTranscriptViewRival(rest, "--type", true);
141
175
  // `--since <duration>` is the window-shaped view over the same event log the -n view dumps:
142
176
  // it reads a bounded past window (event-window.ts's day-keyed backwards scan) and prints the
143
177
  // survivors oldest-first, so an operator can ask for "the hour around that 429 storm" without
144
178
  // guessing a count. Rival shapes stay exclusive — follow means "from now on", -n means "last
145
- // N", and --role swaps in a transcript rather than this log — and each failure names
146
- // both flags. (--prompt requires --role, so it is excluded with it.)
179
+ // N", and --transcript swaps in a transcript rather than this log — and each failure names
180
+ // both flags. (--prompt requires --transcript, so it is excluded with it.)
147
181
  const ms = parseSinceFlag(rest, "logs --since", LOGS_SINCE_MAX_MS);
182
+ const untilMs = parseSinceFlag(rest, "logs --until", LOGS_SINCE_MAX_MS, "--until");
183
+ if (untilMs !== null && ms === null)
184
+ fail("logs --until requires --since");
185
+ if (untilMs !== null && ms !== null && untilMs >= ms)
186
+ fail(`logs --until (${durationLabel(untilMs)}) must be shorter than --since (${durationLabel(ms)})`);
148
187
  if (ms !== null) {
149
188
  if (follow)
150
189
  failRivalShapes("logs --since", "-f/--follow", 'follow means "from now on"; --since shows a bounded past window');
151
190
  if (rest.includes("-n"))
152
191
  failRivalShapes("logs --since", "-n", "a count and a window are rival shapes");
153
- rejectRoleViewRival(rest, "--since", true);
192
+ rejectTranscriptViewRival(rest, "--since", true);
154
193
  // covered is only consulted below when the window has rows (an empty window returns
155
194
  // earlier), so the helper's vacuous empty-log branch never reaches the note here.
156
- const { events, covered } = readEventsSince(root, ms);
157
- // `--type` composes with the window (it is NOT the --since rival --grep is): only the
158
- // window events of the exact type are printed, and an all-filtered window answers with
159
- // the no-match prose (the type's own line) instead of the generic empty-window one —
195
+ const { cutoff, events: windowEvents, covered } = readEventsSince(root, ms);
196
+ const events = untilMs === null ? windowEvents : windowEvents.filter((e) => e.ts < cutoff + ms - untilMs);
197
+ const windowPhrase = untilMs === null ? `in ${durationLabel(ms)}` : `between ${durationLabel(ms)} and ${durationLabel(untilMs)} ago`;
198
+ // `--grep`, `--type` and `--role` all filter the window through feedFilter: only the
199
+ // window events that pass are printed, and an all-filtered window answers with the
200
+ // no-match prose (the filter's own line) instead of the generic empty-window one —
160
201
  // "which merge_failed in the last hour" filters the same as it would the -n view.
161
- const filtered = type === null ? events : events.filter((e) => e.type === wantedType);
202
+ const filtered = events.filter(feedFilter);
162
203
  if (filtered.length === 0) {
163
204
  // In JSON mode an empty result answers silently — an empty output IS the machine-readable
164
205
  // answer, and prose would corrupt a consumer's NDJSON stream.
165
- if (!json)
166
- say(type === null ? `no events in ${durationLabel(ms)}` : typeEmpty(type));
206
+ if (!json) {
207
+ if (grepPattern !== null && type !== null)
208
+ say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
209
+ else if (type !== null)
210
+ say(typeEmpty(type, typePieces));
211
+ else if (grepPattern !== null)
212
+ say(`no events matching "${grepPattern}" ${windowPhrase}`);
213
+ else
214
+ say(roles !== null ? `${roleEmpty} ${windowPhrase}` : `no events ${windowPhrase}`);
215
+ }
167
216
  return;
168
217
  }
169
218
  // `--count` composes with the window: the tally covers exactly the filtered window rows
@@ -187,14 +236,14 @@ export async function cmdLogs(root, args) {
187
236
  }
188
237
  const nRaw = flagValue(rest, "-n");
189
238
  const limit = nRaw !== null ? parseCountFlag("-n", nRaw) : 50;
190
- // The event feed (no --role) never needs the config at all: parseRoleScope reads it only
239
+ // The event feed (no --transcript) never needs the config at all: parseRoleScope reads it only
191
240
  // when the flag is present.
192
- const role = parseRoleScope(root, rest);
241
+ const role = parseRoleScope(root, rest, "--transcript", LOGS_TRANSCRIPT_VALUE_ERROR);
193
242
  const showPrompts = rest.includes("--prompt");
194
243
  if (showPrompts && role === null)
195
- fail("logs --prompt needs --role <id>");
244
+ fail("logs --prompt needs --transcript <id>");
196
245
  if (role !== null) {
197
- await cmdLogsTranscript(root, role, limit, follow, showPrompts);
246
+ await cmdLogsTranscript(root, role, limit, follow, showPrompts, grepLower, grepPattern, seams);
198
247
  return;
199
248
  }
200
249
  // -n bounds the scan, not the print: the matched subset of the last `limit` events is what
@@ -202,7 +251,7 @@ export async function cmdLogs(root, args) {
202
251
  // the tail-with-end shape (not the per-poll cached readEvents) so the follow below can seed
203
252
  // from the byte end this very read covered instead of a later stat of the file.
204
253
  const tail = readEventsTailWithEnd(root, limit);
205
- const shown = grepLower !== null || type !== null ? tail.events.filter(feedFilter) : tail.events;
254
+ const shown = grepLower !== null || type !== null || roleFilter !== null ? tail.events.filter(feedFilter) : tail.events;
206
255
  // `--count` over the -n view answers with the tally of exactly the filtered rows the plain
207
256
  // view would print, and carries over the plain view's empty-match prose below (a `--type`
208
257
  // or `--grep` that matches nothing names its value; an empty unfiltered -n view prints
@@ -210,13 +259,15 @@ export async function cmdLogs(root, args) {
210
259
  if (count) {
211
260
  if (shown.length !== 0)
212
261
  sayTally(shown, json);
213
- else if (!json && (grepPattern !== null || type !== null)) {
262
+ else if (!json && (filtersGrepOrType || roles !== null)) {
214
263
  if (grepPattern !== null && type !== null)
215
- say(`no events matching "${grepPattern}" or ${typeEmpty(type)}`);
264
+ say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
216
265
  // Type-empty gets the hint (only the type branch); a grep-only empty stays the bare
217
266
  // no-match line, because a free-text pattern is not an event type to suggest against.
267
+ else if (!filtersGrepOrType)
268
+ say(roleEmpty);
218
269
  else
219
- say(type !== null ? typeEmpty(type) : `no events matching "${grepPattern ?? ""}"`);
270
+ say(type !== null ? typeEmpty(type, typePieces) : `no events matching "${grepPattern ?? ""}"`);
220
271
  }
221
272
  return;
222
273
  }
@@ -226,13 +277,15 @@ export async function cmdLogs(root, args) {
226
277
  // The empty-match line is prose: in JSON mode the empty output is the answer. It fires
227
278
  // for either filter, naming the flag's value(s) whose match came up empty so the operator
228
279
  // knows what to loosen.
229
- if (shown.length === 0 && !json && (grepPattern !== null || type !== null)) {
280
+ if (shown.length === 0 && !json && (filtersGrepOrType || roles !== null)) {
230
281
  if (grepPattern !== null && type !== null)
231
- say(`no events matching "${grepPattern}" or ${typeEmpty(type)}`);
282
+ say(`no events matching "${grepPattern}" or ${typeEmpty(type, typePieces)}`);
232
283
  // Type-empty gets the hint (only the type branch); a grep-only empty stays the bare
233
284
  // no-match line, because a free-text pattern is not an event type to suggest against.
285
+ else if (!filtersGrepOrType)
286
+ say(roleEmpty);
234
287
  else
235
- say(type !== null ? typeEmpty(type) : `no events matching "${grepPattern ?? ""}"`);
288
+ say(type !== null ? typeEmpty(type, typePieces) : `no events matching "${grepPattern ?? ""}"`);
236
289
  }
237
290
  return;
238
291
  }
@@ -245,7 +298,7 @@ export async function cmdLogs(root, args) {
245
298
  // log scans to coveredEnd 0, and followFile tolerates a missing file (its poll re-stats
246
299
  // every interval), so a read-only command never leaves a harness state file behind — the
247
300
  // module contract above promises writes to stdout only.
248
- followFile(file, tail.coveredEnd, (lines) => {
301
+ await followUntilParentGone(file, tail.coveredEnd, (lines) => {
249
302
  for (const line of lines.filter(Boolean)) {
250
303
  const e = parseEventLine(line);
251
304
  // The filter holds across rotation: every event the follow callback sees goes through
@@ -253,19 +306,31 @@ export async function cmdLogs(root, args) {
253
306
  if (e && feedFilter(e))
254
307
  sayEventLine(e, json);
255
308
  }
256
- });
257
- await new Promise(() => { }); // Follow until Ctrl+C.
309
+ }, seams);
258
310
  }
259
- /** `tumwater logs --role <id>`: print (and optionally follow) one loop's agent transcript —
311
+ /** `tumwater logs --transcript <id>`: print (and optionally follow) one role's agent transcript —
260
312
  * run separators, abbreviated thinking, assistant text, and tool calls; with `--prompt` also
261
313
  * each run's exact prompt text. Read-only; the raw log is pi's streaming event stream, so only
262
- * complete renderable events are shown. */
263
- async function cmdLogsTranscript(root, role, limit, follow, showPrompts) {
314
+ * complete renderable events are shown. With `--grep`, only entries with a rendered line
315
+ * containing the text (case-insensitively) print, each under its run's separator; `-n` still
316
+ * bounds the entries scanned, not printed. */
317
+ async function cmdLogsTranscript(root, role, limit, follow, showPrompts, needle, pattern, seams) {
264
318
  const file = piLogPath(root, role);
265
319
  const opts = { includePrompts: showPrompts };
320
+ const filter = needle === null ? null : createTranscriptFilter(needle);
321
+ let matched = 0;
266
322
  const printEntry = (lines) => {
267
- if (lines.length > 0)
323
+ if (lines.length === 0)
324
+ return;
325
+ if (filter === null) {
268
326
  say(lines.join("\n"));
327
+ return;
328
+ }
329
+ const out = filter(lines);
330
+ if (out === null)
331
+ return;
332
+ matched++;
333
+ say(out.join("\n"));
269
334
  };
270
335
  // Initial window: the last `limit` entries of what is on disk. readTranscriptTail scans back
271
336
  // from EOF only as far as needed instead of re-reading the whole (up to logMaxBytes) file,
@@ -280,6 +345,8 @@ async function cmdLogsTranscript(root, role, limit, follow, showPrompts) {
280
345
  for (const entry of tail.entries)
281
346
  printEntry(entry);
282
347
  offset = tail.end;
348
+ if (needle !== null && matched === 0)
349
+ say(`no transcript entries matching "${pattern ?? ""}" for ${role}`);
283
350
  }
284
351
  if (!follow)
285
352
  return;
@@ -288,9 +355,8 @@ async function cmdLogsTranscript(root, role, limit, follow, showPrompts) {
288
355
  // readTranscriptTail's formatTranscript already flushed any pending separator for what
289
356
  // was on disk.
290
357
  const renderer = createTranscriptRenderer(opts);
291
- followFile(file, offset, (lines) => {
358
+ await followUntilParentGone(file, offset, (lines) => {
292
359
  for (const line of lines)
293
360
  printEntry(renderer.feed(line));
294
- });
295
- await new Promise(() => { }); // Follow until Ctrl+C.
361
+ }, seams);
296
362
  }