tumwater 0.1.3 → 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.
- package/README.md +77 -28
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +23 -0
- package/dist/src/agent/agent-backend.js +9 -7
- package/dist/src/agent/backend-traits.js +77 -0
- package/dist/src/agent/claude/claude-account.js +81 -161
- package/dist/src/agent/claude/claude-args.js +19 -19
- package/dist/src/agent/claude/claude-backend.js +58 -48
- package/dist/src/agent/claude/claude-env.js +7 -3
- package/dist/src/agent/claude/claude-stream.js +11 -19
- package/dist/src/agent/claude/claude-write-guard.js +49 -0
- package/dist/src/agent/claude/write-guard-hook.js +13 -0
- package/dist/src/agent/codex/codex-account.js +119 -0
- package/dist/src/agent/codex/codex-args.js +181 -0
- package/dist/src/agent/codex/codex-backend.js +279 -0
- package/dist/src/agent/codex/codex-catalog.js +97 -0
- package/dist/src/agent/codex/codex-env.js +18 -0
- package/dist/src/agent/codex/codex-hook-trust.js +246 -0
- package/dist/src/agent/codex/codex-patch-paths.js +36 -0
- package/dist/src/agent/codex/codex-rollout.js +179 -0
- package/dist/src/agent/codex/codex-stream.js +402 -0
- package/dist/src/agent/codex/codex-write-guard.js +86 -0
- package/dist/src/agent/codex/write-guard-hook.js +12 -0
- package/dist/src/agent/image-input.js +112 -0
- package/dist/src/agent/notes-shim.js +40 -0
- package/dist/src/agent/opencode/opencode-args.js +60 -0
- package/dist/src/agent/opencode/opencode-backend.js +258 -0
- package/dist/src/agent/opencode/opencode-catalog.js +210 -0
- package/dist/src/agent/opencode/opencode-config.js +193 -0
- package/dist/src/agent/opencode/opencode-env.js +35 -0
- package/dist/src/agent/opencode/opencode-session-id.js +36 -0
- package/dist/src/agent/opencode/opencode-stream.js +278 -0
- package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
- package/dist/src/agent/role-notes-write.js +16 -33
- package/dist/src/agent/run-agent.js +475 -381
- package/dist/src/agent/run-settle.js +214 -0
- package/dist/src/agent/session-ledger.js +142 -16
- package/dist/src/agent/subscription-account.js +220 -0
- package/dist/src/agent/write-guard.js +148 -0
- package/dist/src/backlog/backlog-archive.js +487 -0
- package/dist/src/backlog/backlog-cli-view.js +280 -0
- package/dist/src/backlog/backlog-cli.js +384 -0
- package/dist/src/backlog/backlog-commit.js +4 -5
- package/dist/src/backlog/backlog-eligibility.js +354 -132
- package/dist/src/backlog/backlog-index.js +149 -0
- package/dist/src/backlog/backlog-md.js +132 -22
- package/dist/src/backlog/backlog-parts.js +254 -0
- package/dist/src/backlog/backlog-release.js +117 -0
- package/dist/src/backlog/backlog-render.js +89 -11
- package/dist/src/backlog/backlog-structure.js +271 -125
- package/dist/src/backlog/backlog-write.js +121 -471
- package/dist/src/backlog/backlog.js +129 -17
- package/dist/src/backlog/question-answer.js +345 -0
- package/dist/src/baseline/main-baseline.js +157 -101
- package/dist/src/baseline/main-culprit.js +94 -0
- package/dist/src/baseline/main-guardrails.js +209 -0
- package/dist/src/baseline/main-red.js +164 -55
- package/dist/src/baseline/main-revert.js +133 -0
- package/dist/src/baseline/main-watch.js +184 -0
- package/dist/src/brief/brief-edit.js +41 -0
- package/dist/src/{brief.js → brief/brief.js} +38 -33
- package/dist/src/budget/budget.js +55 -28
- package/dist/src/budget/fallback-breaker.js +31 -31
- package/dist/src/build/build-check-detect.js +25 -16
- package/dist/src/build/build-check-events.js +87 -22
- package/dist/src/build/build-check-report.js +66 -27
- package/dist/src/build/build-check-scoped.js +64 -68
- package/dist/src/build/build-check.js +17 -15
- package/dist/src/build/build-info.js +3 -3
- package/dist/src/build/build-stage.js +31 -35
- package/dist/src/build/dep-install.js +21 -21
- package/dist/src/build/host-sleep.js +1 -1
- package/dist/src/change/change-data.js +131 -37
- package/dist/src/change/change-render.js +56 -14
- package/dist/src/cli/brief-command.js +9 -8
- package/dist/src/cli/cli-args.js +133 -28
- package/dist/src/cli/cli-command-args.js +82 -21
- package/dist/src/cli/cli-editor.js +15 -2
- package/dist/src/cli/cli-flag-specs.js +180 -50
- package/dist/src/cli/cli-init.js +51 -0
- package/dist/src/cli/cli-marker-commands.js +14 -11
- package/dist/src/cli/cli-query-commands.js +325 -224
- package/dist/src/cli/cli-repo-gate.js +12 -2
- package/dist/src/cli/cli-role-command.js +420 -0
- package/dist/src/cli/cli-start-summary.js +160 -0
- package/dist/src/cli/cli-start.js +453 -0
- package/dist/src/cli/completion.js +202 -17
- package/dist/src/cli/config-commands.js +51 -25
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +413 -157
- package/dist/src/cli/log-commands.js +234 -62
- package/dist/src/cli/notify-command.js +33 -18
- package/dist/src/cli/question-commands.js +215 -231
- package/dist/src/cli.js +154 -49
- package/dist/src/collections.js +84 -18
- package/dist/src/concurrency/check-permit.js +35 -19
- package/dist/src/concurrency/lock.js +22 -17
- package/dist/src/concurrency/provider-lease.js +105 -0
- package/dist/src/concurrency/semaphore.js +344 -46
- package/dist/src/config/config-aliases.js +105 -0
- package/dist/src/config/config-editable-keys.js +16 -4
- package/dist/src/config/config-fallbacks.js +416 -0
- package/dist/src/config/config-field-checks.js +58 -19
- package/dist/src/config/config-keys.js +182 -0
- package/dist/src/config/config-live.js +113 -22
- package/dist/src/config/config-request.js +137 -0
- package/dist/src/config/config-schema.js +58 -39
- package/dist/src/config/config-selector-checks.js +123 -0
- package/dist/src/config/config-validation-agents.js +80 -0
- package/dist/src/config/config-validation-model.js +116 -0
- package/dist/src/config/config-validation.js +201 -175
- package/dist/src/config/config-views.js +202 -346
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +219 -391
- package/dist/src/config/config.js +102 -81
- package/dist/src/config/model-selector.js +44 -18
- package/dist/src/doctor/doctor-backlog-bugs.js +287 -0
- package/dist/src/doctor/doctor-backlog-plans.js +263 -0
- package/dist/src/doctor/doctor-backlog.js +191 -161
- package/dist/src/doctor/doctor-check-names.js +81 -0
- package/dist/src/doctor/doctor-checks.js +229 -305
- package/dist/src/doctor/doctor-claude-checks.js +145 -0
- package/dist/src/doctor/doctor-codex-checks.js +279 -0
- package/dist/src/doctor/doctor-contention-checks.js +380 -0
- package/dist/src/doctor/doctor-holds.js +181 -104
- package/dist/src/doctor/doctor-install.js +31 -0
- package/dist/src/doctor/doctor-launch-services.js +2 -2
- package/dist/src/doctor/doctor-model-checks.js +496 -270
- package/dist/src/doctor/doctor-notify.js +5 -7
- package/dist/src/doctor/doctor-opencode-checks.js +162 -0
- package/dist/src/doctor/doctor-orphans.js +189 -69
- package/dist/src/doctor/doctor-project-checks.js +303 -0
- package/dist/src/doctor/doctor-render.js +36 -9
- package/dist/src/doctor/doctor.js +145 -50
- package/dist/src/doctor/image-probe.js +257 -0
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +60 -0
- package/dist/src/events/event-format.js +363 -155
- package/dist/src/events/event-labels.js +123 -0
- package/dist/src/events/event-legacy.js +164 -0
- package/dist/src/events/event-read.js +74 -38
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +83 -27
- package/dist/src/events/events.js +47 -26
- package/dist/src/events/notify.js +85 -35
- package/dist/src/failure/failure-cluster.js +23 -20
- package/dist/src/failure/failure-data.js +140 -92
- package/dist/src/failure/failure-render.js +97 -78
- package/dist/src/failure/failure-state-change.js +45 -33
- package/dist/src/failure/time-spend.js +166 -82
- package/dist/src/files/file-queue.js +16 -4
- package/dist/src/files/files.js +129 -41
- package/dist/src/files/json-files.js +3 -3
- package/dist/src/files/json-object.js +45 -9
- package/dist/src/files/stat-cache.js +2 -2
- package/dist/src/files/tail.js +2 -2
- package/dist/src/gates/bootstrap-gates.js +8 -8
- package/dist/src/gates/budget-gates.js +361 -52
- package/dist/src/gates/disk-gate.js +18 -18
- package/dist/src/gates/gate-polls.js +201 -103
- package/dist/src/gates/maintenance-quota.js +34 -37
- package/dist/src/gates/pause-gates.js +33 -15
- package/dist/src/gates/readiness.js +8 -3
- package/dist/src/gates/role-cap-gates.js +23 -28
- package/dist/src/gates/startup-gate.js +154 -48
- package/dist/src/gates/streak-gate.js +67 -41
- package/dist/src/git/commit-message.js +80 -48
- package/dist/src/git/git-diff.js +29 -23
- package/dist/src/git/git-run.js +87 -46
- package/dist/src/git/git.js +114 -92
- package/dist/src/git/slot-salvage.js +253 -0
- package/dist/src/git/slots-state.js +18 -9
- package/dist/src/git/worktree-pool.js +150 -49
- package/dist/src/git/worktree-use.js +14 -20
- package/dist/src/git/worktree.js +108 -51
- package/dist/src/gui/gui-admin-endpoints.js +96 -0
- package/dist/src/gui/gui-args.js +88 -21
- package/dist/src/gui/gui-backlog-commands.js +297 -0
- package/dist/src/gui/gui-backlog-endpoints.js +155 -0
- package/dist/src/gui/gui-command.js +9 -14
- package/dist/src/gui/gui-config-commands.js +462 -0
- package/dist/src/gui/gui-endpoint-commands.js +17 -716
- package/dist/src/gui/gui-endpoint-common.js +49 -0
- package/dist/src/gui/gui-endpoints.js +13 -331
- package/dist/src/gui/gui-history-endpoints.js +301 -0
- package/dist/src/gui/gui-operator-commands.js +240 -0
- package/dist/src/gui/gui-prompt-commands.js +231 -0
- package/dist/src/gui/gui-report-endpoints.js +90 -0
- package/dist/src/gui/gui-server.js +33 -9
- package/dist/src/gui/gui-team-commands.js +164 -0
- package/dist/src/gui/http-body.js +17 -1
- package/dist/src/history/history-data.js +166 -203
- package/dist/src/history/history-queued.js +147 -0
- package/dist/src/history/history-render.js +96 -0
- package/dist/src/history/history.js +206 -103
- package/dist/src/inbox/inbox-attachments.js +94 -39
- package/dist/src/inbox/inbox-cancel.js +16 -15
- package/dist/src/inbox/inbox-edit.js +10 -10
- package/dist/src/inbox/inbox-submit.js +54 -14
- package/dist/src/inbox/inbox-top.js +99 -0
- package/dist/src/inbox/inbox.js +49 -44
- package/dist/src/inbox/pending-prompt.js +27 -27
- package/dist/src/inbox/prompt-commands.js +154 -100
- package/dist/src/inbox/prompt-not-before.js +2 -2
- package/dist/src/init/init-templates.js +15 -14
- package/dist/src/init/init.js +35 -29
- package/dist/src/merge/backlog-conflicts.js +335 -0
- package/dist/src/{landing/landing-batch.js → merge/merge-batch.js} +100 -93
- package/dist/src/merge/merge-cancel.js +53 -0
- package/dist/src/merge/merge-check-failures.js +209 -0
- package/dist/src/{landing/landing-core.js → merge/merge-core.js} +183 -110
- package/dist/src/{landing/landing-diff.js → merge/merge-diff.js} +6 -5
- package/dist/src/merge/merge-drain.js +351 -0
- package/dist/src/merge/merge-git.js +403 -0
- package/dist/src/merge/merge-pipeline.js +111 -0
- package/dist/src/merge/merge-post-review.js +90 -0
- package/dist/src/{landing/landing-questions.js → merge/merge-questions.js} +9 -6
- package/dist/src/merge/merge-queue-render.js +113 -0
- package/dist/src/merge/merge-queue.js +224 -0
- package/dist/src/merge/merge-requeue.js +102 -0
- package/dist/src/merge/merge-slot.js +385 -0
- package/dist/src/{landing/landing-stack.js → merge/merge-stack.js} +97 -71
- package/dist/src/{landing/landing-merge.js → merge/merge-to-main.js} +151 -82
- package/dist/src/merge/merge-top.js +69 -0
- package/dist/src/merge/merge-verify.js +214 -0
- package/dist/src/notes-cli.js +112 -0
- package/dist/src/operator/operator-commands.js +268 -89
- package/dist/src/operator/operator-intent.js +130 -82
- package/dist/src/operator/operator-requests.js +82 -87
- package/dist/src/operator/retire.js +49 -31
- package/dist/src/operator/revert.js +179 -0
- package/dist/src/orchestrator/orchestrator-launch.js +116 -48
- package/dist/src/orchestrator/orchestrator-poll.js +320 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +78 -348
- package/dist/src/orchestrator/orchestrator.js +173 -361
- package/dist/src/orchestrator/retention.js +66 -15
- package/dist/src/orchestrator/scheduling-admit.js +116 -0
- package/dist/src/orchestrator/scheduling-scope.js +201 -0
- package/dist/src/orchestrator/scheduling-skip.js +185 -0
- package/dist/src/orchestrator/startup-migration-steps.js +52 -0
- package/dist/src/orchestrator/startup-migrations.js +44 -0
- package/dist/src/paths.js +228 -75
- package/dist/src/pi/command-shape.js +12 -1
- package/dist/src/pi/pi-args.js +30 -5
- package/dist/src/pi/pi-backend.js +19 -27
- package/dist/src/pi/pi-bin.js +64 -67
- package/dist/src/pi/pi-event-line.js +17 -1
- package/dist/src/pi/pi-failure.js +87 -0
- package/dist/src/pi/pi-listed-models.js +53 -0
- package/dist/src/pi/pi-models.js +163 -42
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +86 -0
- package/dist/src/pi/pi-stream.js +137 -113
- package/dist/src/pi/pi-watchdogs.js +20 -20
- package/dist/src/pi-extension/bounded-output.js +25 -8
- package/dist/src/pi-extension/context-budget.js +5 -3
- package/dist/src/pi-extension/context-shake.js +54 -21
- package/dist/src/pi-extension/context-window.js +36 -0
- package/dist/src/pi-extension/full-output.js +5 -12
- package/dist/src/pi-extension/strip-thinking.js +79 -0
- package/dist/src/pi-extension/trim-system-prompt.js +37 -0
- package/dist/src/pi-extension/write-guard.js +37 -0
- package/dist/src/process/launch-services.js +11 -11
- package/dist/src/process/node-bin.js +16 -0
- package/dist/src/process/process-group.js +8 -18
- package/dist/src/process/process-table.js +1 -1
- package/dist/src/process/process.js +48 -9
- package/dist/src/process/run-marker.js +42 -7
- package/dist/src/process/supervisor.js +47 -35
- package/dist/src/prompt/dialect.js +46 -11
- package/dist/src/prompt/gate-notes.js +203 -0
- package/dist/src/prompt/gate-prompts.js +201 -188
- package/dist/src/prompt/notes-backlog.js +181 -0
- package/dist/src/prompt/principles.js +1 -1
- package/dist/src/prompt/prompt-followup.js +43 -29
- package/dist/src/prompt/prompt.js +255 -172
- package/dist/src/redeploy/redeploy-policy.js +29 -24
- package/dist/src/redeploy/redeploy-probes.js +1 -1
- package/dist/src/redeploy/redeploy.js +7 -6
- package/dist/src/redeploy/redeployer.js +78 -70
- package/dist/src/redeploy/self-reload.js +3 -2
- package/dist/src/report/report-data.js +374 -285
- package/dist/src/report/report-fold.js +430 -0
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +321 -61
- package/dist/src/report/report-types.js +1 -0
- package/dist/src/report/report.js +26 -9
- package/dist/src/request-timeouts.js +2 -2
- package/dist/src/review/known-flakes.js +133 -14
- package/dist/src/review/post-merge-review.js +106 -0
- package/dist/src/review/protected-edits.js +7 -7
- package/dist/src/review/review-followup.js +21 -9
- package/dist/src/review/review-precheck.js +77 -33
- package/dist/src/review/review-run.js +395 -0
- package/dist/src/review/review-verdict.js +16 -2
- package/dist/src/review/review.js +172 -356
- package/dist/src/review/secret-leaks.js +147 -0
- package/dist/src/review/suite-rerun.js +3 -3
- package/dist/src/roles/legacy-role-ids.js +21 -0
- package/dist/src/roles/legacy-role-migration.js +135 -0
- package/dist/src/roles/role-catalog.js +172 -137
- package/dist/src/roles/role-guidance.js +112 -44
- package/dist/src/roles/role-labels.js +9 -0
- package/dist/src/roles/role-render.js +221 -28
- package/dist/src/roles/role-view.js +244 -33
- package/dist/src/roles/roles.js +37 -35
- package/dist/src/roles/runner-ids.js +97 -0
- package/dist/src/run/acceptance-greps.js +119 -0
- package/dist/src/run/feedback-filings.js +51 -0
- package/dist/src/{tick → run}/qa-coverage.js +10 -27
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +166 -136
- package/dist/src/run/run-detail-data.js +179 -0
- package/dist/src/run/run-detail-render.js +98 -0
- package/dist/src/run/run-detail.js +168 -0
- package/dist/src/run/run-finalize.js +154 -0
- package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -6
- package/dist/src/run/run-prompt.js +304 -0
- package/dist/src/{tick/tick-resume.js → run/run-resume.js} +80 -35
- package/dist/src/run/run-stage.js +295 -0
- package/dist/src/run/run-timing.js +198 -0
- package/dist/src/run/run-usage.js +180 -0
- package/dist/src/run/run-verdict.js +301 -0
- package/dist/src/run/selfcheck-digest.js +40 -0
- package/dist/src/run/stage-backlog-checks.js +249 -0
- package/dist/src/run/stage-check.js +172 -0
- package/dist/src/run/stage-path-checks.js +730 -0
- package/dist/src/runner/leftover.js +185 -0
- package/dist/src/{loop → runner}/model-fallback.js +13 -12
- package/dist/src/runner/revision.js +202 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +10 -10
- package/dist/src/runner/runner-outcome.js +107 -0
- package/dist/src/runner/runner-pi.js +433 -0
- package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +34 -28
- package/dist/src/runner/runner-run.js +625 -0
- package/dist/src/runner/runner-state-load.js +268 -0
- package/dist/src/runner/runner-state.js +102 -0
- package/dist/src/runner/runner.js +350 -0
- package/dist/src/scheduling/backoff.js +64 -54
- package/dist/src/scheduling/claims.js +46 -26
- package/dist/src/scheduling/once-round.js +30 -30
- package/dist/src/scheduling/quiet-hours.js +26 -34
- package/dist/src/scheduling/scheduling.js +87 -74
- package/dist/src/scheduling/{work-landed-cache.js → work-merged-cache.js} +14 -14
- package/dist/src/status/status-budget.js +123 -0
- package/dist/src/status/status-data.js +106 -191
- package/dist/src/status/status-inbox.js +47 -0
- package/dist/src/status/status-install.js +26 -0
- package/dist/src/status/status-polls.js +70 -59
- package/dist/src/status/status-roles.js +131 -0
- package/dist/src/{fleet → team}/error-storm.js +16 -16
- package/dist/src/{fleet → team}/failure-spread.js +10 -10
- package/dist/src/{fleet → team}/orchestrator-info.js +97 -25
- package/dist/src/{fleet → team}/reclaim.js +9 -9
- package/dist/src/{fleet/fleet-hold.js → team/team-hold.js} +60 -26
- package/dist/src/team/team-polls.js +299 -0
- package/dist/src/{fleet/fleet-state.js → team/team-state.js} +47 -40
- package/dist/src/text/datetime.js +102 -16
- package/dist/src/text/format.js +49 -3
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +89 -54
- package/dist/src/text/suggest.js +3 -2
- package/dist/src/text/text.js +68 -14
- package/dist/src/ui/badges.js +165 -93
- package/dist/src/ui/gui/gui-client-backlog.js +292 -0
- package/dist/src/ui/gui/gui-client-boot.js +221 -42
- package/dist/src/ui/gui/gui-client-composer.js +105 -27
- package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
- package/dist/src/ui/gui/gui-client-drawer.js +226 -130
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +492 -68
- package/dist/src/ui/gui/gui-client-labels.js +23 -0
- package/dist/src/ui/gui/gui-client-markdown.js +1 -1
- package/dist/src/ui/gui/gui-client-model.js +84 -76
- package/dist/src/ui/gui/gui-client-operator.js +112 -78
- package/dist/src/ui/gui/gui-client-pending.js +248 -41
- package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
- package/dist/src/ui/gui/gui-client-report.js +122 -179
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +118 -76
- package/dist/src/ui/gui/gui-client-settings.js +180 -20
- package/dist/src/ui/gui/gui-client-team.js +252 -0
- package/dist/src/ui/gui/gui-client.js +48 -40
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +25 -24
- package/dist/src/ui/gui/gui-styles.js +46 -38
- package/dist/src/ui/progress-data.js +11 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +40 -40
- package/dist/src/ui/status-model.js +276 -194
- package/dist/src/ui/status-payload.js +227 -62
- package/dist/src/ui/status-render.js +196 -108
- package/dist/src/ui/team-alerts.js +532 -0
- package/dist/src/ui/tone.js +11 -11
- package/dist/src/ui/transcript-run.js +58 -0
- package/dist/src/ui/transcript-tail.js +28 -19
- package/dist/src/ui/transcript.js +85 -38
- package/dist/src/ui/tui/tui-backlog.js +132 -21
- package/dist/src/ui/tui/tui-editor-answer.js +93 -0
- package/dist/src/ui/tui/tui-editor-attach.js +105 -0
- package/dist/src/ui/tui/tui-editor-backends.js +132 -0
- package/dist/src/ui/tui/tui-editor-brief.js +105 -0
- package/dist/src/ui/tui/tui-editor-budget.js +159 -0
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +24 -0
- package/dist/src/ui/tui/tui-editor-concurrency.js +24 -0
- package/dist/src/ui/tui/tui-editor-config.js +86 -0
- package/dist/src/ui/tui/tui-editor-ctx.js +1 -0
- package/dist/src/ui/tui/tui-editor-entry.js +219 -0
- package/dist/src/ui/tui/tui-editor-fallback.js +31 -0
- package/dist/src/ui/tui/tui-editor-instances.js +37 -0
- package/dist/src/ui/tui/tui-editor-instructions.js +136 -0
- package/dist/src/ui/tui/tui-editor-interval.js +124 -0
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +112 -0
- package/dist/src/ui/tui/tui-editor-notify.js +25 -0
- package/dist/src/ui/tui/tui-editor-pause.js +255 -0
- package/dist/src/ui/tui/tui-editor-prompt.js +90 -0
- package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
- package/dist/src/ui/tui/tui-editor-queued.js +94 -0
- package/dist/src/ui/tui/tui-editor-quiet.js +170 -0
- package/dist/src/ui/tui/tui-editor-retire.js +81 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +76 -0
- package/dist/src/ui/tui/tui-editor-role-model.js +28 -0
- package/dist/src/ui/tui/tui-editor-search.js +85 -0
- package/dist/src/ui/tui/tui-editor-session.js +123 -0
- package/dist/src/ui/tui/tui-editor-slots.js +23 -0
- package/dist/src/ui/tui/tui-editors.js +185 -0
- package/dist/src/ui/tui/tui-frame.js +107 -58
- package/dist/src/ui/tui/tui-history.js +16 -12
- package/dist/src/ui/tui/tui-input.js +56 -60
- package/dist/src/ui/tui/tui-keys.js +106 -1825
- package/dist/src/ui/tui/tui-pane-activity.js +102 -0
- package/dist/src/ui/tui/tui-pane-backlog.js +269 -0
- package/dist/src/ui/tui/tui-pane-brief.js +29 -0
- package/dist/src/ui/tui/tui-pane-ctx.js +11 -0
- package/dist/src/ui/tui/tui-pane-health.js +174 -0
- package/dist/src/ui/tui/tui-pane-history.js +636 -0
- package/dist/src/ui/tui/tui-pane-pending.js +369 -0
- package/dist/src/ui/tui/tui-pane-queued.js +159 -0
- package/dist/src/ui/tui/tui-pane-report.js +154 -0
- package/dist/src/ui/tui/tui-pane-session.js +120 -0
- package/dist/src/ui/tui/tui-pane-transcript.js +224 -0
- package/dist/src/ui/tui/tui-pane.js +125 -44
- package/dist/src/ui/tui/tui-panes.js +364 -0
- package/dist/src/ui/tui/tui.js +52 -34
- package/dist/src/verdict/fix-claim.js +135 -62
- package/dist/src/verdict/no-change.js +19 -0
- package/dist/src/verdict/refusal.js +80 -24
- package/dist/src/verdict/reply-contract.js +51 -28
- package/dist/src/version.js +2 -2
- package/package.json +5 -2
- package/dist/src/agent/mcp/role-notes-server.js +0 -160
- package/dist/src/brief-edit.js +0 -42
- package/dist/src/cli/cli-run.js +0 -469
- package/dist/src/fleet/fleet-polls.js +0 -207
- package/dist/src/landing/backlog-conflicts.js +0 -116
- package/dist/src/landing/landing-check-failures.js +0 -141
- package/dist/src/landing/landing-drain.js +0 -204
- package/dist/src/landing/landing-git.js +0 -190
- package/dist/src/landing/landing-pipeline.js +0 -96
- package/dist/src/landing/landing-queue-render.js +0 -56
- package/dist/src/landing/landing-queue.js +0 -149
- package/dist/src/landing/landing-slot.js +0 -256
- package/dist/src/landing/landing-vetting.js +0 -196
- package/dist/src/loop/leftover.js +0 -129
- package/dist/src/loop/loop-pi.js +0 -370
- package/dist/src/loop/loop-state.js +0 -204
- package/dist/src/loop/loop-tick.js +0 -545
- package/dist/src/loop/loop.js +0 -315
- package/dist/src/loop/revision.js +0 -61
- package/dist/src/pi-extension/role-notes.js +0 -40
- package/dist/src/roles/loop-ids.js +0 -60
- package/dist/src/tick/stage-check.js +0 -302
- package/dist/src/tick/telemetry-digest.js +0 -24
- package/dist/src/tick/tick-detail-data.js +0 -107
- package/dist/src/tick/tick-detail.js +0 -150
- package/dist/src/tick/tick-finalize.js +0 -135
- package/dist/src/tick/tick-prompt.js +0 -201
- package/dist/src/tick/tick-stage.js +0 -223
- package/dist/src/tick/tick-timing.js +0 -195
- package/dist/src/tick/tick-usage.js +0 -142
- package/dist/src/tick/tick-verdict.js +0 -201
- package/dist/src/ui/fleet-alerts.js +0 -174
- package/dist/src/ui/gui/gui-client-fleet.js +0 -576
- package/dist/src/ui/gui/gui-client-sound.js +0 -52
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/** How tumwater learns the write guard's trust from codex itself (plans/codex-write-guard.md
|
|
2
|
+
* "Trust (parts 4–5)", findings 2–3): codex runs a session-flag PreToolUse hook only when its
|
|
3
|
+
* `trusted_hash` matches the hash codex computed for the hook's definition, and that hash is
|
|
4
|
+
* opaque — no simple digest of any command or JSON spelling matched (finding 3). So this module
|
|
5
|
+
* asks codex's own `hooks/list` RPC for the hook's `key` and `currentHash`, and caches the
|
|
6
|
+
* answer per build and codex version so a run's argv never spawns `app-server` again. The
|
|
7
|
+
* answer is never hardcoded: the key (`/<session-flags>/config.toml:pre_tool_use:0:0` on
|
|
8
|
+
* 0.163.0) and the hash both come from this lookup.
|
|
9
|
+
*
|
|
10
|
+
* The lookup spawns `app-server` in its own process group under the run's env and a bounded
|
|
11
|
+
* deadline, kills the group whether it succeeds, times out, or fails, and makes no model
|
|
12
|
+
* request and needs no login (an empty `CODEX_HOME` is enough — finding 3). Part 5/9 wires it
|
|
13
|
+
* into a run before the codex adapter's synchronous invocation. */
|
|
14
|
+
import { spawn } from "node:child_process";
|
|
15
|
+
import { codexHookArg } from "./codex-write-guard.js";
|
|
16
|
+
import { readJsonFile, writeJsonAtomic } from "../../files/json-files.js";
|
|
17
|
+
import { releaseChildHandles, signalTree } from "../../process/process.js";
|
|
18
|
+
import { codexHookTrustPath } from "../../paths.js";
|
|
19
|
+
import { PACKAGE_JSON, packageVersion } from "../../version.js";
|
|
20
|
+
/** The JSON-RPC request/notification sequence one lookup sends over `app-server`'s stdio: the
|
|
21
|
+
* `initialize` handshake (carrying tumwater's own version), the `initialized` notification,
|
|
22
|
+
* then `hooks/list` for the one cwd. The reply to `id: 2` carries the hooks. */
|
|
23
|
+
function hookListMessages(cwd) {
|
|
24
|
+
const version = packageVersion(PACKAGE_JSON).version;
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
jsonrpc: "2.0",
|
|
28
|
+
id: 1,
|
|
29
|
+
method: "initialize",
|
|
30
|
+
params: { clientInfo: { name: "tumwater", version: version ?? "unknown" } },
|
|
31
|
+
},
|
|
32
|
+
{ jsonrpc: "2.0", method: "initialized" },
|
|
33
|
+
{ jsonrpc: "2.0", id: 2, method: "hooks/list", params: { cwds: [cwd] } },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
/** The first hook in `hooks/list`'s `result.data[*].hooks` with `source: "sessionFlags"`,
|
|
37
|
+
* `eventName: "preToolUse"` and `command` exactly the guard definition's (finding 3), or null
|
|
38
|
+
* when none matches. The key and hash are returned as codex reports them. */
|
|
39
|
+
function findTrust(result, definition) {
|
|
40
|
+
if (typeof result !== "object" || result === null)
|
|
41
|
+
return null;
|
|
42
|
+
const data = result.data;
|
|
43
|
+
if (!Array.isArray(data))
|
|
44
|
+
return null;
|
|
45
|
+
for (const group of data) {
|
|
46
|
+
if (typeof group !== "object" || group === null)
|
|
47
|
+
continue;
|
|
48
|
+
const hooks = group.hooks;
|
|
49
|
+
if (!Array.isArray(hooks))
|
|
50
|
+
continue;
|
|
51
|
+
for (const hook of hooks) {
|
|
52
|
+
if (typeof hook !== "object" || hook === null)
|
|
53
|
+
continue;
|
|
54
|
+
const h = hook;
|
|
55
|
+
if (h.source !== "sessionFlags" || h.eventName !== "preToolUse")
|
|
56
|
+
continue;
|
|
57
|
+
if (h.command !== definition.command)
|
|
58
|
+
continue;
|
|
59
|
+
if (typeof h.key === "string" && typeof h.currentHash === "string") {
|
|
60
|
+
return { key: h.key, hash: h.currentHash };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/** A short name for a JSON-RPC `error` value, for the rejection message. */
|
|
67
|
+
function describeError(error) {
|
|
68
|
+
if (typeof error === "string")
|
|
69
|
+
return error;
|
|
70
|
+
if (typeof error === "object" && error !== null) {
|
|
71
|
+
const message = error.message;
|
|
72
|
+
if (typeof message === "string")
|
|
73
|
+
return message;
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify(error);
|
|
76
|
+
}
|
|
77
|
+
/** Ask codex for the guard hook's trust. Spawns `<bin> -c <codexHookArg(definition)>
|
|
78
|
+
* app-server` in `cwd` under `env`, in its own process group, sends the JSON-RPC sequence
|
|
79
|
+
* above over stdio, and resolves with the trust from the first matching `hooks/list` hook.
|
|
80
|
+
*
|
|
81
|
+
* It rejects on a timeout (`timeoutMs`), on the child exiting before the reply, on a JSON-RPC
|
|
82
|
+
* `error`, and on codex listing no session-flag PreToolUse hook with this command. The child's
|
|
83
|
+
* process group is SIGKILLed whether the lookup settles by success, failure, or timeout, so no
|
|
84
|
+
* `app-server` outlives the call. */
|
|
85
|
+
export function lookupCodexHookTrust(opts) {
|
|
86
|
+
const timeoutMs = opts.timeoutMs ?? 15_000;
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const child = spawn(opts.bin, ["-c", codexHookArg(opts.definition), "app-server"], {
|
|
89
|
+
cwd: opts.cwd,
|
|
90
|
+
env: opts.env,
|
|
91
|
+
detached: true,
|
|
92
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
93
|
+
});
|
|
94
|
+
let settled = false;
|
|
95
|
+
let buffer = "";
|
|
96
|
+
const finish = (err, trust) => {
|
|
97
|
+
if (settled)
|
|
98
|
+
return;
|
|
99
|
+
settled = true;
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
signalTree(child, "SIGKILL");
|
|
102
|
+
releaseChildHandles(child);
|
|
103
|
+
if (err !== null)
|
|
104
|
+
reject(err);
|
|
105
|
+
else
|
|
106
|
+
resolve(trust);
|
|
107
|
+
};
|
|
108
|
+
const timer = setTimeout(() => {
|
|
109
|
+
finish(new Error(`codex hook trust lookup timed out after ${timeoutMs} ms`));
|
|
110
|
+
}, timeoutMs);
|
|
111
|
+
timer.unref?.();
|
|
112
|
+
const onLine = (line) => {
|
|
113
|
+
let msg;
|
|
114
|
+
try {
|
|
115
|
+
msg = JSON.parse(line);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return; // A non-JSON-RPC line (a notice, a prompt) is not our reply.
|
|
119
|
+
}
|
|
120
|
+
if (typeof msg !== "object" || msg === null)
|
|
121
|
+
return;
|
|
122
|
+
const m = msg;
|
|
123
|
+
if (m.error !== undefined) {
|
|
124
|
+
finish(new Error(`codex hooks/list errored: ${describeError(m.error)}`));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (m.id !== 2)
|
|
128
|
+
return;
|
|
129
|
+
const trust = findTrust(m.result, opts.definition);
|
|
130
|
+
if (trust !== null)
|
|
131
|
+
finish(null, trust);
|
|
132
|
+
else
|
|
133
|
+
finish(new Error("codex listed no session-flag PreToolUse hook with this command"));
|
|
134
|
+
};
|
|
135
|
+
child.stdout?.setEncoding("utf8");
|
|
136
|
+
child.stdout?.on("data", (chunk) => {
|
|
137
|
+
buffer += chunk;
|
|
138
|
+
let nl = buffer.indexOf("\n");
|
|
139
|
+
while (nl >= 0) {
|
|
140
|
+
const line = buffer.slice(0, nl);
|
|
141
|
+
buffer = buffer.slice(nl + 1);
|
|
142
|
+
if (line.trim() !== "")
|
|
143
|
+
onLine(line);
|
|
144
|
+
nl = buffer.indexOf("\n");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
child.on("error", (err) => finish(err instanceof Error ? err : new Error(String(err))));
|
|
148
|
+
child.on("close", (code) => {
|
|
149
|
+
if (!settled) {
|
|
150
|
+
finish(new Error(`codex app-server exited (code ${code ?? "signal"}) before answering hooks/list`));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
for (const message of hookListMessages(opts.cwd)) {
|
|
154
|
+
try {
|
|
155
|
+
child.stdin?.write(JSON.stringify(message) + "\n");
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// The pipe is gone (a failed spawn); the error handler settles the lookup.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** The trimmed output of `<bin> --version`, or "" when the probe fails or prints nothing. It
|
|
164
|
+
* keys the trust cache, so a codex upgrade re-looks-up the guard's trust for the new build.
|
|
165
|
+
* Async (spawn, not spawnSync): a hung binary's probe must not block the event loop — every
|
|
166
|
+
* run's `prepare` awaits this, and a blocking probe would freeze the whole agent while waiting
|
|
167
|
+
* out its deadline. The child's group is killed whether it succeeds or times out. */
|
|
168
|
+
function codexVersion(bin, env, cwd, timeoutMs) {
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
const child = spawn(bin, ["--version"], {
|
|
171
|
+
env,
|
|
172
|
+
cwd,
|
|
173
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
174
|
+
});
|
|
175
|
+
let out = "";
|
|
176
|
+
let done = false;
|
|
177
|
+
const finish = (value) => {
|
|
178
|
+
if (done)
|
|
179
|
+
return;
|
|
180
|
+
done = true;
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
signalTree(child, "SIGKILL");
|
|
183
|
+
releaseChildHandles(child);
|
|
184
|
+
resolve(value);
|
|
185
|
+
};
|
|
186
|
+
const timer = setTimeout(() => finish(""), timeoutMs);
|
|
187
|
+
timer.unref?.();
|
|
188
|
+
child.stdout?.setEncoding("utf8");
|
|
189
|
+
child.stdout?.on("data", (chunk) => {
|
|
190
|
+
out += chunk;
|
|
191
|
+
});
|
|
192
|
+
child.on("error", () => finish(""));
|
|
193
|
+
child.on("close", () => finish(out.trim()));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Whether a cached value is a usable `{key, hash}` entry. */
|
|
197
|
+
function isTrust(value) {
|
|
198
|
+
return (typeof value === "object" &&
|
|
199
|
+
value !== null &&
|
|
200
|
+
typeof value.key === "string" &&
|
|
201
|
+
typeof value.hash === "string");
|
|
202
|
+
}
|
|
203
|
+
/** Resolve the guard hook's trust, cached per codex version and hook definition in
|
|
204
|
+
* `.tumwater/state/codex-hook-trust.json` so a run's argv never spawns `app-server` again.
|
|
205
|
+
* Returns the trust, rejecting on a failed lookup.
|
|
206
|
+
*
|
|
207
|
+
* The cache key is the trimmed `<bin> --version` output plus `JSON.stringify(definition)`, so
|
|
208
|
+
* a changed codex version, a changed hook command, or a changed `timeout` (which changes the
|
|
209
|
+
* hash, finding 3) each re-look-up; each entry is `{key, hash, checkedAt}`. A hit resolves
|
|
210
|
+
* without spawning `app-server`; a miss looks up and writes the entry atomically.
|
|
211
|
+
*
|
|
212
|
+
* `root` undefined (a hand run with no `.tumwater` root) looks up and caches nothing and never
|
|
213
|
+
* creates a `.tumwater` directory — a hand run once minted a phantom `$TMPDIR/.tumwater` and
|
|
214
|
+
* reddened every suite on the host (plans/codex-write-guard.md "Visibility"). */
|
|
215
|
+
export async function codexHookTrust(root, opts) {
|
|
216
|
+
const timeoutMs = opts.timeoutMs ?? 15_000;
|
|
217
|
+
if (root === undefined)
|
|
218
|
+
return lookupCodexHookTrust(opts);
|
|
219
|
+
const file = codexHookTrustPath(root);
|
|
220
|
+
// The version probe and the app-server lookup share one deadline (timeoutMs): a failed
|
|
221
|
+
// lookup — a hung binary, an app-server that never answers — never delays a run past the
|
|
222
|
+
// bound, and the probe never blocks the event loop (it is async, not spawnSync). The probe
|
|
223
|
+
// is a quick identity read, so its own slice is capped at a third of the deadline — a slow
|
|
224
|
+
// probe never starves the app-server lookup of its budget, and the sum of the two always
|
|
225
|
+
// stays within the one deadline.
|
|
226
|
+
const deadline = Date.now() + timeoutMs;
|
|
227
|
+
const version = await codexVersion(opts.bin, opts.env, opts.cwd, Math.floor(timeoutMs / 3));
|
|
228
|
+
const cacheKey = `${version}${JSON.stringify(opts.definition)}`;
|
|
229
|
+
const cache = readJsonFile(file);
|
|
230
|
+
const hit = cache?.[cacheKey];
|
|
231
|
+
if (isTrust(hit))
|
|
232
|
+
return { key: hit.key, hash: hit.hash };
|
|
233
|
+
const trust = await lookupCodexHookTrust({
|
|
234
|
+
...opts,
|
|
235
|
+
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
236
|
+
});
|
|
237
|
+
const next = { ...(cache ?? {}) };
|
|
238
|
+
next[cacheKey] = { key: trust.key, hash: trust.hash, checkedAt: Date.now() };
|
|
239
|
+
try {
|
|
240
|
+
writeJsonAtomic(file, next);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// A cache write failure must never fail the lookup whose result it is only persisting.
|
|
244
|
+
}
|
|
245
|
+
return trust;
|
|
246
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** List the paths an `apply_patch` text writes, from the patch text alone (plans/
|
|
2
|
+
* codex-write-guard.md "The hook (parts 1–2)"; PLANS.md "Write guard on Codex, part 1/9").
|
|
3
|
+
* Pure: no filesystem, no imports beyond node built-ins, so a later part can call it from the
|
|
4
|
+
* hook without dragging in a registry or subprocess.
|
|
5
|
+
*
|
|
6
|
+
* Codex's PreToolUse payload carries the patch text, not a path, and a patch is applied even
|
|
7
|
+
* when it is truncated or its `*** Begin Patch` / `*** End Patch` markers are missing — so this
|
|
8
|
+
* scans every line, marker or not, and trusts only lines that begin at column 0 with a header.
|
|
9
|
+
* A hunk line (`+`, `-`, a space, `@@`) never counts as a header even when its text reads like
|
|
10
|
+
* one, because a hunk prefix shifts `*** ` off column 0. Paths are kept as written; the caller
|
|
11
|
+
* resolves relative ones against the run's directory. */
|
|
12
|
+
/** Headers that name a path the patch writes, in the order a line may spell them. */
|
|
13
|
+
const HEADERS = [
|
|
14
|
+
"*** Add File: ",
|
|
15
|
+
"*** Delete File: ",
|
|
16
|
+
"*** Update File: ",
|
|
17
|
+
"*** Move to: ",
|
|
18
|
+
];
|
|
19
|
+
/** The paths an `apply_patch` text writes, in first-appearance order, without duplicates. */
|
|
20
|
+
export function patchWritePaths(patch) {
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const paths = [];
|
|
23
|
+
for (const line of patch.split("\n")) {
|
|
24
|
+
for (const header of HEADERS) {
|
|
25
|
+
if (line.startsWith(header)) {
|
|
26
|
+
const path = line.slice(header.length).replace(/[\s\r]+$/, "");
|
|
27
|
+
if (path !== "" && !seen.has(path)) {
|
|
28
|
+
seen.add(path);
|
|
29
|
+
paths.push(path);
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return paths;
|
|
36
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/** The codex adapter's rollout reader (plans/codex-backend.md "The rollout read that
|
|
2
|
+
* completes it"). `codex exec --json`'s stream reports usage only once, at the end, counting
|
|
3
|
+
* the WHOLE thread (finding 4) — a resume would otherwise charge its earlier run to the follow-
|
|
4
|
+
* up. The authoritative per-run figures live in `$CODEX_HOME/sessions/…/rollout-*-<id>.jsonl`,
|
|
5
|
+
* Codex's own JSONL record of a session, where each run's `task_started` .. `task_complete`
|
|
6
|
+
* block carries its own `token_usage_record`, `token_count` (the rate limits) and
|
|
7
|
+
* `turn_context` (the model that actually ran). This module reads just that block: what the
|
|
8
|
+
* stream lacks — this run's own usage, the billing and the two usage windows.
|
|
9
|
+
*
|
|
10
|
+
* Pure over text (plus one filesystem walk for `findRollout`): no spawn, no billing math, no
|
|
11
|
+
* session state. Part 7/9's adapter wires the lines into the canonical stream in `afterRun`.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { isJsonObject, nonNegativeNumber, objectOrEmpty, parseJsonObject } from "../../files/json-object.js";
|
|
16
|
+
import { windowTypeName } from "./codex-stream.js";
|
|
17
|
+
function str(value) {
|
|
18
|
+
return typeof value === "string" ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
function finite(value) {
|
|
21
|
+
return nonNegativeNumber(value, undefined);
|
|
22
|
+
}
|
|
23
|
+
/** The rate-limit status of one window: `rejected` when the account hit it outright,
|
|
24
|
+
* `allowed_warning` when it is at or past 90% full, else `allowed` (design §5, decision 7). */
|
|
25
|
+
function windowStatus(windowType, rateLimitReachedType, usedPercent) {
|
|
26
|
+
if (rateLimitReachedType === windowType)
|
|
27
|
+
return "rejected";
|
|
28
|
+
if (usedPercent !== undefined && usedPercent >= 100)
|
|
29
|
+
return "rejected";
|
|
30
|
+
if (usedPercent !== undefined && usedPercent >= 90)
|
|
31
|
+
return "allowed_warning";
|
|
32
|
+
return "allowed";
|
|
33
|
+
}
|
|
34
|
+
/** Read the records of ONE turn out of a rollout's text. A rollout is one file per thread that
|
|
35
|
+
* appends a new `task_started` block per run, so the adapter's `afterRun` read (which knows
|
|
36
|
+
* only the thread id that names the file, never a turn id) reads the LAST block — everything
|
|
37
|
+
* after the last `task_started`. `sinceTurnStart` (optional) selects a specific turn's block
|
|
38
|
+
* instead, which pins per-turn isolation: the thread total hides a resume's own figures
|
|
39
|
+
* (finding 4). `requestedModel` is the `-m` tumwater passed; a turn whose `turn_context.model`
|
|
40
|
+
* differs reports the switch. */
|
|
41
|
+
export function rolloutLines(text, opts) {
|
|
42
|
+
const { sinceTurnStart, requestedModel } = opts;
|
|
43
|
+
let currentTurn;
|
|
44
|
+
let sawTurn = false;
|
|
45
|
+
let lastUsage;
|
|
46
|
+
let lastRateLimits;
|
|
47
|
+
let model;
|
|
48
|
+
let planType;
|
|
49
|
+
for (const line of text.split("\n")) {
|
|
50
|
+
if (!line.trim())
|
|
51
|
+
continue;
|
|
52
|
+
const parsed = parseJsonObject(line);
|
|
53
|
+
if (!parsed)
|
|
54
|
+
continue; // a torn or foreign line belongs to no turn
|
|
55
|
+
const payload = objectOrEmpty(parsed.payload);
|
|
56
|
+
// `task_started`, `task_complete` and `token_count` come wrapped in an `event_msg` whose
|
|
57
|
+
// `payload.type` discriminates; `turn_context` and `token_usage_record` name themselves in
|
|
58
|
+
// the outer `type`. Read either.
|
|
59
|
+
const ptype = str(parsed.type) === "event_msg" ? str(payload.type) : str(parsed.type);
|
|
60
|
+
if (ptype === "task_started") {
|
|
61
|
+
currentTurn = str(payload.turn_id);
|
|
62
|
+
// Each new task_started begins a fresh turn (a resume appends a second run to the same
|
|
63
|
+
// file), so the collectors restart at the block we are reading. With no turn id (the
|
|
64
|
+
// adapter's read) every task_started restarts them and the last block wins; with one,
|
|
65
|
+
// only the requested turn's does.
|
|
66
|
+
if (sinceTurnStart === undefined || currentTurn === sinceTurnStart) {
|
|
67
|
+
sawTurn = true;
|
|
68
|
+
lastUsage = undefined;
|
|
69
|
+
lastRateLimits = undefined;
|
|
70
|
+
model = undefined;
|
|
71
|
+
planType = undefined;
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (sinceTurnStart !== undefined && currentTurn !== sinceTurnStart)
|
|
76
|
+
continue;
|
|
77
|
+
switch (ptype) {
|
|
78
|
+
case "turn_context":
|
|
79
|
+
if (model === undefined)
|
|
80
|
+
model = str(payload.model);
|
|
81
|
+
break;
|
|
82
|
+
case "token_usage_record": {
|
|
83
|
+
const turnUsage = objectOrEmpty(payload.turn_token_usage);
|
|
84
|
+
if (isJsonObject(turnUsage) && (turnUsage.input_tokens !== undefined || turnUsage.output_tokens !== undefined)) {
|
|
85
|
+
lastUsage = turnUsage;
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "token_count": {
|
|
90
|
+
const rateLimits = objectOrEmpty(payload.rate_limits);
|
|
91
|
+
lastRateLimits = rateLimits;
|
|
92
|
+
const plan = str(rateLimits.plan_type);
|
|
93
|
+
if (plan !== undefined)
|
|
94
|
+
planType = plan;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// No matching task_started, or the turn's usage record is missing or malformed (a truncated
|
|
102
|
+
// or torn file): there is no reading. (currentTurn will have moved on to a later run's
|
|
103
|
+
// task_started in a resume-shared file; sawTurn — not currentTurn — is the real signal.)
|
|
104
|
+
if (!sawTurn || !isJsonObject(lastUsage)) {
|
|
105
|
+
return { lines: [] };
|
|
106
|
+
}
|
|
107
|
+
const lines = [];
|
|
108
|
+
const input = finite(lastUsage.input_tokens) ?? 0;
|
|
109
|
+
const cacheRead = finite(lastUsage.cached_input_tokens) ?? 0;
|
|
110
|
+
lines.push(JSON.stringify({
|
|
111
|
+
type: "run_usage",
|
|
112
|
+
input: Math.max(0, input - cacheRead),
|
|
113
|
+
output: finite(lastUsage.output_tokens) ?? 0,
|
|
114
|
+
cacheRead,
|
|
115
|
+
cacheWrite: finite(lastUsage.cache_write_input_tokens) ?? 0,
|
|
116
|
+
}));
|
|
117
|
+
if (planType !== undefined) {
|
|
118
|
+
// A reading with a plan_type bills on the subscription (finding 5; API-key login is
|
|
119
|
+
// refused, decision 5).
|
|
120
|
+
lines.push(JSON.stringify({ type: "billing", billing: "subscription" }));
|
|
121
|
+
}
|
|
122
|
+
// Two windows when present: the primary (5 h) and the secondary (weekly). A killed run's
|
|
123
|
+
// rollout has a token_usage_record but no token_count, so it yields run_usage with no
|
|
124
|
+
// rate_limit (finding 23).
|
|
125
|
+
if (lastRateLimits !== undefined) {
|
|
126
|
+
const reached = str(lastRateLimits.rate_limit_reached_type);
|
|
127
|
+
for (const key of ["primary", "secondary"]) {
|
|
128
|
+
const windowRaw = lastRateLimits[key];
|
|
129
|
+
if (!isJsonObject(windowRaw))
|
|
130
|
+
continue;
|
|
131
|
+
const minutes = finite(windowRaw.window_minutes);
|
|
132
|
+
if (minutes === undefined)
|
|
133
|
+
continue;
|
|
134
|
+
const typeName = windowTypeName(minutes);
|
|
135
|
+
const usedPercent = finite(windowRaw.used_percent);
|
|
136
|
+
lines.push(JSON.stringify({
|
|
137
|
+
type: "rate_limit",
|
|
138
|
+
rateLimitType: typeName,
|
|
139
|
+
status: windowStatus(typeName, reached, usedPercent),
|
|
140
|
+
...(usedPercent !== undefined ? { utilization: usedPercent / 100 } : {}),
|
|
141
|
+
...(finite(windowRaw.resets_at) !== undefined ? { resetsAt: finite(windowRaw.resets_at) } : {}),
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const switchedTo = model !== undefined && requestedModel !== undefined && model !== requestedModel ? model : undefined;
|
|
146
|
+
return { lines, ...(switchedTo !== undefined ? { switchedTo } : {}) };
|
|
147
|
+
}
|
|
148
|
+
/** Locate the rollout file of `$CODEX_HOME` that names the session `id`: the first
|
|
149
|
+
* `sessions/…/rollout-*-<id>.jsonl` under `home`, or null. It takes no start date and does no
|
|
150
|
+
* date math — the id match alone decides (finding 5: Codex's rollout name carries local time,
|
|
151
|
+
* so a UTC-derived bound can miss the file; the sessions tree is one directory per active day,
|
|
152
|
+
* so the walk stays small). */
|
|
153
|
+
export function findRollout(home, id) {
|
|
154
|
+
const sessionsRoot = path.join(home, "sessions");
|
|
155
|
+
if (!fs.existsSync(sessionsRoot))
|
|
156
|
+
return null;
|
|
157
|
+
const suffix = `-${id}.jsonl`;
|
|
158
|
+
const stack = [sessionsRoot];
|
|
159
|
+
while (stack.length > 0) {
|
|
160
|
+
const dir = stack.pop();
|
|
161
|
+
let entries;
|
|
162
|
+
try {
|
|
163
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
continue; // an unreadable dir is simply no match, never a failure
|
|
167
|
+
}
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const full = path.join(dir, entry.name);
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
stack.push(full);
|
|
172
|
+
}
|
|
173
|
+
else if (entry.isFile() && entry.name.startsWith("rollout-") && entry.name.endsWith(suffix)) {
|
|
174
|
+
return full;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|