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,18 +1,18 @@
|
|
|
1
1
|
import { runCostIsReal } from "../pi/pi-run-result.js";
|
|
2
2
|
import { recordDailyCost } from "../budget/budget.js";
|
|
3
3
|
import { nonNegativeNumber } from "../files/json-object.js";
|
|
4
|
-
/** Usage accounting for one role
|
|
5
|
-
* because token/cost/turn bookkeeping is a self-contained concern with its own consumers: the
|
|
4
|
+
/** Usage accounting for one role runner, split out of runner.ts — which keeps the run lifecycle
|
|
5
|
+
* — because token/cost/turn bookkeeping is a self-contained concern with its own consumers: the
|
|
6
6
|
* commit trailer reads turns, the run_end event reads costUsd, and the orchestrator's team-wide
|
|
7
7
|
* hold reads lastRateLimit and lastBackendFailure (src/team/team-hold.ts,
|
|
8
8
|
* src/team/team-polls.ts).
|
|
9
9
|
*
|
|
10
|
-
* fold() is the once-per-run choke point: every pi run of a
|
|
10
|
+
* fold() is the once-per-run choke point: every pi run of a run — main attempt, transient
|
|
11
11
|
* retry, conflict resolution, merge runs via foldMergeUsage — ends up here exactly once, so
|
|
12
12
|
* adding a usage field to PiRunResult touches this single place. */
|
|
13
13
|
/** One persisted counter, healed to a finite non-negative number before it is read and
|
|
14
|
-
* rewritten: `generatedTokens`/`peakContextTokens` are per-
|
|
15
|
-
* lifetime total. `
|
|
14
|
+
* rewritten: `generatedTokens`/`peakContextTokens` are per-run windows and `totalCostUsd` a
|
|
15
|
+
* lifetime total. `loadRunnerState` already heals every top-level numeric state field on read,
|
|
16
16
|
* but fold keeps its own invariant at this write choke point so a hand-edited or torn field (a
|
|
17
17
|
* string that would make `+=` concatenate, NaN/±Infinity that would propagate through Math.max
|
|
18
18
|
* and every display of the figure, or a negative that would run a total backwards) can never
|
|
@@ -21,46 +21,51 @@ import { nonNegativeNumber } from "../files/json-object.js";
|
|
|
21
21
|
function counter(value) {
|
|
22
22
|
return nonNegativeNumber(value, 0);
|
|
23
23
|
}
|
|
24
|
-
/** One role
|
|
25
|
-
* the billing and rate-window readings the orchestrator persists at
|
|
26
|
-
export class
|
|
27
|
-
/** Assistant turns folded into THIS
|
|
24
|
+
/** One role runner's per-run usage accounting: the counters fold() grows from each pi run, plus
|
|
25
|
+
* the billing and rate-window readings the orchestrator persists at run end. */
|
|
26
|
+
export class RunUsage {
|
|
27
|
+
/** Assistant turns folded into THIS run so far (non-persisted): reset at run start,
|
|
28
28
|
* grown in fold. Read at commit time for the trailer, where it holds exactly the
|
|
29
29
|
* pre-commit runs' total (main + transient retry) — conflict-resolution and review runs
|
|
30
|
-
* fold after the commit and never inflate it. Deliberately not on
|
|
30
|
+
* fold after the commit and never inflate it. Deliberately not on RunnerState: its only
|
|
31
31
|
* consumer is the trailer stamped into the commit message itself, which is durable. */
|
|
32
32
|
turns = 0;
|
|
33
|
-
/** USD cost folded into THIS
|
|
34
|
-
* turns, grown in fold. Deliberately not on
|
|
33
|
+
/** USD cost folded into THIS run so far (non-persisted): reset at run start alongside
|
|
34
|
+
* turns, grown in fold. Deliberately not on RunnerState — unlike generatedTokens, no
|
|
35
35
|
* dashboard reads it mid-run; its only consumer is the run_end event, which fires before
|
|
36
|
-
* the next
|
|
36
|
+
* the next run resets it (plans: per-run usage in the event feed). */
|
|
37
37
|
costUsd = 0;
|
|
38
|
-
/** Subscription runs' cost folded into THIS
|
|
38
|
+
/** Subscription runs' cost folded into THIS run (non-persisted): API-equivalent spend the
|
|
39
39
|
* run reported but that never charges the daily cap (plans/agent-backends.md part 6a). The
|
|
40
|
-
* run_end event carries it and the report shows it on its own notional line. Reset at
|
|
40
|
+
* run_end event carries it and the report shows it on its own notional line. Reset at run
|
|
41
41
|
* start like costUsd. */
|
|
42
42
|
notionalCostUsd = 0;
|
|
43
|
-
/** How the
|
|
43
|
+
/** How the run's runs were last reported as paid for, and the newest subscription
|
|
44
44
|
* rate-window readings any of them carried — the account state (claude-account.ts) the
|
|
45
|
-
* orchestrator persists at
|
|
46
|
-
* reporting run of the
|
|
45
|
+
* orchestrator persists at run end. Per-run windows: reset at run start, the last
|
|
46
|
+
* reporting run of the run wins. Undefined until a run reports them. */
|
|
47
47
|
billing;
|
|
48
48
|
rateWindows;
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
49
|
+
/** The backend id whose run last reported billing or rate windows (subscription-account.ts's
|
|
50
|
+
* per-backend file owner): "claude" for example. Undefined until a run that reported either
|
|
51
|
+
* folds in. Only meaningful beside billing/rateWindows — a pi run reports neither and owns
|
|
52
|
+
* no account file. */
|
|
53
|
+
agent;
|
|
54
|
+
/** Prompt tokens folded into THIS run so far (non-persisted, reset at run start). Every
|
|
55
|
+
* pi run of the run contributes — the authoring run, its transient retry, the SUMMARY
|
|
51
56
|
* follow-up, and (through foldMergeUsage) review and conflict-resolution runs — because
|
|
52
57
|
* all of them re-send context the operator paid for. */
|
|
53
58
|
promptTokens = 0;
|
|
54
59
|
/** The cache-read share of promptTokens folded so far. */
|
|
55
60
|
cacheReadTokens = 0;
|
|
56
|
-
/** Prompt tokens folded before the
|
|
57
|
-
* authoring run reports an edit (plans/per-
|
|
61
|
+
/** Prompt tokens folded before the run's first edit: authoring runs only, frozen once an
|
|
62
|
+
* authoring run reports an edit (plans/per-run-prompt-telemetry). Merge, review and
|
|
58
63
|
* conflict-resolution runs are excluded — their context is the gate's, not the authoring
|
|
59
|
-
*
|
|
64
|
+
* run's pre-edit search. */
|
|
60
65
|
preEditPromptTokens = 0;
|
|
61
66
|
/** True once an authoring run has reported a first edit; the pre-edit counter freezes. */
|
|
62
67
|
editSeen = false;
|
|
63
|
-
/** This
|
|
68
|
+
/** This role's most recent pi run that ended on a provider 429 — author run, retry,
|
|
64
69
|
* reviewer or merge run alike, since every one folds through fold — with the provider's
|
|
65
70
|
* Retry-After hint when it sent one. The orchestrator reads it every poll as this role's
|
|
66
71
|
* rate-limit input to the team-wide hold (src/team/team-hold.ts): the per-run retry has no
|
|
@@ -68,38 +73,39 @@ export class TickUsage {
|
|
|
68
73
|
* fact without a new cross-module channel. In memory only — a hold is minutes long, so a
|
|
69
74
|
* restart forgetting it costs nothing. Undefined until the first such run. */
|
|
70
75
|
lastRateLimit;
|
|
71
|
-
/** Per-provider spend folded into THIS
|
|
76
|
+
/** Per-provider spend folded into THIS run (non-persisted, reset at run start): each
|
|
72
77
|
* run's real/notional cost keyed by the provider it ran on, so the run_end event can say
|
|
73
78
|
* which seam of a mixed team spent what (plans: per-provider spend in the day report). A
|
|
74
79
|
* run whose provider was never resolved attributes nothing — the `provider !== undefined`
|
|
75
|
-
* guard below skips the bump — so an old single-provider
|
|
80
|
+
* guard below skips the bump — so an old single-provider run keeps today's byte-identical
|
|
76
81
|
* behavior. Parallel to costUsd/notionalCostUsd, never subtracted from them. */
|
|
77
82
|
providerCost = new Map();
|
|
78
|
-
/** This
|
|
83
|
+
/** This role's most recent pi run that ended on a provider-wide failure that was NOT rate
|
|
79
84
|
* limiting — the connection down, a 5xx, the model failing to load (PiRunResult.
|
|
80
85
|
* transientBackend) — with the kind the hold's storm test groups storms by. Stamped under
|
|
81
86
|
* the same rule and for the same consumer as lastRateLimit above: the team-wide hold
|
|
82
87
|
* (src/team/team-hold.ts) reads it every poll. In memory only, like lastRateLimit.
|
|
83
88
|
* Undefined until the first such run. */
|
|
84
89
|
lastBackendFailure;
|
|
85
|
-
/** Clear the per-
|
|
90
|
+
/** Clear the per-run windows (turns, costUsd, the prompt-token counters) at run start.
|
|
86
91
|
* lastRateLimit and lastBackendFailure are NOT cleared: they are episodic observations the
|
|
87
|
-
* orchestrator's hold consumes, not per-
|
|
92
|
+
* orchestrator's hold consumes, not per-run windows. */
|
|
88
93
|
reset() {
|
|
89
94
|
this.turns = 0;
|
|
90
95
|
this.costUsd = 0;
|
|
91
96
|
this.notionalCostUsd = 0;
|
|
92
97
|
this.billing = undefined;
|
|
93
98
|
this.rateWindows = undefined;
|
|
99
|
+
this.agent = undefined;
|
|
94
100
|
this.promptTokens = 0;
|
|
95
101
|
this.cacheReadTokens = 0;
|
|
96
102
|
this.preEditPromptTokens = 0;
|
|
97
103
|
this.editSeen = false;
|
|
98
104
|
this.providerCost = new Map();
|
|
99
105
|
}
|
|
100
|
-
/** Fold one pi run's usage into the
|
|
101
|
-
* the
|
|
102
|
-
* through foldMergeUsage: those never advance the
|
|
106
|
+
/** Fold one pi run's usage into the run's counters (gen / peak ctx / cost / turns) and
|
|
107
|
+
* the role's lifetime totals on `s`. `authoring` is false for a merge/review run folded
|
|
108
|
+
* through foldMergeUsage: those never advance the run's pre-edit counter, which measures
|
|
103
109
|
* the authoring search. */
|
|
104
110
|
fold(s, run, authoring = true, provider, pair) {
|
|
105
111
|
s.generatedTokens = counter(s.generatedTokens) + run.outputTokens;
|
|
@@ -111,8 +117,8 @@ export class TickUsage {
|
|
|
111
117
|
if (runCostIsReal(run)) {
|
|
112
118
|
this.costUsd += run.costUsd;
|
|
113
119
|
// The daily cost budget window (plans/daily-cost-budget.md): every real-money pi run of
|
|
114
|
-
// a
|
|
115
|
-
// each
|
|
120
|
+
// a run folds here exactly once, so the team's spend for the local day is complete at
|
|
121
|
+
// each run end.
|
|
116
122
|
recordDailyCost(s, run.costUsd, Date.now(), pair);
|
|
117
123
|
this.bumpProviderCost(provider, run.costUsd, 'costUsd');
|
|
118
124
|
}
|
|
@@ -124,9 +130,11 @@ export class TickUsage {
|
|
|
124
130
|
this.billing = run.billing;
|
|
125
131
|
if (run.rateWindows !== undefined)
|
|
126
132
|
this.rateWindows = run.rateWindows;
|
|
133
|
+
if (run.billing !== undefined || run.rateWindows !== undefined)
|
|
134
|
+
this.agent = run.agent;
|
|
127
135
|
this.promptTokens += run.promptTokens;
|
|
128
136
|
this.cacheReadTokens += run.cacheReadTokens;
|
|
129
|
-
// The pre-edit prefix freezes at the
|
|
137
|
+
// The pre-edit prefix freezes at the run's first edit: every authoring run while no edit
|
|
130
138
|
// has been seen adds its pre-edit count (including the run that edits), and once one
|
|
131
139
|
// reports an edit later runs add nothing. A review/merge run is skipped entirely.
|
|
132
140
|
if (authoring) {
|
|
@@ -149,7 +157,7 @@ export class TickUsage {
|
|
|
149
157
|
if (run.transientBackend && !run.ok && run.backendKind)
|
|
150
158
|
this.lastBackendFailure = { at: Date.now(), kind: run.backendKind, provider };
|
|
151
159
|
}
|
|
152
|
-
/** Add `amount` to one field of the
|
|
160
|
+
/** Add `amount` to one field of the run's per-provider spend ledger, initializing the
|
|
153
161
|
* provider's entry on first contact. `provider` undefined (a run that never resolved one)
|
|
154
162
|
* is a no-op, matching how fold calls it: only runs with a provider reach this. */
|
|
155
163
|
bumpProviderCost(provider, amount, field) {
|
|
@@ -161,7 +169,7 @@ export class TickUsage {
|
|
|
161
169
|
}
|
|
162
170
|
/** Serialize the per-provider split for the run_end payload: a `Record<provider,
|
|
163
171
|
* {costUsd, notionalCostUsd}>`, or `{}` when no run carried a provider — the
|
|
164
|
-
* omit-when-empty idiom
|
|
172
|
+
* omit-when-empty idiom run-finalize.ts applies, so a run whose runs never resolved a
|
|
165
173
|
* provider renders no providerCost field at all. */
|
|
166
174
|
serializeProviderCost() {
|
|
167
175
|
const out = {};
|
|
@@ -1,33 +1,47 @@
|
|
|
1
1
|
import { imageReroute } from "../agent/image-input.js";
|
|
2
2
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
3
3
|
import { isDirty, deleteRef } from "../git/git.js";
|
|
4
|
-
import { applyConfigRequest } from "../config/config-
|
|
4
|
+
import { applyConfigRequest } from "../config/config-request.js";
|
|
5
5
|
import { diagnoseNoChange, extractNoChangeAnswer } from "../verdict/no-change.js";
|
|
6
6
|
import { handleRefusal, refusalContradiction } from "../verdict/refusal.js";
|
|
7
7
|
import { extractSummary } from "../git/commit-message.js";
|
|
8
|
+
import { errorMessage } from "../text/text.js";
|
|
8
9
|
import { recordFlow } from "./qa-coverage.js";
|
|
10
|
+
import { recordFiled } from "./feedback-filings.js";
|
|
9
11
|
import { resetWorktreeToMain } from "../git/worktree.js";
|
|
12
|
+
import { readPiListedModels } from "../pi/pi-listed-models.js";
|
|
10
13
|
import { rejectedRefName } from "../paths.js";
|
|
11
14
|
import { logEventBestEffort } from "../events/events.js";
|
|
12
15
|
import { subscriptionWindowRejected } from "../agent/claude/claude-account.js";
|
|
16
|
+
/** End a run as an abort that arms a resume, the arming the subscription-rejected and
|
|
17
|
+
* budget-capped arms share: the lease release reads `resumePending` as soon as the run
|
|
18
|
+
* returns (runOnceInLeasePhase), before applyRunOutcome runs, so without this the slot is
|
|
19
|
+
* detached and the kept worktree/session edits are lost. Filed here so that arming's timing
|
|
20
|
+
* invariant cannot drift between the two arms (the error-dirty arm arming itself inline). */
|
|
21
|
+
function abortWithResume(s, ctx, pi, lastError, result, resumeCause) {
|
|
22
|
+
s.lastError = pi.errorMessage ?? lastError;
|
|
23
|
+
s.resumePending = true;
|
|
24
|
+
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
25
|
+
return { result, resumeCause };
|
|
26
|
+
}
|
|
13
27
|
/** Classify what the finished run left behind; null means the run is fulfillable and staging
|
|
14
28
|
* should take over. Never throws: every branch returns an outcome, recording what went wrong
|
|
15
29
|
* in `state.lastError` or a warning event. */
|
|
16
|
-
export async function
|
|
30
|
+
export async function resolveRunVerdict(ctx) {
|
|
17
31
|
const { pi, state: s } = ctx;
|
|
18
32
|
// A run whose `--resume` target the backend no longer holds records why before any branch
|
|
19
|
-
// returns: the resume guard the next
|
|
33
|
+
// returns: the resume guard the next run consults is the transcript FILE, which can outlive
|
|
20
34
|
// the conversation (a torn/empty file, or the fake's planted one), so this verdict must
|
|
21
35
|
// outrank it. The failed-dirty arm below is the common case, but an aborted, quiet-killed, or
|
|
22
|
-
// progressing-timeout run keeps the same session and arms the same resume (
|
|
23
|
-
// recording it once here covers every path that resumes (BUGS.md 2026-10-09). Director
|
|
36
|
+
// progressing-timeout run keeps the same session and arms the same resume (run-apply.ts), so
|
|
37
|
+
// recording it once here covers every path that resumes (BUGS.md 2026-10-09). Director runs
|
|
24
38
|
// never resume, so the flag is role-only.
|
|
25
39
|
if (pi.resumeMissing && ctx.role !== DIRECTOR_ROLE)
|
|
26
40
|
s.resumeMissing = true;
|
|
27
41
|
// A killed run (shutdown or timeout) may leave half-done edits; never commit those.
|
|
28
|
-
// The next
|
|
42
|
+
// The next run's reset discards them.
|
|
29
43
|
if (pi.aborted)
|
|
30
|
-
return ctx.
|
|
44
|
+
return ctx.finishAbortedRun();
|
|
31
45
|
ctx.pending.clear();
|
|
32
46
|
// Harness-mediated config writes (plans/portability.md §3/7): the director may have left a
|
|
33
47
|
// config request in its worktree. Consume it here — after the abort return (a deliberate
|
|
@@ -35,7 +49,7 @@ export async function resolveTickVerdict(ctx) {
|
|
|
35
49
|
// timeout, refusal, isDirty, commitAll) — so the request file never enters a diff or a
|
|
36
50
|
// review prompt, and a quiet-killed/timeout re-run starts clean instead of losing the
|
|
37
51
|
// request to the reset. Applied names are announced by the orchestrator's ~2 s live reload
|
|
38
|
-
// (one config_changed event naming the keys); this
|
|
52
|
+
// (one config_changed event naming the keys); this run logs only the rejection paths.
|
|
39
53
|
if (ctx.role === DIRECTOR_ROLE) {
|
|
40
54
|
const request = applyConfigRequest(ctx.root, ctx.wt);
|
|
41
55
|
if (request) {
|
|
@@ -47,8 +61,8 @@ export async function resolveTickVerdict(ctx) {
|
|
|
47
61
|
}
|
|
48
62
|
if (pi.quietKilled) {
|
|
49
63
|
// A hung tool call, not a slow run: the session and the worktree's edits are intact.
|
|
50
|
-
// Preserve both —
|
|
51
|
-
// leaving hours of work for the next
|
|
64
|
+
// Preserve both — applyRunOutcome resumes them promptly like an interruption instead of
|
|
65
|
+
// leaving hours of work for the next run's reset to discard. Director runs never resume;
|
|
52
66
|
// their prompt goes back to the inbox to run fresh, as on any other unfulfilled kill.
|
|
53
67
|
s.lastError = pi.errorMessage ?? "killed as hung";
|
|
54
68
|
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
@@ -60,8 +74,8 @@ export async function resolveTickVerdict(ctx) {
|
|
|
60
74
|
// 97 timeouts in 8 hours, ~55 agent-hours discarded). Handle it the way a quiet kill is
|
|
61
75
|
// handled — keep the session and the worktree's edits, resume promptly, bounded by the
|
|
62
76
|
// same quiet-kill streak — while a run with no recent progress keeps the discard path
|
|
63
|
-
// below. Director
|
|
64
|
-
// fresh (src/inbox/pending-prompt.ts), and
|
|
77
|
+
// below. Director runs never resume; requeueForResume re-queues their prompt
|
|
78
|
+
// fresh (src/inbox/pending-prompt.ts), and applyRunOutcome schedules the immediate retry
|
|
65
79
|
// without a resume.
|
|
66
80
|
s.lastError = pi.errorMessage ?? "timed out while still making progress";
|
|
67
81
|
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
@@ -70,44 +84,36 @@ export async function resolveTickVerdict(ctx) {
|
|
|
70
84
|
if (pi.timedOut) {
|
|
71
85
|
s.lastError = pi.errorMessage ?? "timed out";
|
|
72
86
|
// The request never ran to completion and no work merged: put it back so the next
|
|
73
|
-
//
|
|
87
|
+
// run retries it. (A killed run's half-done edits are discarded by the reset.)
|
|
74
88
|
ctx.pending.requeueUnfulfilled(ctx.userPrompt);
|
|
75
89
|
return { result: "error" };
|
|
76
90
|
}
|
|
77
91
|
// A subscription claude run the usage window rejected is a hold, not a run failure: the
|
|
78
92
|
// team hold keeps every subscription claude seam from starting until `resetsAt`
|
|
79
93
|
// (plans/agent-backends.md §8, part 6c), and this run's session and worktree edits must
|
|
80
|
-
// survive the wait — the next eligible
|
|
94
|
+
// survive the wait — the next eligible run resumes them (part 6e) instead of committing
|
|
81
95
|
// partial work or resetting the worktree to main. The rejected-window reading is the
|
|
82
96
|
// signal (subscriptionWindowRejected), so a pi 429, an API-billed run, or an overage
|
|
83
97
|
// window keeps the paths below.
|
|
84
98
|
if (!pi.ok && ctx.role !== DIRECTOR_ROLE && subscriptionWindowRejected(pi)) {
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
// lost. This is the same inline arming the error-dirty arm below does.
|
|
90
|
-
s.resumePending = true;
|
|
91
|
-
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
92
|
-
return { result: "error", resumeCause: "rate-limit" };
|
|
99
|
+
// The rejection is a hold, not a run failure: arm the resume (the arming the lease
|
|
100
|
+
// release reads as soon as the run returns, before applyRunOutcome) so the kept worktree
|
|
101
|
+
// and session survive the wait. This is the same arming the budget-capped arm below does.
|
|
102
|
+
return abortWithResume(s, ctx, pi, "claude subscription usage window rejected", "error", "rate-limit");
|
|
93
103
|
}
|
|
94
104
|
// An API-billed claude run cut off at its per-run `--max-budget-usd` cap is a budget
|
|
95
|
-
// handback, not a failed
|
|
96
|
-
// its session and worktree edits intact. Keep both and let the next
|
|
105
|
+
// handback, not a failed run: the remaining daily budget is spent, so the run stopped with
|
|
106
|
+
// its session and worktree edits intact. Keep both and let the next run hand off over the
|
|
97
107
|
// kept worktree (plans/agent-backends.md §7, part 7a/7); the cap is what keeps one runaway
|
|
98
108
|
// run from overshooting the daily cap. `aborted` is the budget-handback result
|
|
99
|
-
// (src/
|
|
109
|
+
// (src/runner/runner-recovery.ts) and schedules the resume promptly.
|
|
100
110
|
if (!pi.ok && ctx.role !== DIRECTOR_ROLE && pi.budgetCapped) {
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
// applyTickOutcome runs, so without this the slot is detached and the edits are lost.
|
|
105
|
-
s.resumePending = true;
|
|
106
|
-
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
107
|
-
return { result: "aborted", resumeCause: "budget-capped" };
|
|
111
|
+
// The per-run cap is a handback, not a failure: arm the resume (shared with the
|
|
112
|
+
// subscription-rejected arm above) so the next run hands off over the kept worktree.
|
|
113
|
+
return abortWithResume(s, ctx, pi, "claude per-run budget cap reached", "aborted", "budget-capped");
|
|
108
114
|
}
|
|
109
115
|
// A run the runner killed the moment its agent read an image its model cannot see
|
|
110
|
-
// (plans/image-input.md "Failing the
|
|
116
|
+
// (plans/image-input.md "Failing the run"): pi strips such an image from the request, so
|
|
111
117
|
// the agent was about to answer about a picture it never saw. The run was deliberately
|
|
112
118
|
// stopped before it could work on, so its half-done edits are discarded exactly as a
|
|
113
119
|
// timed-out run's are — nothing to resume, and the prompt re-queues to run fresh on a
|
|
@@ -115,9 +121,9 @@ export async function resolveTickVerdict(ctx) {
|
|
|
115
121
|
// as for any error.
|
|
116
122
|
if (pi.imageBlind) {
|
|
117
123
|
// One hop only (plans/image-input.md "Rerunning on a model that can read images"): a
|
|
118
|
-
//
|
|
119
|
-
// rerouted a second time — it fails, so a misreadable image cannot bounce the
|
|
120
|
-
// between tiers forever. Set at
|
|
124
|
+
// run that ALREADY started on the image-capable target and still read blind is not
|
|
125
|
+
// rerouted a second time — it fails, so a misreadable image cannot bounce the role
|
|
126
|
+
// between tiers forever. Set at run start by runPhase when it routes a run onto the
|
|
121
127
|
// target (blind-read handoff or attachment preflight).
|
|
122
128
|
if (s.imageRerouteActive) {
|
|
123
129
|
s.lastError = pi.errorMessage ?? `read ${pi.imageBlind.path}, but the model cannot read images`;
|
|
@@ -128,17 +134,19 @@ export async function resolveTickVerdict(ctx) {
|
|
|
128
134
|
return { result: "error" };
|
|
129
135
|
}
|
|
130
136
|
// Otherwise, when the role's tier has an image-capable reroute target, the failed work
|
|
131
|
-
// is not discarded: the next
|
|
137
|
+
// is not discarded: the next run starts directly on that target over the kept worktree.
|
|
132
138
|
// The one-shot field carries the target seam plus the `from`/`path` facts the handoff
|
|
133
|
-
// note needs; the
|
|
134
|
-
//
|
|
139
|
+
// note needs; the run ends `aborted` with the `image-reroute` resume cause, and
|
|
140
|
+
// applyRunOutcome schedules it promptly (nextRunAt = now) while planRunStart hands the
|
|
135
141
|
// kept worktree off to a FRESH session on the target.
|
|
136
142
|
const regime = ctx.rerouteRegime ?? "primary";
|
|
137
|
-
const target = ctx.config !== undefined ? imageReroute(ctx.config, ctx.role, regime
|
|
143
|
+
const target = ctx.config !== undefined ? imageReroute(ctx.config, ctx.role, regime, undefined, null, undefined, null, readPiListedModels(ctx.root))
|
|
144
|
+
: null;
|
|
138
145
|
if (target) {
|
|
139
146
|
const from = ctx.runModel ?? target.config.model ?? "the model";
|
|
140
147
|
s.imageReroute = {
|
|
141
148
|
tier: target.tier,
|
|
149
|
+
backend: target.config.agent ?? "pi",
|
|
142
150
|
provider: target.config.provider,
|
|
143
151
|
model: target.config.model,
|
|
144
152
|
thinking: target.config.thinking,
|
|
@@ -180,6 +188,7 @@ export async function resolveTickVerdict(ctx) {
|
|
|
180
188
|
root: ctx.root,
|
|
181
189
|
mainBranch: ctx.mainBranch,
|
|
182
190
|
turns: ctx.turns,
|
|
191
|
+
modelLine: ctx.modelLine,
|
|
183
192
|
merge: (w, sum) => ctx.merge(w, sum),
|
|
184
193
|
}, s, ctx.wt, pi);
|
|
185
194
|
}
|
|
@@ -189,16 +198,16 @@ export async function resolveTickVerdict(ctx) {
|
|
|
189
198
|
// 4xx config error, an auth failure, or a dead backend ends on an assistant message_end with
|
|
190
199
|
// empty `content` and stopReason "error" — one completed turn, zero content — so
|
|
191
200
|
// `producedAssistantContent` stays false, while a run that did real work (tool calls, text)
|
|
192
|
-
// before failing sets it true. On a resume the dirty files are the interrupted
|
|
201
|
+
// before failing sets it true. On a resume the dirty files are the interrupted run's
|
|
193
202
|
// half-done edits, which the abort branch above deliberately refuses to commit; staging them
|
|
194
203
|
// would ship unfinished work under a diff-derived subject and drop the resumable session
|
|
195
204
|
// (BUGS.md 2026-10-06). Keep the worktree and session for the next resume.
|
|
196
205
|
if (!pi.ok && (!changed || !pi.producedAssistantContent)) {
|
|
197
206
|
s.lastError = pi.errorMessage ?? "the agent failed";
|
|
198
|
-
// A dirty worktree on a run that added nothing of its own is a prior
|
|
199
|
-
// resume so the next
|
|
200
|
-
//
|
|
201
|
-
// rather than leaving a duplicate for a later fresh
|
|
207
|
+
// A dirty worktree on a run that added nothing of its own is a prior run's work: arm the
|
|
208
|
+
// resume so the next run continues that session instead of resetting the edits. A role
|
|
209
|
+
// runner re-queues through requeueForResume, so the resume reclaims exactly this prompt
|
|
210
|
+
// rather than leaving a duplicate for a later fresh run to run again (BUGS.md 2026-09-25);
|
|
202
211
|
// the director never resumes, so its prompt always re-runs fresh. A clean worktree has
|
|
203
212
|
// nothing to carry forward, so the plain re-queue still applies.
|
|
204
213
|
if (changed && ctx.role !== DIRECTOR_ROLE) {
|
|
@@ -213,14 +222,14 @@ export async function resolveTickVerdict(ctx) {
|
|
|
213
222
|
}
|
|
214
223
|
return { result: "error" };
|
|
215
224
|
}
|
|
216
|
-
// A revision
|
|
225
|
+
// A revision run that declares nothing-to-do drops the rejected change (the revise-rejected plan
|
|
217
226
|
// in PLANS.md): the author judged the objections show the change should not exist, so reset the
|
|
218
227
|
// re-applied diff away, delete the rejected ref, and end no_change without staging anything.
|
|
219
|
-
// Guarded on the
|
|
228
|
+
// Guarded on the run's own revisionRound, not state.revision: a dirty user-request run that
|
|
220
229
|
// happens to leave a pending revision untouched must not drop it. Checked before the
|
|
221
230
|
// clean-worktree branch because the re-applied diff can leave the worktree clean — an empty
|
|
222
231
|
// cherry-pick range, or the author reverting the edits while deciding — and the author's
|
|
223
|
-
// NOTHING_TO_DO must end the change anyway, or every later
|
|
232
|
+
// NOTHING_TO_DO must end the change anyway, or every later run re-applies the same rejected
|
|
224
233
|
// diff.
|
|
225
234
|
if (ctx.revisionRound !== undefined && s.revision && pi.nothingToDo) {
|
|
226
235
|
const { sha } = s.revision;
|
|
@@ -229,7 +238,7 @@ export async function resolveTickVerdict(ctx) {
|
|
|
229
238
|
await deleteRef(ctx.root, rejectedRefName(ctx.role));
|
|
230
239
|
// The drop's durable effects — the worktree reset, the rejected ref's deletion, and the
|
|
231
240
|
// cleared state — have all taken effect; the event is observability, so an unwritable feed must
|
|
232
|
-
// not end the
|
|
241
|
+
// not end the run as an error and mask a change that really was dropped.
|
|
233
242
|
logEventBestEffort(ctx.root, { source: ctx.role, type: "revision", action: "dropped", round: ctx.revisionRound, sha });
|
|
234
243
|
return { result: "no_change" };
|
|
235
244
|
}
|
|
@@ -243,20 +252,46 @@ export async function resolveTickVerdict(ctx) {
|
|
|
243
252
|
(diagnosis.notes.length ? ` (${diagnosis.notes.join(", ")})` : ""));
|
|
244
253
|
}
|
|
245
254
|
// A cut-off run did real work and was NOT fulfilled: a director prompt goes back
|
|
246
|
-
// to the inbox to rerun fresh; a role
|
|
247
|
-
// next
|
|
255
|
+
// to the inbox to rerun fresh; a role runner resumes the just-compacted session
|
|
256
|
+
// next run (see the cutOff handling in run()).
|
|
248
257
|
if (diagnosis.cutOff)
|
|
249
258
|
ctx.pending.requeueForResume(s, ctx.userPrompt);
|
|
250
259
|
// A cut-off run did real work but was truncated before declaring its outcome: the FLOW
|
|
251
260
|
// line it left mid-stream is not a finished verdict, so recording it would advance the
|
|
252
261
|
// rotation past a check that did not complete. Only a run that was not cut off records.
|
|
253
|
-
if (ctx.flow && !diagnosis.cutOff)
|
|
254
|
-
|
|
255
|
-
|
|
262
|
+
if (ctx.flow && !diagnosis.cutOff) {
|
|
263
|
+
// The ledger only orders the qa menu, so a failed write warns rather than failing the run.
|
|
264
|
+
try {
|
|
265
|
+
recordFlow(ctx.root, ctx.flow.flow, ctx.flow.result, ctx.flow.result === "bug" ? extractSummary(pi.finalText) ?? undefined : undefined);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
ctx.warn(`qa flow "${ctx.flow.flow}" not recorded: ${errorMessage(err)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// A fulfilled feedback run that filed records its per-install ledger entry and logs the
|
|
272
|
+
// issue_filed event so the filing is visible in the operator's activity feed. Recording is
|
|
273
|
+
// bookkeeping that never fails the run: a failed ledger write warns and the no_change
|
|
274
|
+
// outcome stands. A cut-off run records nothing — truncation is not a filing the run
|
|
275
|
+
// declared (the same guard the FLOW line carries).
|
|
276
|
+
if (ctx.filed && !diagnosis.cutOff) {
|
|
277
|
+
logEventBestEffort(ctx.root, {
|
|
278
|
+
source: ctx.role,
|
|
279
|
+
type: "issue_filed",
|
|
280
|
+
issue: ctx.filed.issue,
|
|
281
|
+
title: ctx.filed.title,
|
|
282
|
+
});
|
|
283
|
+
try {
|
|
284
|
+
recordFiled(ctx.root, ctx.filed.issue, ctx.filed.title);
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
ctx.warn(`feedback filing #${ctx.filed.issue} not recorded: ${errorMessage(err)}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// A prompt-driven (director) run that answers a question in prose ends `no_change` with
|
|
256
291
|
// no SUMMARY block, so its reply would be lost — the dashboard reads a bare "No change"
|
|
257
292
|
// and the feed carries nothing (BUGS.md 2026-10-14). Keep the prose above the sentinel as
|
|
258
|
-
// the
|
|
259
|
-
// operator asked the question. A role
|
|
293
|
+
// the run's summary so `lastSummary` and the run_end event show the answer where the
|
|
294
|
+
// operator asked the question. A role runner's bare nothing-to-do stays summary-less.
|
|
260
295
|
const summary = ctx.userPrompt !== null && !diagnosis.cutOff
|
|
261
296
|
? (extractNoChangeAnswer(pi.finalText) ?? undefined)
|
|
262
297
|
: undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** The `selfcheck` and `feedback` observers' run-time evidence: the failure digest rendered
|
|
2
|
+
* over the trailing 24 h window both share. Split out of src/failure/failure-render.ts — which
|
|
3
|
+
* stays the pure renderer — so every observer role's evidence builder has its own module the way
|
|
4
|
+
* qa-coverage.ts and backlog-structure.ts do, and the run lifecycle (run-prompt.ts) injects
|
|
5
|
+
* it from there instead of reaching into a report module. */
|
|
6
|
+
import { DAY_MS } from "../text/datetime.js";
|
|
7
|
+
import { collectFailureReportSince } from "../failure/failure-data.js";
|
|
8
|
+
import { renderFailureMarkdown } from "../failure/failure-render.js";
|
|
9
|
+
import { guardrailWarnings } from "../baseline/main-guardrails.js";
|
|
10
|
+
/** The `selfcheck` and `feedback` observers' shared digest window: the trailing 24 h ending now,
|
|
11
|
+
* not the local calendar day. A calendar day starts at midnight, so a run at 06:32 would never
|
|
12
|
+
* see the previous evening. The CLI keeps the usage report's 14-day default; the role reads 24 h
|
|
13
|
+
* so a cluster re-surfaces only while it is live. The collector's equal-length prior window is
|
|
14
|
+
* what makes the delta line meaningful. */
|
|
15
|
+
export const SELFCHECK_DIGEST_MS = DAY_MS;
|
|
16
|
+
/** The `selfcheck` and `feedback` observers' run-time evidence: the failure digest rendered over
|
|
17
|
+
* their shared trailing 24 h window. A missing or corrupt log omits the block (undefined) and
|
|
18
|
+
* never fails the run — an observer must not break on bookkeeping (plans/telemetry-role.md). The
|
|
19
|
+
* window and the swallow-errors policy live with the digest, not in the run lifecycle that
|
|
20
|
+
* injects it. */
|
|
21
|
+
export function selfcheckDigest(root) {
|
|
22
|
+
let digest;
|
|
23
|
+
try {
|
|
24
|
+
digest = renderFailureMarkdown(collectFailureReportSince(root, SELFCHECK_DIGEST_MS));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
// The guardrails section is own-evidence extra that must never cost the digest: a throw in
|
|
30
|
+
// guardrailWarnings (an unreadable log or quarantine) drops only the section.
|
|
31
|
+
try {
|
|
32
|
+
const warnings = guardrailWarnings(root);
|
|
33
|
+
if (warnings.length === 0)
|
|
34
|
+
return digest;
|
|
35
|
+
return `${digest}\n## Fast merge guardrails\n${warnings.join("\n")}\n`;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return digest;
|
|
39
|
+
}
|
|
40
|
+
}
|