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.
- package/README.md +27 -13
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-args.js +8 -9
- package/dist/src/agent/agent-backend.js +8 -6
- package/dist/src/agent/backend-traits.js +42 -25
- package/dist/src/agent/claude/claude-account.js +71 -204
- package/dist/src/agent/claude/claude-args.js +9 -6
- package/dist/src/agent/claude/claude-backend.js +10 -10
- package/dist/src/agent/claude/claude-env.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +4 -3
- 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 +29 -24
- package/dist/src/agent/notes-shim.js +11 -12
- package/dist/src/agent/opencode/opencode-args.js +0 -1
- package/dist/src/agent/opencode/opencode-backend.js +12 -9
- package/dist/src/agent/opencode/opencode-catalog.js +55 -29
- package/dist/src/agent/opencode/opencode-config.js +19 -14
- package/dist/src/agent/opencode/opencode-env.js +0 -1
- package/dist/src/agent/opencode/opencode-session-id.js +1 -2
- package/dist/src/agent/opencode/opencode-stream.js +1 -1
- package/dist/src/agent/opencode/write-guard-plugin/index.js +48 -0
- package/dist/src/agent/role-notes-write.js +5 -6
- package/dist/src/agent/run-agent.js +465 -448
- package/dist/src/agent/run-settle.js +214 -0
- package/dist/src/agent/session-ledger.js +86 -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 +246 -55
- package/dist/src/backlog/backlog-cli-view.js +280 -0
- package/dist/src/backlog/backlog-cli.js +85 -274
- package/dist/src/backlog/backlog-commit.js +4 -5
- package/dist/src/backlog/backlog-eligibility.js +175 -242
- package/dist/src/backlog/backlog-index.js +149 -0
- package/dist/src/backlog/backlog-md.js +54 -6
- 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 +27 -0
- package/dist/src/backlog/backlog-structure.js +99 -156
- package/dist/src/backlog/backlog-write.js +40 -127
- package/dist/src/backlog/backlog.js +21 -6
- package/dist/src/backlog/question-answer.js +21 -15
- package/dist/src/baseline/main-baseline.js +89 -20
- 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 +139 -32
- 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 +9 -11
- package/dist/src/brief/brief.js +31 -51
- package/dist/src/budget/budget.js +30 -20
- package/dist/src/budget/fallback-breaker.js +26 -27
- package/dist/src/build/build-check-detect.js +23 -15
- package/dist/src/build/build-check-events.js +25 -6
- package/dist/src/build/build-check-report.js +49 -9
- package/dist/src/build/build-check-scoped.js +18 -11
- package/dist/src/build/build-check.js +2 -1
- package/dist/src/build/build-info.js +2 -3
- package/dist/src/build/build-stage.js +9 -14
- package/dist/src/build/dep-install.js +1 -2
- package/dist/src/change/change-data.js +123 -31
- package/dist/src/change/change-render.js +46 -5
- package/dist/src/cli/brief-command.js +3 -4
- package/dist/src/cli/cli-args.js +118 -33
- package/dist/src/cli/cli-command-args.js +46 -18
- package/dist/src/cli/cli-editor.js +2 -1
- package/dist/src/cli/cli-flag-specs.js +150 -45
- package/dist/src/cli/cli-init.js +51 -0
- package/dist/src/cli/cli-marker-commands.js +4 -4
- package/dist/src/cli/cli-query-commands.js +308 -590
- package/dist/src/cli/cli-repo-gate.js +11 -1
- 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-run.js → cli-start.js} +165 -237
- package/dist/src/cli/completion.js +149 -22
- package/dist/src/cli/config-commands.js +41 -18
- package/dist/src/cli/follow-view.js +75 -0
- package/dist/src/cli/help.js +189 -73
- package/dist/src/cli/log-commands.js +148 -82
- package/dist/src/cli/question-commands.js +205 -10
- package/dist/src/cli.js +106 -43
- package/dist/src/collections.js +26 -10
- package/dist/src/concurrency/check-permit.js +20 -3
- package/dist/src/concurrency/lock.js +17 -13
- package/dist/src/concurrency/provider-lease.js +44 -8
- package/dist/src/concurrency/semaphore.js +102 -26
- package/dist/src/config/config-aliases.js +0 -1
- package/dist/src/config/config-editable-keys.js +13 -1
- package/dist/src/config/config-fallbacks.js +93 -56
- package/dist/src/config/config-field-checks.js +40 -10
- package/dist/src/config/config-keys.js +30 -8
- package/dist/src/config/config-live.js +63 -24
- package/dist/src/config/config-request.js +137 -0
- package/dist/src/config/config-schema.js +31 -24
- 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 +141 -199
- package/dist/src/config/config-views.js +127 -53
- package/dist/src/config/config-write-core.js +135 -0
- package/dist/src/config/config-write.js +131 -264
- package/dist/src/config/config.js +48 -38
- package/dist/src/config/model-selector.js +34 -34
- 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 +100 -496
- package/dist/src/doctor/doctor-check-names.js +81 -0
- package/dist/src/doctor/doctor-checks.js +52 -579
- 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 +82 -66
- package/dist/src/doctor/doctor-install.js +2 -1
- package/dist/src/doctor/doctor-model-checks.js +236 -402
- package/dist/src/doctor/doctor-notify.js +3 -4
- package/dist/src/doctor/doctor-opencode-checks.js +162 -0
- package/dist/src/doctor/doctor-orphans.js +182 -63
- package/dist/src/doctor/doctor-project-checks.js +303 -0
- package/dist/src/doctor/doctor.js +121 -77
- package/dist/src/doctor/image-probe.js +21 -8
- package/dist/src/doctor/outcome.js +3 -2
- package/dist/src/doctor/probe.js +7 -11
- package/dist/src/events/event-format.js +181 -86
- package/dist/src/events/event-labels.js +12 -5
- package/dist/src/events/event-legacy.js +39 -15
- package/dist/src/events/event-read.js +39 -39
- package/dist/src/events/event-tally.js +16 -0
- package/dist/src/events/event-window.js +60 -24
- package/dist/src/events/events.js +30 -13
- package/dist/src/events/notify.js +7 -7
- package/dist/src/failure/failure-cluster.js +12 -12
- package/dist/src/failure/failure-data.js +56 -56
- package/dist/src/failure/failure-render.js +31 -23
- package/dist/src/failure/failure-state-change.js +17 -12
- package/dist/src/failure/time-spend.js +99 -35
- package/dist/src/files/files.js +111 -35
- package/dist/src/files/json-files.js +3 -3
- package/dist/src/files/json-object.js +28 -7
- package/dist/src/files/stat-cache.js +2 -2
- package/dist/src/files/tail.js +1 -1
- package/dist/src/gates/bootstrap-gates.js +4 -5
- package/dist/src/gates/budget-gates.js +177 -84
- package/dist/src/gates/disk-gate.js +1 -1
- package/dist/src/gates/gate-polls.js +108 -58
- package/dist/src/gates/maintenance-quota.js +10 -13
- package/dist/src/gates/pause-gates.js +28 -10
- package/dist/src/gates/readiness.js +7 -3
- package/dist/src/gates/role-cap-gates.js +8 -8
- package/dist/src/gates/startup-gate.js +94 -32
- package/dist/src/gates/streak-gate.js +54 -30
- package/dist/src/git/commit-message.js +77 -46
- package/dist/src/git/git-diff.js +16 -10
- package/dist/src/git/git-run.js +79 -38
- package/dist/src/git/git.js +36 -34
- package/dist/src/git/slot-salvage.js +253 -0
- package/dist/src/git/slots-state.js +6 -6
- package/dist/src/git/worktree-pool.js +132 -38
- package/dist/src/git/worktree-use.js +4 -11
- package/dist/src/git/worktree.js +94 -37
- package/dist/src/gui/gui-admin-endpoints.js +96 -0
- package/dist/src/gui/gui-args.js +79 -8
- package/dist/src/gui/gui-backlog-commands.js +42 -18
- package/dist/src/gui/gui-backlog-endpoints.js +155 -0
- package/dist/src/gui/gui-command.js +8 -13
- package/dist/src/gui/gui-config-commands.js +94 -72
- package/dist/src/gui/gui-endpoint-commands.js +4 -3
- package/dist/src/gui/gui-endpoint-common.js +4 -4
- package/dist/src/gui/gui-endpoints.js +11 -380
- package/dist/src/gui/gui-history-endpoints.js +301 -0
- package/dist/src/gui/gui-operator-commands.js +108 -13
- package/dist/src/gui/gui-prompt-commands.js +11 -14
- package/dist/src/gui/gui-report-endpoints.js +90 -0
- package/dist/src/gui/gui-server.js +11 -4
- package/dist/src/gui/gui-team-commands.js +7 -7
- package/dist/src/gui/http-body.js +1 -1
- package/dist/src/history/history-data.js +122 -234
- package/dist/src/history/history-queued.js +147 -0
- package/dist/src/history/history-render.js +42 -22
- package/dist/src/history/history.js +193 -49
- package/dist/src/inbox/inbox-attachments.js +29 -30
- package/dist/src/inbox/inbox-cancel.js +10 -11
- package/dist/src/inbox/inbox-edit.js +9 -9
- package/dist/src/inbox/inbox-submit.js +18 -18
- package/dist/src/inbox/inbox-top.js +25 -9
- package/dist/src/inbox/inbox.js +42 -43
- package/dist/src/inbox/pending-prompt.js +26 -26
- package/dist/src/inbox/prompt-commands.js +62 -49
- package/dist/src/inbox/prompt-not-before.js +2 -2
- package/dist/src/init/init-templates.js +1 -2
- package/dist/src/init/init.js +28 -23
- package/dist/src/merge/backlog-conflicts.js +215 -35
- package/dist/src/merge/merge-batch.js +14 -8
- package/dist/src/merge/merge-cancel.js +53 -0
- package/dist/src/merge/merge-check-failures.js +44 -22
- package/dist/src/merge/merge-core.js +81 -32
- package/dist/src/merge/merge-diff.js +5 -4
- package/dist/src/merge/merge-drain.js +150 -11
- package/dist/src/merge/merge-git.js +177 -38
- package/dist/src/merge/merge-pipeline.js +15 -3
- package/dist/src/merge/merge-post-review.js +90 -0
- package/dist/src/merge/merge-queue-render.js +49 -11
- package/dist/src/merge/merge-queue.js +31 -15
- package/dist/src/merge/merge-requeue.js +102 -0
- package/dist/src/merge/merge-slot.js +61 -16
- package/dist/src/merge/merge-stack.js +33 -11
- package/dist/src/merge/merge-to-main.js +86 -27
- package/dist/src/merge/merge-top.js +69 -0
- package/dist/src/merge/merge-verify.js +12 -10
- package/dist/src/notes-cli.js +30 -15
- package/dist/src/operator/operator-commands.js +192 -81
- package/dist/src/operator/operator-intent.js +69 -42
- package/dist/src/operator/operator-requests.js +35 -40
- package/dist/src/operator/retire.js +5 -6
- package/dist/src/operator/revert.js +142 -34
- package/dist/src/orchestrator/orchestrator-launch.js +60 -52
- package/dist/src/orchestrator/orchestrator-poll.js +320 -0
- package/dist/src/orchestrator/orchestrator-scheduling.js +68 -369
- package/dist/src/orchestrator/orchestrator.js +128 -369
- package/dist/src/orchestrator/retention.js +42 -8
- 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 +8 -7
- package/dist/src/orchestrator/startup-migrations.js +11 -7
- package/dist/src/paths.js +116 -49
- package/dist/src/pi/command-shape.js +12 -1
- package/dist/src/pi/pi-args.js +6 -4
- package/dist/src/pi/pi-backend.js +6 -6
- package/dist/src/pi/pi-bin.js +54 -63
- package/dist/src/pi/pi-listed-models.js +53 -0
- package/dist/src/pi/pi-models.js +63 -30
- package/dist/src/pi/pi-run-result.js +1 -1
- package/dist/src/pi/pi-settings.js +1 -1
- package/dist/src/pi/pi-stream.js +6 -6
- package/dist/src/pi/pi-watchdogs.js +18 -19
- package/dist/src/pi-extension/bounded-output.js +5 -3
- package/dist/src/pi-extension/context-budget.js +2 -2
- package/dist/src/pi-extension/context-shake.js +1 -1
- package/dist/src/pi-extension/context-window.js +0 -1
- package/dist/src/pi-extension/full-output.js +3 -10
- package/dist/src/pi-extension/strip-thinking.js +0 -1
- package/dist/src/pi-extension/trim-system-prompt.js +3 -4
- package/dist/src/pi-extension/write-guard.js +37 -0
- package/dist/src/process/launch-services.js +1 -2
- package/dist/src/process/node-bin.js +16 -0
- package/dist/src/process/process-group.js +8 -18
- package/dist/src/process/process.js +41 -2
- package/dist/src/process/run-marker.js +18 -6
- package/dist/src/process/supervisor.js +22 -21
- package/dist/src/prompt/dialect.js +17 -2
- package/dist/src/prompt/gate-notes.js +203 -0
- package/dist/src/prompt/gate-prompts.js +121 -211
- package/dist/src/prompt/notes-backlog.js +181 -0
- package/dist/src/prompt/principles.js +1 -2
- package/dist/src/prompt/prompt-followup.js +19 -14
- package/dist/src/prompt/prompt.js +129 -204
- package/dist/src/redeploy/redeploy-policy.js +24 -19
- package/dist/src/redeploy/redeploy-probes.js +1 -1
- package/dist/src/redeploy/redeploy.js +2 -3
- package/dist/src/redeploy/redeployer.js +20 -20
- package/dist/src/redeploy/self-reload.js +2 -1
- package/dist/src/report/report-data.js +161 -77
- package/dist/src/report/report-fold.js +129 -52
- package/dist/src/report/report-main-red.js +55 -0
- package/dist/src/report/report-render.js +133 -64
- 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 +100 -1
- package/dist/src/review/post-merge-review.js +106 -0
- package/dist/src/review/protected-edits.js +4 -5
- package/dist/src/review/review-followup.js +8 -8
- package/dist/src/review/review-precheck.js +66 -23
- package/dist/src/review/review-run.js +395 -0
- package/dist/src/review/review-verdict.js +2 -2
- package/dist/src/review/review.js +155 -360
- package/dist/src/review/secret-leaks.js +36 -16
- package/dist/src/review/suite-rerun.js +0 -1
- package/dist/src/roles/legacy-role-migration.js +80 -7
- package/dist/src/roles/role-catalog.js +69 -12
- package/dist/src/roles/role-guidance.js +55 -20
- package/dist/src/roles/role-labels.js +1 -2
- package/dist/src/roles/role-render.js +67 -50
- package/dist/src/roles/role-view.js +59 -47
- package/dist/src/roles/roles.js +31 -31
- 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 +9 -27
- package/dist/src/{tick/tick-apply.js → run/run-apply.js} +95 -94
- 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/{tick/tick-finalize.js → run/run-finalize.js} +72 -60
- package/dist/src/{tick/tick-outcome.js → run/run-outcome.js} +6 -7
- package/dist/src/{tick/tick-prompt.js → run/run-prompt.js} +108 -73
- package/dist/src/{tick/tick-resume.js → run/run-resume.js} +55 -26
- package/dist/src/{tick/tick-stage.js → run/run-stage.js} +108 -50
- package/dist/src/{tick/tick-timing.js → run/run-timing.js} +43 -41
- package/dist/src/{tick/tick-usage.js → run/run-usage.js} +45 -37
- package/dist/src/{tick/tick-verdict.js → run/run-verdict.js} +91 -56
- 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/{loop → runner}/leftover.js +80 -25
- package/dist/src/{loop → runner}/model-fallback.js +12 -12
- package/dist/src/runner/revision.js +202 -0
- package/dist/src/{loop/loop-fallback.js → runner/runner-fallback.js} +8 -8
- package/dist/src/runner/runner-outcome.js +107 -0
- package/dist/src/{loop/loop-pi.js → runner/runner-pi.js} +140 -131
- package/dist/src/{loop/loop-recovery.js → runner/runner-recovery.js} +21 -15
- package/dist/src/{loop/loop-tick.js → runner/runner-run.js} +218 -240
- package/dist/src/{loop/loop-state.js → runner/runner-state-load.js} +61 -114
- package/dist/src/runner/runner-state.js +102 -0
- package/dist/src/{loop/loop.js → runner/runner.js} +123 -120
- package/dist/src/scheduling/backoff.js +38 -38
- package/dist/src/scheduling/claims.js +35 -22
- package/dist/src/scheduling/once-round.js +22 -22
- package/dist/src/scheduling/quiet-hours.js +5 -6
- package/dist/src/scheduling/scheduling.js +74 -64
- package/dist/src/scheduling/work-merged-cache.js +2 -2
- package/dist/src/status/status-budget.js +55 -47
- package/dist/src/status/status-data.js +43 -16
- package/dist/src/status/status-inbox.js +2 -2
- package/dist/src/status/status-install.js +0 -1
- package/dist/src/status/status-polls.js +28 -28
- package/dist/src/status/status-roles.js +39 -13
- package/dist/src/team/error-storm.js +8 -8
- package/dist/src/team/failure-spread.js +2 -2
- package/dist/src/team/orchestrator-info.js +15 -9
- package/dist/src/team/reclaim.js +5 -6
- package/dist/src/team/team-hold.js +7 -7
- package/dist/src/team/team-polls.js +91 -50
- package/dist/src/team/team-state.js +8 -2
- package/dist/src/text/datetime.js +63 -17
- package/dist/src/text/format.js +48 -11
- package/dist/src/text/markdown.js +8 -5
- package/dist/src/text/phrases.js +57 -23
- package/dist/src/text/suggest.js +1 -1
- package/dist/src/text/text.js +67 -13
- package/dist/src/ui/badges.js +65 -37
- package/dist/src/ui/gui/gui-client-backlog.js +292 -0
- package/dist/src/ui/gui/gui-client-boot.js +32 -26
- package/dist/src/ui/gui/gui-client-composer.js +8 -9
- package/dist/src/ui/gui/gui-client-drawer-config.js +212 -0
- package/dist/src/ui/gui/gui-client-drawer.js +126 -285
- package/dist/src/ui/gui/gui-client-feed.js +271 -0
- package/dist/src/ui/gui/gui-client-history.js +434 -67
- package/dist/src/ui/gui/gui-client-labels.js +1 -2
- package/dist/src/ui/gui/gui-client-markdown.js +1 -2
- package/dist/src/ui/gui/gui-client-model.js +27 -36
- package/dist/src/ui/gui/gui-client-operator.js +55 -51
- package/dist/src/ui/gui/gui-client-pending.js +227 -23
- package/dist/src/ui/gui/gui-client-report-chart.js +464 -0
- package/dist/src/ui/gui/gui-client-report.js +87 -427
- package/dist/src/ui/gui/{gui-client-loops.js → gui-client-roles.js} +43 -44
- package/dist/src/ui/gui/gui-client-settings.js +56 -4
- package/dist/src/ui/gui/gui-client-team.js +25 -465
- package/dist/src/ui/gui/gui-client.js +27 -25
- package/dist/src/ui/gui/gui-icons.js +0 -2
- package/dist/src/ui/gui/gui-page.js +9 -7
- package/dist/src/ui/gui/gui-styles.js +34 -34
- package/dist/src/ui/progress-data.js +9 -11
- package/dist/src/ui/{tick-progress-model.js → run-progress-model.js} +31 -31
- package/dist/src/ui/status-model.js +133 -125
- package/dist/src/ui/status-payload.js +136 -49
- package/dist/src/ui/status-render.js +101 -82
- package/dist/src/ui/team-alerts.js +248 -57
- package/dist/src/ui/tone.js +5 -6
- package/dist/src/ui/transcript-run.js +58 -0
- package/dist/src/ui/transcript-tail.js +28 -20
- package/dist/src/ui/transcript.js +63 -23
- package/dist/src/ui/tui/tui-app.js +0 -1
- package/dist/src/ui/tui/tui-backlog.js +51 -14
- package/dist/src/ui/tui/tui-editor-answer.js +27 -31
- package/dist/src/ui/tui/tui-editor-attach.js +3 -5
- package/dist/src/ui/tui/tui-editor-backends.js +132 -0
- package/dist/src/ui/tui/tui-editor-brief.js +10 -12
- package/dist/src/ui/tui/tui-editor-budget.js +35 -31
- 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-entry.js +36 -47
- 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 +11 -9
- package/dist/src/ui/tui/tui-editor-model.js +29 -0
- package/dist/src/ui/tui/tui-editor-notes.js +3 -5
- package/dist/src/ui/tui/tui-editor-notify.js +25 -0
- package/dist/src/ui/tui/tui-editor-pause.js +48 -59
- package/dist/src/ui/tui/tui-editor-prompt.js +11 -16
- package/dist/src/ui/tui/tui-editor-provider-caps.js +27 -0
- package/dist/src/ui/tui/tui-editor-queued.js +5 -9
- package/dist/src/ui/tui/tui-editor-quiet.js +99 -35
- package/dist/src/ui/tui/tui-editor-retire.js +4 -6
- 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 +45 -33
- package/dist/src/ui/tui/tui-editor-session.js +60 -34
- package/dist/src/ui/tui/tui-editor-slots.js +23 -0
- package/dist/src/ui/tui/tui-editors.js +35 -2
- package/dist/src/ui/tui/tui-frame.js +56 -11
- package/dist/src/ui/tui/tui-history.js +8 -9
- package/dist/src/ui/tui/tui-input.js +13 -11
- package/dist/src/ui/tui/tui-keymap.js +0 -1
- package/dist/src/ui/tui/tui-keys.js +24 -9
- package/dist/src/ui/tui/tui-pane-activity.js +14 -11
- package/dist/src/ui/tui/tui-pane-backlog.js +171 -126
- package/dist/src/ui/tui/tui-pane-brief.js +0 -1
- package/dist/src/ui/tui/tui-pane-ctx.js +2 -2
- package/dist/src/ui/tui/tui-pane-health.js +58 -8
- package/dist/src/ui/tui/tui-pane-history.js +402 -82
- package/dist/src/ui/tui/tui-pane-pending.js +308 -13
- package/dist/src/ui/tui/tui-pane-queued.js +71 -55
- package/dist/src/ui/tui/tui-pane-report.js +46 -14
- package/dist/src/ui/tui/tui-pane-session.js +25 -6
- package/dist/src/ui/tui/tui-pane-transcript.js +111 -37
- package/dist/src/ui/tui/tui-pane.js +80 -23
- package/dist/src/ui/tui/tui-panes.js +100 -30
- package/dist/src/ui/tui/tui.js +14 -14
- package/dist/src/verdict/fix-claim.js +82 -32
- package/dist/src/verdict/no-change.js +4 -4
- package/dist/src/verdict/refusal.js +40 -16
- package/dist/src/verdict/reply-contract.js +42 -31
- package/dist/src/version.js +0 -1
- package/package.json +2 -1
- package/dist/src/loop/revision.js +0 -70
- package/dist/src/roles/loop-ids.js +0 -77
- package/dist/src/tick/stage-check.js +0 -419
- package/dist/src/tick/telemetry-digest.js +0 -25
- package/dist/src/tick/tick-detail-data.js +0 -108
- package/dist/src/tick/tick-detail.js +0 -163
- package/dist/src/ui/gui/gui-client-sound.js +0 -53
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { runPi } from "../pi/pi.js";
|
|
2
|
-
import {
|
|
2
|
+
import { backendSessionResumable } from "../agent/agent-backend.js";
|
|
3
3
|
import { HOLD_BASE_MS } from "../team/team-hold.js";
|
|
4
4
|
import { backendKindPhrase } from "../text/phrases.js";
|
|
5
|
-
import { configForRole,
|
|
5
|
+
import { configForRole, resolvedBackendConfig, seamHoldKey, seamPairName } from "../config/config-views.js";
|
|
6
6
|
import { ensureProviderLease } from "../concurrency/provider-lease.js";
|
|
7
7
|
import { buildSummaryRequestPrompt } from "../prompt/prompt-followup.js";
|
|
8
8
|
import { readClaudeAccount, subscriptionWindowRejected } from "../agent/claude/claude-account.js";
|
|
9
9
|
import { observeOpencodeRun, opencodeModelKey } from "../agent/opencode/opencode-catalog.js";
|
|
10
10
|
import { dailyCost, teamDailyCost, pairDailyCost } from "../budget/budget.js";
|
|
11
|
-
import {
|
|
12
|
-
import { baseRoleOf,
|
|
11
|
+
import { loadRunnerState } from "./runner-state-load.js";
|
|
12
|
+
import { baseRoleOf, runnerIds } from "../roles/runner-ids.js";
|
|
13
13
|
import { buildStageFixPrompt } from "../prompt/gate-prompts.js";
|
|
14
14
|
import { logEventBestEffort } from "../events/events.js";
|
|
15
15
|
import { nativeLogPath, piLogPath, roleNotesPath, sessionDir } from "../paths.js";
|
|
@@ -17,15 +17,15 @@ import { piModelsPath } from "../pi/pi-models.js";
|
|
|
17
17
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
18
18
|
import { cappedRequestTimeouts } from "../request-timeouts.js";
|
|
19
19
|
/** Upper bound on how long the transient retry waits out a provider's Retry-After hint
|
|
20
|
-
* before re-attempting a rate-limited run. Honoring the hint is the point; capping it is
|
|
21
|
-
* what keeps one generous hint from consuming the
|
|
22
|
-
* full fresh run budget, so a wait larger than the cap would spend the
|
|
20
|
+
* before re-attempting a rate-limited agent run. Honoring the hint is the point; capping it is
|
|
21
|
+
* what keeps one generous hint from consuming the run's own agent run budget (the retry gets a
|
|
22
|
+
* full fresh agent run budget, so a wait larger than the cap would spend the run waiting, not
|
|
23
23
|
* working). */
|
|
24
24
|
const RATE_LIMIT_RETRY_AFTER_CAP_S = 120;
|
|
25
25
|
/** What a hint-less 429 waits before its one retry. With no Retry-After from the provider, the
|
|
26
26
|
* team's own constants state the refill physics: team/team-hold.ts's base hold is one minute,
|
|
27
27
|
* "the shortest pause that lets the bucket refill". Retrying sooner re-enters the same exhausted
|
|
28
|
-
* per-minute bucket the first request just emptied and burns the
|
|
28
|
+
* per-minute bucket the first request just emptied and burns the run's only retry on a
|
|
29
29
|
* near-certain second 429. A present hint always wins instead; the cap above still bounds
|
|
30
30
|
* whatever the wait ends up being. */
|
|
31
31
|
const RATE_LIMIT_NO_HINT_RETRY_S = Math.round(HOLD_BASE_MS / 1000);
|
|
@@ -38,10 +38,10 @@ function sleepFor(ms) {
|
|
|
38
38
|
* base role's own remaining cap when `maxDailyCostUsdPerRole` names it. The cap map and its
|
|
39
39
|
* spend are keyed by BASE role, exactly as the scheduler's role-cap gate reads them
|
|
40
40
|
* (src/gates/role-cap-gates.ts: `baseRoleOf` groups `feature`/`feature-2` and sums the
|
|
41
|
-
* group's spend), so instance
|
|
41
|
+
* group's spend), so instance runner ids share their base's cap instead of silently falling
|
|
42
42
|
* through to the team budget. Returns undefined — no cap — for the director, a non-claude
|
|
43
43
|
* seam, an account that is not API-billed, a disabled cap (0), or no remaining budget at all
|
|
44
|
-
* (a non-positive cap is dropped by the argv builder). Spend is read from the persisted
|
|
44
|
+
* (a non-positive cap is dropped by the argv builder). Spend is read from the persisted runner
|
|
45
45
|
* states: each role's daily window is what the budget gate and the dashboards sum, and this
|
|
46
46
|
* runs before the authoring run records its own cost. A pair with a step cap of its own (a
|
|
47
47
|
* priced fallback step, `fallbackStepCaps`) is also held to that step's remaining budget. */
|
|
@@ -51,7 +51,7 @@ function authoringBudgetCap(root, role, cfg, agent, pair) {
|
|
|
51
51
|
if (readClaudeAccount(root)?.billing !== "api")
|
|
52
52
|
return undefined;
|
|
53
53
|
const now = Date.now();
|
|
54
|
-
const states =
|
|
54
|
+
const states = runnerIds(cfg).map((r) => loadRunnerState(root, r));
|
|
55
55
|
let cap = cfg.maxDailyCostUsd - teamDailyCost(states, now);
|
|
56
56
|
const base = baseRoleOf(role);
|
|
57
57
|
const perRole = cfg.maxDailyCostUsdPerRole?.[base];
|
|
@@ -59,11 +59,11 @@ function authoringBudgetCap(root, role, cfg, agent, pair) {
|
|
|
59
59
|
const members = states.filter((s) => baseRoleOf(s.role) === base);
|
|
60
60
|
const spent = members.length > 0
|
|
61
61
|
? members.reduce((sum, s) => sum + dailyCost(s, now), 0)
|
|
62
|
-
: dailyCost(
|
|
62
|
+
: dailyCost(loadRunnerState(root, role), now);
|
|
63
63
|
cap = Math.min(cap, perRole - spent);
|
|
64
64
|
}
|
|
65
65
|
// A fallback step with its own cap (applyFallbackModel's fallbackStepCaps, set only on the
|
|
66
|
-
// derived view) lowers the run to what is left of the step's budget for the day.
|
|
66
|
+
// derived view) lowers the agent run to what is left of the step's budget for the day.
|
|
67
67
|
const stepCap = cfg.fallbackStepCaps?.[pair];
|
|
68
68
|
if (stepCap !== undefined)
|
|
69
69
|
cap = Math.min(cap, stepCap - pairDailyCost(states, pair, now));
|
|
@@ -71,28 +71,28 @@ function authoringBudgetCap(root, role, cfg, agent, pair) {
|
|
|
71
71
|
cap = Math.round(cap * 1e6) / 1e6;
|
|
72
72
|
return cap > 0 ? cap : undefined;
|
|
73
73
|
}
|
|
74
|
-
/** The pi-invocation plumbing of one role
|
|
75
|
-
* the shared per-
|
|
76
|
-
* signal), the one bounded transient-failure retry all runs share, the
|
|
77
|
-
* run (which watches only the shutdown signal), and the
|
|
78
|
-
* It owns HOW a
|
|
79
|
-
* and folds every run's spend back through the host's foldUsage (authoring) or
|
|
74
|
+
/** The pi-invocation plumbing of one role's runner, extracted from RoleRunner
|
|
75
|
+
* (src/runner/runner.ts): the shared per-runner wiring for every agent run (role config, session
|
|
76
|
+
* dir, raw log, abort signal), the one bounded transient-failure retry all agent runs share, the
|
|
77
|
+
* merge slot's agent run (which watches only the shutdown signal), and the run's SUMMARY follow-up
|
|
78
|
+
* turn. It owns HOW a runner talks to pi; the RoleRunner state machine owns WHAT agent runs happen
|
|
79
|
+
* and folds every agent run's spend back through the host's foldUsage (authoring) or
|
|
80
80
|
* foldMergeUsage (merge/review). */
|
|
81
|
-
export class
|
|
81
|
+
export class RunnerPi {
|
|
82
82
|
host;
|
|
83
83
|
constructor(host) {
|
|
84
84
|
this.host = host;
|
|
85
85
|
}
|
|
86
|
-
/** The shared per-
|
|
87
|
-
* role-resolved config, this
|
|
86
|
+
/** The shared per-runner wiring for every agent run this run makes in worktree `wt`: the
|
|
87
|
+
* role-resolved config, this runner's session dir and raw log, and the per-run abort signal.
|
|
88
88
|
* One place for those derivations — a new PiRunOptions field touches only here instead of
|
|
89
89
|
* drifting between the author run and the SUMMARY follow-up. Callers override what differs
|
|
90
90
|
* (the follow-up's capped timeouts, the merge's shutdown-only signal). */
|
|
91
|
-
|
|
91
|
+
runnerPiOpts(wt, prompt, sessionName, kind, resume = false, config, notesPath) {
|
|
92
92
|
// A caller-supplied config (the merge conflict resolver's strong-tier one,
|
|
93
|
-
// plans/model-tiers.md part 4/8) replaces the role's own for that one run; the role's
|
|
93
|
+
// plans/model-tiers.md part 4/8) replaces the role's own for that one agent run; the role's
|
|
94
94
|
// config is the default for every other caller. Resolved once here so the native-log path
|
|
95
|
-
// names the
|
|
95
|
+
// names the agent that run actually uses.
|
|
96
96
|
const resolved = config ?? configForRole(this.host.config(), this.host.role);
|
|
97
97
|
const maxBudgetUsd = kind === "author"
|
|
98
98
|
? authoringBudgetCap(this.host.root, this.host.role, this.host.config(), resolved.agent ?? "pi", seamPairName(resolved))
|
|
@@ -101,7 +101,7 @@ export class LoopPi {
|
|
|
101
101
|
cwd: wt,
|
|
102
102
|
prompt,
|
|
103
103
|
...(maxBudgetUsd !== undefined ? { maxBudgetUsd } : {}),
|
|
104
|
-
// Every run names its kind explicitly; the marker it writes is the dashboards' demux
|
|
104
|
+
// Every agent run names its kind explicitly; the marker it writes is the dashboards' demux
|
|
105
105
|
// key, so parts 2/5 and 4/5 may put author and gate runs in the same worktree.
|
|
106
106
|
kind,
|
|
107
107
|
config: resolved,
|
|
@@ -112,60 +112,61 @@ export class LoopPi {
|
|
|
112
112
|
nativeLogFile: nativeLogPath(this.host.root, this.host.role, resolved.agent ?? "pi"),
|
|
113
113
|
signal: this.host.runSignal(),
|
|
114
114
|
// Only runAuthoringPi and its SUMMARY/stage-fix follow-ups pass this; a merge,
|
|
115
|
-
// review, or conflict-resolution run has no
|
|
115
|
+
// review, or conflict-resolution run has no notes, and neither does the director.
|
|
116
116
|
notesPath,
|
|
117
117
|
// A tool call silent for the configured stall threshold names itself in the event feed
|
|
118
118
|
// while the quiet watchdog still counts down (BUGS.md 2026-09-13 sibling): before this,
|
|
119
119
|
// a hung command was invisible until the kill. The dashboards derive their own flag from
|
|
120
120
|
// the raw log, so only the harness event needs wiring here.
|
|
121
121
|
onToolCallStalled: (message) => this.host.warn(message),
|
|
122
|
+
onLeftoversSwept: (message) => this.host.warn(message),
|
|
122
123
|
// The translator's unmapped-native-type and cost_unknown warnings reach the event feed
|
|
123
|
-
// as their own warning events, once per run.
|
|
124
|
+
// as their own warning events, once per agent run.
|
|
124
125
|
onAgentWarning: (warning) => this.host.warn(`agent_unknown_event: ${warning}`),
|
|
125
|
-
//
|
|
126
|
+
// An agent run that read an image its model cannot see (plans/image-input.md "Detecting a
|
|
126
127
|
// blind read"): the runner killed the authoring run (action "fail") or a gate run
|
|
127
|
-
// merely recorded it ("logged") — each logged here with this
|
|
128
|
-
// feed names who, on which
|
|
128
|
+
// merely recorded it ("logged") — each logged here with this runner's identity so the
|
|
129
|
+
// feed names who, on which run, read which file on which blind model.
|
|
129
130
|
onBlindRead: (path, action) => logEventBestEffort(this.host.root, {
|
|
130
131
|
source: this.host.role,
|
|
131
132
|
type: "image_blind",
|
|
132
|
-
run: this.host.
|
|
133
|
+
run: this.host.runNumber(),
|
|
133
134
|
path,
|
|
134
135
|
model: resolved.model,
|
|
135
136
|
action,
|
|
136
137
|
}),
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
|
-
/** Run the orchestrator's merge slot's pi (merge queue 3/5): the shared per-
|
|
140
|
+
/** Run the orchestrator's merge slot's pi (merge queue 3/5): the shared per-runner wiring
|
|
140
141
|
* of runRolePi — role config, session dir, raw log, transient-failure retry — with usage
|
|
141
|
-
* folded into the authoring
|
|
142
|
+
* folded into the authoring runner's counters, but watching ONLY the harness shutdown signal
|
|
142
143
|
* (which may be undefined — PiRunOptions.signal is optional): a merge is outside any
|
|
143
|
-
*
|
|
144
|
-
* never reach it, and the stale per-
|
|
144
|
+
* run, so `tumwater abort --role` (which targets a run's per-run abort controller) must
|
|
145
|
+
* never reach it, and the stale per-run controller of a finished run must not abort it
|
|
145
146
|
* either. Session naming is the caller's (the reviewer composes its own from
|
|
146
|
-
* ReviewContext.
|
|
147
|
+
* ReviewContext.runNumber; merge-to-main.ts keeps its conflict-resolver naming through
|
|
147
148
|
* MergerContext.runPi).
|
|
148
149
|
*/
|
|
149
150
|
async runMergePi(wt, prompt, sessionName, config) {
|
|
150
151
|
return this.runWithTransientRetry({
|
|
151
|
-
...this.
|
|
152
|
+
...this.runnerPiOpts(wt, prompt, sessionName, "gate", false, config),
|
|
152
153
|
signal: this.host.signal,
|
|
153
154
|
}, { authoring: false });
|
|
154
155
|
}
|
|
155
156
|
/** The merge gate's reviewer runs (review.ts's review run and verdict follow-up): the
|
|
156
157
|
* same shared transient-retry wiring — a 429 in the gate waits its hint out and retries
|
|
157
158
|
* once, exactly like an authoring run (BUGS.md 2026-10-01) — with one folding difference:
|
|
158
|
-
* the FINAL run's usage is folded by the gate's caller (merge-core's foldUsage of
|
|
159
|
+
* the FINAL agent run's usage is folded by the gate's caller (merge-core's foldUsage of
|
|
159
160
|
* gate.run / gate.followUpRun), so only a RETRIED first attempt is folded here. That fold
|
|
160
161
|
* is not optional: it stamps the team-wide rate-limit hold's input before the wait, and
|
|
161
162
|
* it is the only record the failed attempt's spend would ever get.
|
|
162
|
-
* Takes full PiRunOptions because the gate's two runs differ in everything but the
|
|
163
|
+
* Takes full PiRunOptions because the gate's two agent runs differ in everything but the runner
|
|
163
164
|
* they charge (config, session dir and name, label, tool-call callbacks, signal).
|
|
164
165
|
*/
|
|
165
166
|
async runGatePi(opts) {
|
|
166
167
|
return this.runWithTransientRetry(opts, { foldFinal: false, authoring: false });
|
|
167
168
|
}
|
|
168
|
-
/** The one bounded transient-failure retry shared by EVERY pi run this
|
|
169
|
+
/** The one bounded transient-failure retry shared by EVERY pi run this runner makes
|
|
169
170
|
* (runRolePi, runMergePi, and runGatePi): two transient failures of the world (not of
|
|
170
171
|
* the session) earn exactly one retry that continues the same session — the model server
|
|
171
172
|
* timing out an idle predict stream, the provider severing that stream outright (undici's
|
|
@@ -174,22 +175,23 @@ export class LoopPi {
|
|
|
174
175
|
* kind), pi itself crashing on a torn server chunk (a JSON.parse failure on its stderr),
|
|
175
176
|
* and the provider rate-limiting the request with HTTP 429 (where the provider's
|
|
176
177
|
* Retry-After hint, when sent, is waited out first — capped, so one provider's generosity
|
|
177
|
-
* cannot eat the
|
|
178
|
-
* the transient-retry path: its session is intact but resuming it would just re-hit
|
|
179
|
-
* whatever hung, burning another full quiet timeout.
|
|
178
|
+
* cannot eat the run's own agent run budget). A harness-killed or quiet-killed agent run never
|
|
179
|
+
* takes the transient-retry path: its session is intact but resuming it would just re-hit
|
|
180
|
+
* whatever hung, burning another full quiet timeout. An agent run whose `--resume` target the
|
|
180
181
|
* backend no longer holds (`PiRunResult.resumeMissing`) is not retried here: continuing is
|
|
181
|
-
* impossible and this layer has no fresh
|
|
182
|
-
* run is returned and its kept worktree resumes as a `handoff` on the next
|
|
183
|
-
* (src/
|
|
182
|
+
* impossible and this layer has no fresh run prompt to hand a new session, so the failed
|
|
183
|
+
* agent run is returned and its kept worktree resumes as a `handoff` on the next run
|
|
184
|
+
* (src/run/run-resume.ts).
|
|
184
185
|
* Extracted verbatim from runRolePi so the rule lives in one place
|
|
185
|
-
* (574a14c's
|
|
186
|
+
* (574a14c's runnerPiOpts move was the wiring half of the same single-source-of-truth).
|
|
186
187
|
*/
|
|
187
|
-
/** Part 6c/7: demote a catalog-free opencode model the first time
|
|
188
|
-
* here — the fold chokepoint every run of the
|
|
189
|
-
* place that has the run's resolved model (opts.config) and its cost (the run)
|
|
190
|
-
* it holds the
|
|
191
|
-
* have no opencode catalog entry and no-op. Best-effort like
|
|
192
|
-
* unwritable cache or event feed must not fail the run that
|
|
188
|
+
/** Part 6c/7: demote a catalog-free opencode model the first time an agent run bills it. Observed
|
|
189
|
+
* here — the fold chokepoint every agent run of the runner passes through — because it is the one
|
|
190
|
+
* place that has the agent run's resolved model (opts.config) and its cost (the agent run)
|
|
191
|
+
* together, and it holds the runner's root and role for the cache and the event feed. pi and
|
|
192
|
+
* claude agent runs have no opencode catalog entry and no-op. Best-effort like
|
|
193
|
+
* observeClaudeAccount: an unwritable cache or event feed must not fail the agent run that
|
|
194
|
+
* observed it. */
|
|
193
195
|
observeFreeModelCost(run, config) {
|
|
194
196
|
if (config.agent !== "opencode")
|
|
195
197
|
return;
|
|
@@ -197,32 +199,35 @@ export class LoopPi {
|
|
|
197
199
|
observeOpencodeRun(this.host.root, { model: opencodeModelKey(config.model ?? ""), costUsd: run.costUsd }, { source: this.host.role });
|
|
198
200
|
}
|
|
199
201
|
catch {
|
|
200
|
-
// The next priced run demotes it; nothing this run depends on is lost.
|
|
202
|
+
// The next priced run demotes it; nothing this agent run depends on is lost.
|
|
201
203
|
}
|
|
202
204
|
}
|
|
203
205
|
async runWithTransientRetry(opts,
|
|
204
|
-
/** runGatePi only: skip folding the FINAL returned run (the caller folds it), while a
|
|
206
|
+
/** runGatePi only: skip folding the FINAL returned agent run (the caller folds it), while a
|
|
205
207
|
* retried first attempt still folds — its spend and its rate-limit hold stamp must not
|
|
206
208
|
* vanish just because the gate routes its own folding. Default folds everything, the
|
|
207
209
|
* authoring and merge behavior. */
|
|
208
210
|
{ foldFinal = true, authoring = true } = {}) {
|
|
209
|
-
// The fold every attempt takes: an authoring run charges the
|
|
211
|
+
// The fold every attempt takes: an authoring run charges the runner's run counters
|
|
210
212
|
// (foldUsage); a merge, reviewer, or conflict-resolution run charges the same cost and
|
|
211
|
-
// prompt-token totals but never the
|
|
212
|
-
// The seam this run actually ran on, as the team hold keys it (seamHoldKey): the fold
|
|
213
|
+
// prompt-token totals but never the run's pre-edit prefix (foldMergeUsage).
|
|
214
|
+
// The seam this agent run actually ran on, as the team hold keys it (seamHoldKey): the fold
|
|
213
215
|
// stamps the rate-limit and backend-failure observations with it, so a storm on a claude
|
|
214
216
|
// reviewer holds `claude` and a pi author on a different provider stays open. opts.config
|
|
215
|
-
// is always the run's own resolved seam (reviewRunConfig for the reviewer, the role's
|
|
217
|
+
// is always the agent run's own resolved seam (reviewRunConfig for the reviewer, the role's
|
|
216
218
|
// config or fallback pair for the author), so this names the backend that failed.
|
|
217
219
|
const provider = seamHoldKey(opts.config);
|
|
218
|
-
// The run's model pair name, the key of the per-step daily spend
|
|
220
|
+
// The agent run's model pair name, the key of the per-step daily spend
|
|
221
|
+
// (RunnerState.dayCostByPair).
|
|
219
222
|
const pair = seamPairName(opts.config);
|
|
223
|
+
// Names the seam in per-run provider warnings; omitted when pi picks its own default model.
|
|
224
|
+
const on = opts.config.model !== undefined ? ` on ${pair}` : "";
|
|
220
225
|
const fold = (run) => authoring ? this.host.foldUsage(run, provider, pair) : this.host.foldMergeUsage(run, provider, pair);
|
|
221
|
-
// The run re-keys the calling
|
|
222
|
-
// not already there (plans/provider-concurrency.md §5): a budget handback or a
|
|
223
|
-
// switch that moved this run onto another seam moves the
|
|
224
|
-
// claude-authored
|
|
225
|
-
// carries no lease (a bare
|
|
226
|
+
// The agent run re-keys the calling run's provider lease to the provider THIS agent run uses,
|
|
227
|
+
// if it is not already there (plans/provider-concurrency.md §5): a budget handback or a
|
|
228
|
+
// model-failure switch that moved this agent run onto another seam moves the run's lease with
|
|
229
|
+
// it, so a claude-authored run whose gate agent run lands on omlx is counted there. No-op when
|
|
230
|
+
// the run carries no lease (a bare RunnerPi unit test, `tumwater history`).
|
|
226
231
|
await ensureProviderLease(opts.config);
|
|
227
232
|
const pi = await runPi(opts);
|
|
228
233
|
this.warnFallbackClone(pi);
|
|
@@ -237,33 +242,33 @@ export class LoopPi {
|
|
|
237
242
|
!pi.ok &&
|
|
238
243
|
// A subscription claude usage-window rejection is not the 429 the retry's minute-scale
|
|
239
244
|
// wait beats: the window resets hours out, so a within-run retry is guaranteed to fail
|
|
240
|
-
// after burning up to RATE_LIMIT_RETRY_AFTER_CAP_S. The
|
|
245
|
+
// after burning up to RATE_LIMIT_RETRY_AFTER_CAP_S. The run ends and the kept session
|
|
241
246
|
// resumes once the team hold lifts (plans/agent-backends.md §8, part 6e).
|
|
242
247
|
!subscriptionWindowRejected(pi)) {
|
|
243
248
|
this.host.warn(pi.transientPiCrash
|
|
244
|
-
? `pi crashed on malformed JSON (${pi.errorMessage ?? "no detail"}) — resuming the session once`
|
|
249
|
+
? `pi crashed on malformed JSON (${pi.errorMessage ?? "no detail"})${on} — resuming the session once`
|
|
245
250
|
: pi.transientRateLimit
|
|
246
|
-
? `provider rate-limited the request (429${pi.retryAfterSeconds ? `, retry after ${pi.retryAfterSeconds}s` : `, no hint — waiting ${RATE_LIMIT_NO_HINT_RETRY_S}s`}) — retrying the agent run once`
|
|
251
|
+
? `provider rate-limited the request (429${pi.retryAfterSeconds ? `, retry after ${pi.retryAfterSeconds}s` : `, no hint — waiting ${RATE_LIMIT_NO_HINT_RETRY_S}s`})${on} — retrying the agent run once`
|
|
247
252
|
: pi.backendKind === "stream-severed"
|
|
248
|
-
?
|
|
253
|
+
? `provider severed the in-flight stream (terminated)${on} — retrying the agent run once`
|
|
249
254
|
: pi.backendKind === "timeout"
|
|
250
|
-
?
|
|
251
|
-
:
|
|
255
|
+
? `provider request timed out after accepting the connection${on} — retrying the agent run once`
|
|
256
|
+
: `model server timed out an idle predict stream (e.g. machine sleep)${on} — retrying the agent run once`);
|
|
252
257
|
// The failed attempt folds NOW, before the wait and the retry: a 429 it ended on is the
|
|
253
|
-
// team-wide rate-limit hold's input (
|
|
258
|
+
// team-wide rate-limit hold's input (RoleRunner.lastRateLimit, stamped at fold time), and
|
|
254
259
|
// folding after a retry that ran on for an hour would report the storm an hour late.
|
|
255
260
|
fold(pi);
|
|
256
261
|
// The pause is the rate-limit branch's, plus the timeout kind's: a server timeout or
|
|
257
262
|
// pi crash is a failure of the local path, retried at once, while a 429 must wait its
|
|
258
263
|
// per-minute bucket out. A hint-less 429 defaults to that minute-scale refill pause
|
|
259
264
|
// instead of 0 — an immediate retry lands in the same exhausted bucket and burns the
|
|
260
|
-
//
|
|
265
|
+
// run's only retry (BUGS.md 2026-09-25). A present hint wins; the cap bounds either.
|
|
261
266
|
// A hint of 0 (or any non-positive value) is no usable hint — "retry now" is exactly
|
|
262
267
|
// the burned-retry bug a hint-less 429 causes — so it falls back to the refill pause
|
|
263
268
|
// just like a missing hint, keeping the warning text and the actual wait in agreement.
|
|
264
269
|
// The timeout kind (BUGS.md 2026-09-30) takes that same minute-scale pause: the
|
|
265
270
|
// provider accepted the request and failed to answer it, an overload shape — an
|
|
266
|
-
// immediate re-request lands at the back of the same queue and burns the
|
|
271
|
+
// immediate re-request lands at the back of the same queue and burns the run's only
|
|
267
272
|
// retry, so the retry waits the refill pause out first.
|
|
268
273
|
const hintS = pi.retryAfterSeconds && pi.retryAfterSeconds > 0 ? pi.retryAfterSeconds : RATE_LIMIT_NO_HINT_RETRY_S;
|
|
269
274
|
const waitS = pi.transientRateLimit
|
|
@@ -274,40 +279,41 @@ export class LoopPi {
|
|
|
274
279
|
if (waitS > 0)
|
|
275
280
|
await (this.host.sleep?.(waitS * 1000) ?? sleepFor(waitS * 1000));
|
|
276
281
|
// Within-run continuity only: resume the session the first attempt created, so its
|
|
277
|
-
// partial progress is not re-done. The next
|
|
282
|
+
// partial progress is not re-done. The next run still starts fresh.
|
|
278
283
|
const retry = await runPi({ ...opts, continueSession: true });
|
|
279
284
|
this.warnFallbackClone(retry);
|
|
280
285
|
this.observeFreeModelCost(retry, opts.config);
|
|
281
286
|
if (foldFinal)
|
|
282
287
|
fold(retry);
|
|
283
|
-
this.warnBackendKindFloor(retry);
|
|
288
|
+
this.warnBackendKindFloor(retry, on);
|
|
284
289
|
return retry;
|
|
285
290
|
}
|
|
286
|
-
this.warnBackendKindFloor(pi);
|
|
291
|
+
this.warnBackendKindFloor(pi, on);
|
|
287
292
|
if (foldFinal)
|
|
288
293
|
fold(pi);
|
|
289
294
|
return pi;
|
|
290
295
|
}
|
|
291
|
-
/** The backend-kind floor (BUGS.md 2026-09-29): a failed run that ended on a
|
|
296
|
+
/** The backend-kind floor (BUGS.md 2026-09-29): a failed agent run that ended on a
|
|
292
297
|
* provider-backend failure (connection down, 5xx, model load) still warns — parity with
|
|
293
298
|
* the 429 branch above, so any episode is visible from the feed alone instead of living
|
|
294
|
-
* only in per-
|
|
299
|
+
* only in per-run error events an operator must aggregate by hand. The team-wide hold
|
|
295
300
|
* (src/team/team-hold.ts) and the storm alarms judge the spread; this line is the per-run
|
|
296
|
-
* floor under them, not a retry: a dead backend is not something an immediate re-run beats.
|
|
297
|
-
* Applied to whichever run is returned (BUGS.md 2026-10-09): the retry's returned run
|
|
298
|
-
* second, independent run the retry predicate was never applied to, so a retry that
|
|
299
|
-
* on an uncovered kind must warn exactly as a first attempt does.
|
|
300
|
-
* DID admit on a backend kind (stream-severed/timeout) is excluded, so the warning's
|
|
301
|
-
* not cover this kind" stays true and a retry failing on a covered kind emits no such line
|
|
302
|
-
* (BUGS.md 2026-10-09 review).
|
|
303
|
-
|
|
301
|
+
* floor under them, not a retry: a dead backend is not something an immediate agent re-run beats.
|
|
302
|
+
* Applied to whichever agent run is returned (BUGS.md 2026-10-09): the retry's returned agent run
|
|
303
|
+
* is a second, independent agent run the retry predicate was never applied to, so a retry that
|
|
304
|
+
* dies on an uncovered kind must warn exactly as a first attempt does. An agent run the retry
|
|
305
|
+
* predicate DID admit on a backend kind (stream-severed/timeout) is excluded, so the warning's
|
|
306
|
+
* "does not cover this kind" stays true and a retry failing on a covered kind emits no such line
|
|
307
|
+
* (BUGS.md 2026-10-09 review). The line names the seam's provider/model pair (`on`,
|
|
308
|
+
* empty with no model). */
|
|
309
|
+
warnBackendKindFloor(run, on) {
|
|
304
310
|
if (!run.ok &&
|
|
305
311
|
!run.aborted &&
|
|
306
312
|
!run.timedOut &&
|
|
307
313
|
!run.quietKilled &&
|
|
308
314
|
run.transientBackend &&
|
|
309
315
|
!this.retryCoversBackendKind(run)) {
|
|
310
|
-
this.host.warn(`provider backend failure (${backendKindPhrase(run.backendKind)}) — the transient retry does not cover this kind; the team-wide wait watches for a storm`);
|
|
316
|
+
this.host.warn(`provider backend failure (${backendKindPhrase(run.backendKind)})${on} — the transient retry does not cover this kind; the team-wide wait watches for a storm`);
|
|
311
317
|
}
|
|
312
318
|
}
|
|
313
319
|
/** Whether the one bounded transient retry admits `run` on its backend-kind alone: the
|
|
@@ -318,48 +324,49 @@ export class LoopPi {
|
|
|
318
324
|
retryCoversBackendKind(run) {
|
|
319
325
|
return run.transientBackend && (run.backendKind === "stream-severed" || run.backendKind === "timeout");
|
|
320
326
|
}
|
|
321
|
-
/** One warning per run whose model id pi could not resolve exactly (BUGS.md 2026-10-06):
|
|
322
|
-
* pi cloned the provider's default model, so the run is priced and windowed as that default
|
|
327
|
+
/** One warning per agent run whose model id pi could not resolve exactly (BUGS.md 2026-10-06):
|
|
328
|
+
* pi cloned the provider's default model, so the agent run is priced and windowed as that default
|
|
323
329
|
* while the operator's config says otherwise — unconditionally visible from the feed alone.
|
|
324
|
-
* A no-op for the ordinary run that printed no such stderr line. */
|
|
330
|
+
* A no-op for the ordinary agent run that printed no such stderr line. */
|
|
325
331
|
warnFallbackClone(run) {
|
|
326
332
|
if (!run.fallbackClone)
|
|
327
333
|
return;
|
|
328
334
|
this.host.warn(`pi could not resolve the model id exactly (${run.fallbackClone}) — the run uses the provider default's price and context window; add an exact entry to ${piModelsPath()}`);
|
|
329
335
|
}
|
|
330
|
-
/** Run pi for this
|
|
331
|
-
* the transient-failure retry, with usage folded into the
|
|
332
|
-
* entry point that carries the role
|
|
333
|
-
* read by the `tumwater-notes` command): every other run (runRolePi's merge/conflict path,
|
|
336
|
+
/** Run pi for this runner's authoring run in worktree `wt`: the shared per-runner wiring and
|
|
337
|
+
* the transient-failure retry, with usage folded into the run's counters. It is the ONE
|
|
338
|
+
* entry point that carries the role notes (PLANS.md "Role notebook": TUMWATER_NOTES_PATH,
|
|
339
|
+
* read by the `tumwater-notes` command): every other agent run (runRolePi's merge/conflict path,
|
|
334
340
|
* runMergePi, runGatePi) leaves it unset, and the director is excluded because its work is
|
|
335
341
|
* the operator's prompt, not a recurring search.
|
|
336
342
|
*
|
|
337
|
-
* Every run starts a FRESH pi session: context never accumulates across
|
|
343
|
+
* Every agent run starts a FRESH pi session: context never accumulates across runs, so runs start
|
|
338
344
|
* cheap (small prefill), never inherit a near-full window, and durable knowledge lives where
|
|
339
345
|
* the prompt makes pi read it — README/PLANS/BUGS, the code itself, and the role's own note.
|
|
340
346
|
* `resume` continues the role's most recent session instead — used only when picking up
|
|
341
|
-
* a
|
|
347
|
+
* a run that a harness shutdown interrupted. */
|
|
342
348
|
async runAuthoringPi(wt, prompt, sessionName, resume = false, config) {
|
|
343
|
-
return this.runWithTransientRetry(this.
|
|
349
|
+
return this.runWithTransientRetry(this.runnerPiOpts(wt, prompt, sessionName, "author", resume, config, this.authorNotesPath()));
|
|
344
350
|
}
|
|
345
|
-
/** The
|
|
346
|
-
* director, which has no
|
|
347
|
-
*
|
|
351
|
+
/** The notes path an authoring run carries: the role's note file, or undefined for the
|
|
352
|
+
* director, which has no notes. The SUMMARY/stage-fix follow-ups share the authoring
|
|
353
|
+
* run's session and must carry the same notes wiring — every invocation reads the path
|
|
348
354
|
* from TUMWATER_NOTES_PATH in the environment, so the follow-up re-passes the same path. */
|
|
349
355
|
authorNotesPath() {
|
|
350
356
|
return this.host.role === DIRECTOR_ROLE
|
|
351
357
|
? undefined
|
|
352
358
|
: roleNotesPath(this.host.root, this.host.role);
|
|
353
359
|
}
|
|
354
|
-
/** The shared authoring/conflict run wiring WITHOUT the role
|
|
360
|
+
/** The shared authoring/conflict agent run wiring WITHOUT the role notes. The merge pipeline's
|
|
355
361
|
* conflict resolver reaches pi through this (plans/merge-queue.md), and a conflict resolution
|
|
356
362
|
* is not the role's recurring search: it must not carry the notes tool or rewrite the note.
|
|
357
|
-
*
|
|
363
|
+
* An authoring agent run goes through runAuthoringPi above, which is the only notes-carrying
|
|
364
|
+
* path. */
|
|
358
365
|
async runRolePi(wt, prompt, sessionName, resume = false, config) {
|
|
359
|
-
// The refusal-note merge (
|
|
360
|
-
// so its runs demux as author — exactly the verdict the pre-part-1/5 cwd test gave them.
|
|
366
|
+
// The refusal-note merge (runner.ts's merge) reaches pi here, in the ROLE's own worktree,
|
|
367
|
+
// so its agent runs demux as author — exactly the verdict the pre-part-1/5 cwd test gave them.
|
|
361
368
|
// The shared-worktree conflict resolver goes through runMergePi and demuxes as gate.
|
|
362
|
-
return this.runWithTransientRetry(this.
|
|
369
|
+
return this.runWithTransientRetry(this.runnerPiOpts(wt, prompt, sessionName, "author", resume, config));
|
|
363
370
|
}
|
|
364
371
|
/** Hard caps on the SUMMARY follow-up turn: it should take one short reply on a warm session,
|
|
365
372
|
* so it never gets the authoring run's hours-long budget. */
|
|
@@ -370,55 +377,57 @@ export class LoopPi {
|
|
|
370
377
|
* are short replies to a session that already holds the work. */
|
|
371
378
|
static STAGE_FIX_TIMEOUT_S = 600;
|
|
372
379
|
/** The shared body of the two `--continue` follow-up turns (requestSummary and
|
|
373
|
-
* requestStageFix): guard on a resumable session, resolve the
|
|
374
|
-
* run one capped turn on the
|
|
380
|
+
* requestStageFix): guard on a resumable session, resolve the run's effective config, and
|
|
381
|
+
* run one capped turn on the runner's shared transient-retry wiring. Both follow-ups must keep
|
|
375
382
|
* the same session guard and capped-config discipline, so the two read one assembly here;
|
|
376
383
|
* `suffix` names the turn in its session name and `timeoutS` is its hard cap, while both
|
|
377
384
|
* share the quiet watchdog value (SUMMARY_REQUEST_QUIET_S). Null when there is no session to
|
|
378
|
-
* continue; the run is returned even when it failed so the caller can honor a shutdown
|
|
385
|
+
* continue; the agent run is returned even when it failed so the caller can honor a shutdown
|
|
386
|
+
* abort.
|
|
379
387
|
*
|
|
380
|
-
* The follow-up continues this
|
|
381
|
-
* session ran on: the caller passes the
|
|
388
|
+
* The follow-up continues this run's own session, so it must run on the same pair that
|
|
389
|
+
* session ran on: the caller passes the run's effective config (the fallback pair while a
|
|
382
390
|
* model-fallback episode is active). A caller without one rides the role's own config. The
|
|
383
|
-
* shared per-
|
|
384
|
-
* authoring run's budget: one short reply on a warm session. The run takes the
|
|
385
|
-
* transient-failure retry (BUGS.md 2026-10-01): a 429 on the follow-up turn waits its
|
|
391
|
+
* shared per-runner wiring (runnerPiOpts) takes the follow-up's hard caps overriding the
|
|
392
|
+
* authoring run's budget: one short reply on a warm session. The agent run takes the runner's
|
|
393
|
+
* SHARED transient-failure retry (BUGS.md 2026-10-01): a 429 on the follow-up turn waits its
|
|
386
394
|
* Retry-After hint out and retries once on the same session, exactly like the authoring run,
|
|
387
|
-
* the merge slot's run, and the gate's reviewer runs it mirrors. The caps bound BOTH
|
|
395
|
+
* the merge slot's agent run, and the gate's reviewer agent runs it mirrors. The caps bound BOTH
|
|
388
396
|
* attempts (the retry inherits this capped config), so a flaky provider cannot stretch the
|
|
389
397
|
* follow-up past its budget by more than one capped turn. The wiring folds every attempt
|
|
390
|
-
* (the failed 429 before the wait), and the returned final run is folded here by the wiring
|
|
398
|
+
* (the failed 429 before the wait), and the returned final agent run is folded here by the wiring
|
|
391
399
|
* itself — no second fold at the call site. */
|
|
392
400
|
async requestFollowUp(wt, prompt, suffix, timeoutS, config) {
|
|
393
401
|
const cfg = config ?? configForRole(this.host.config(), this.host.role);
|
|
394
402
|
const dir = sessionDir(this.host.root, this.host.role);
|
|
395
|
-
// The follow-up continues THIS
|
|
403
|
+
// The follow-up continues THIS run's author session. The backend's own resumable rule
|
|
396
404
|
// keeps it honest: pi would reach the newest file, claude its recorded id, so a conflict
|
|
397
|
-
// resolver's gate run in the same dir cannot be mistaken for this
|
|
405
|
+
// resolver's gate run in the same dir cannot be mistaken for this run's session. The
|
|
398
406
|
// claude config dir rides along so a claude team's resume looks under its own dir.
|
|
399
|
-
if (!
|
|
407
|
+
if (!backendSessionResumable(dir, { kind: "author", agent: cfg.agent ?? "pi" }, resolvedBackendConfig(cfg, "claude").configDir)) {
|
|
400
408
|
return null;
|
|
401
409
|
}
|
|
402
410
|
return this.runWithTransientRetry({
|
|
403
|
-
...this.
|
|
411
|
+
...this.runnerPiOpts(wt, prompt, `tumwater-${this.host.role}-${this.host.runNumber()}-${suffix}`, "author", true, cfg, this.authorNotesPath()),
|
|
404
412
|
config: {
|
|
405
413
|
...cfg,
|
|
406
|
-
...cappedRequestTimeouts(cfg, timeoutS,
|
|
414
|
+
...cappedRequestTimeouts(cfg, timeoutS, RunnerPi.SUMMARY_REQUEST_QUIET_S),
|
|
407
415
|
},
|
|
408
416
|
});
|
|
409
417
|
}
|
|
410
|
-
/** Ask the
|
|
411
|
-
* bounded turn, folded into the
|
|
418
|
+
/** Ask the run's own pi session (--continue) for the missing SUMMARY block: one tightly
|
|
419
|
+
* bounded turn, folded into the run's usage like every other agent run. Null when there is no
|
|
412
420
|
* session to continue (pi never wrote one) — the caller then derives a subject itself. The
|
|
413
|
-
* run is returned even when it failed so the caller can honor a shutdown abort. */
|
|
421
|
+
* agent run is returned even when it failed so the caller can honor a shutdown abort. */
|
|
414
422
|
async requestSummary(wt, config, overLength) {
|
|
415
|
-
return this.requestFollowUp(wt, buildSummaryRequestPrompt(overLength), "summary",
|
|
423
|
+
return this.requestFollowUp(wt, buildSummaryRequestPrompt(overLength), "summary", RunnerPi.SUMMARY_REQUEST_TIMEOUT_S, config);
|
|
416
424
|
}
|
|
417
|
-
/** Ask the
|
|
418
|
-
* one bounded turn, folded into the
|
|
419
|
-
*
|
|
425
|
+
/** Ask the run's own pi session (--continue) to fix the pre-queue self-check's findings:
|
|
426
|
+
* one bounded turn, folded into the run's usage like every other agent run. `change` is the
|
|
427
|
+
* change's description so far, shown so the turn does not restate it. Null when there is no
|
|
428
|
+
* session to continue — the caller then warns and lets the gate judge. The agent run is returned
|
|
420
429
|
* even when it failed so the caller can honor a shutdown abort. */
|
|
421
|
-
async requestStageFix(wt, findings, config) {
|
|
422
|
-
return this.requestFollowUp(wt, buildStageFixPrompt(findings), "stagefix",
|
|
430
|
+
async requestStageFix(wt, findings, change, config) {
|
|
431
|
+
return this.requestFollowUp(wt, buildStageFixPrompt(findings, change), "stagefix", RunnerPi.STAGE_FIX_TIMEOUT_S, config);
|
|
423
432
|
}
|
|
424
433
|
}
|