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,7 +1,6 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import { removeQuiet, unlinkAllMissingTolerant } from "../files/files.js";
|
|
3
|
+
import { dirEntryNames, removeQuiet, unlinkAllMissingTolerant } from "../files/files.js";
|
|
5
4
|
import { isJsonObject } from "../files/json-object.js";
|
|
6
5
|
import { roleInboxDir } from "../paths.js";
|
|
7
6
|
import { errorMessage, isNonBlankString } from "../text/text.js";
|
|
@@ -10,7 +9,7 @@ import { agree, plural } from "../text/phrases.js";
|
|
|
10
9
|
/** Images attached to queued prompts: a GUI drop or paste into the composer saves each image
|
|
11
10
|
* beside the prompt's queue file (same stem, an image extension — the `.md` filter in
|
|
12
11
|
* listQueueFiles never sees them) and appends one `[image attached: <absolute path>]` line per
|
|
13
|
-
* image to the queued text, so the receiving
|
|
12
|
+
* image to the queued text, so the receiving role's pi agent can view the file with its read
|
|
14
13
|
* tool. Validation lives here once; every surface (the GUI endpoints through submitRolePrompt,
|
|
15
14
|
* the requeue policy through stripVanishedImageReferences) shares it. */
|
|
16
15
|
/** The image extensions pi's read tool renders — exactly what an attachment may carry.
|
|
@@ -32,6 +31,17 @@ export function isPromptImageSibling(stem, entry) {
|
|
|
32
31
|
return ((rest.startsWith(".") && PROMPT_IMAGE_EXTENSIONS.includes(rest.toLowerCase())) ||
|
|
33
32
|
(/^-\d+\./.test(rest) && PROMPT_IMAGE_EXTENSIONS.includes(rest.slice(rest.indexOf(".")).toLowerCase())));
|
|
34
33
|
}
|
|
34
|
+
/** The image siblings of `stem` already in `dir`, as the collision set for a new image's name.
|
|
35
|
+
* A directory that is missing or unreadable yields an empty set: nothing there to collide
|
|
36
|
+
* with, and the write that follows reports the real failure, if any. */
|
|
37
|
+
function takenImageSiblings(dir, stem) {
|
|
38
|
+
const taken = new Set();
|
|
39
|
+
for (const entry of dirEntryNames(dir)) {
|
|
40
|
+
if (isPromptImageSibling(stem, entry))
|
|
41
|
+
taken.add(entry);
|
|
42
|
+
}
|
|
43
|
+
return taken;
|
|
44
|
+
}
|
|
35
45
|
/** Pick a file name with extension `ext` under `stem` that does not collide with any name
|
|
36
46
|
* already in `taken`, and record it: `<stem><ext>` when free, else the first
|
|
37
47
|
* `<stem>-<n><ext>` (n from 2 up) that is. The one home of the dedup-suffix rule
|
|
@@ -60,7 +70,7 @@ const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
|
60
70
|
/** The image-count rule: at most PROMPT_IMAGES_MAX_COUNT entries. Split out of
|
|
61
71
|
* promptImagesProblem so a surface holding the raw path list can refuse an over-long
|
|
62
72
|
* `--attach` list before reading or encoding a single file, with the count wording still the
|
|
63
|
-
* one definition
|
|
73
|
+
* one definition every raw-path surface renders. Null when the count is acceptable. */
|
|
64
74
|
export function promptImagesCountProblem(count) {
|
|
65
75
|
return count > PROMPT_IMAGES_MAX_COUNT
|
|
66
76
|
? `at most ${PROMPT_IMAGES_MAX_COUNT} images per prompt (got ${count})`
|
|
@@ -186,16 +196,7 @@ export function savePromptImages(root, role, queueFile, images) {
|
|
|
186
196
|
// that adds a `.png` to a prompt whose image is already `<stem>.png` lands as `<stem>-2.png`
|
|
187
197
|
// instead of overwriting (and orphaning the reference to) the existing file. A directory
|
|
188
198
|
// that does not exist yet leaves the set empty; the write below creates it.
|
|
189
|
-
const taken =
|
|
190
|
-
try {
|
|
191
|
-
for (const entry of fs.readdirSync(dir)) {
|
|
192
|
-
if (isPromptImageSibling(stem, entry))
|
|
193
|
-
taken.add(entry);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
catch {
|
|
197
|
-
// No inbox dir yet (or unreadable): the write below reports the real failure, if any.
|
|
198
|
-
}
|
|
199
|
+
const taken = takenImageSiblings(dir, stem);
|
|
199
200
|
const paths = [];
|
|
200
201
|
// The image whose write is in flight: it only joins `paths` once its write returns, so a
|
|
201
202
|
// write that throws after creating the file (ENOSPC, EIO) would otherwise be the one
|
|
@@ -237,7 +238,7 @@ export function imageReferenceLines(paths) {
|
|
|
237
238
|
* counterpart to imageReferenceLines. Used by the edit path to carry an attached prompt's
|
|
238
239
|
* references (and so their sibling files) over a text rewrite: an edit replaces the prompt's
|
|
239
240
|
* text, not its attachments, which travel with the prompt until a cancel or the fulfilling
|
|
240
|
-
*
|
|
241
|
+
* run's sweep removes them. A line that is not exactly the writer's shape is content, not a
|
|
241
242
|
* reference, and is ignored. */
|
|
242
243
|
export function imageReferencePaths(text) {
|
|
243
244
|
const paths = [];
|
|
@@ -250,7 +251,7 @@ export function imageReferencePaths(text) {
|
|
|
250
251
|
}
|
|
251
252
|
/** Move a re-queued prompt's attachments from its old queue file's stem to the new queue
|
|
252
253
|
* file's stem and rewrite their `[image attached: …]` lines, so an attachment always travels
|
|
253
|
-
* with the prompt's current queue file: the cancel sweep and the fulfilling
|
|
254
|
+
* with the prompt's current queue file: the cancel sweep and the fulfilling run's sweep
|
|
254
255
|
* (removeSameStemSiblings, keyed on that file's stem) then find it, and a requeue can never
|
|
255
256
|
* strand a file under a stem no queue file names. Only a line naming a same-stem sibling of
|
|
256
257
|
* `fromFile` inside its directory is treated as an attachment — a hand-typed or edited
|
|
@@ -259,23 +260,16 @@ export function imageReferencePaths(text) {
|
|
|
259
260
|
* referenced attachment that is already gone is left for stripVanishedImageReferences to drop
|
|
260
261
|
* and record. Each attachment moves on its own: one file that is gone or cannot be renamed is
|
|
261
262
|
* left in place and named in `problem`, while the attachments around it still move, so a
|
|
262
|
-
* single missing or unmovable file cannot strand the rest under a stem no queue file names.
|
|
263
|
+
* single missing or unmovable file cannot strand the rest under a stem no queue file names.
|
|
264
|
+
* `moves` lists each rename done, so a caller whose later write fails can move them back. */
|
|
263
265
|
export function movePromptImages(fromFile, toFile, text) {
|
|
264
266
|
const dir = path.resolve(path.dirname(fromFile));
|
|
265
267
|
const fromStem = path.basename(fromFile, ".md");
|
|
266
268
|
const toStem = path.basename(toFile, ".md");
|
|
267
269
|
const toDir = path.resolve(path.dirname(toFile));
|
|
268
|
-
const taken =
|
|
269
|
-
try {
|
|
270
|
-
for (const entry of fs.readdirSync(toDir)) {
|
|
271
|
-
if (isPromptImageSibling(toStem, entry))
|
|
272
|
-
taken.add(entry);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
catch {
|
|
276
|
-
// The queue directory is gone; nothing to collide with.
|
|
277
|
-
}
|
|
270
|
+
const taken = takenImageSiblings(toDir, toStem);
|
|
278
271
|
const problems = [];
|
|
272
|
+
const moves = [];
|
|
279
273
|
const out = [];
|
|
280
274
|
for (const line of text.split("\n")) {
|
|
281
275
|
const referenced = referencedImagePath(line);
|
|
@@ -306,9 +300,14 @@ export function movePromptImages(fromFile, toFile, text) {
|
|
|
306
300
|
out.push(line);
|
|
307
301
|
continue;
|
|
308
302
|
}
|
|
303
|
+
moves.push({ from: resolved, to: target });
|
|
309
304
|
out.push(line.replace(referenced, target));
|
|
310
305
|
}
|
|
311
|
-
return {
|
|
306
|
+
return {
|
|
307
|
+
text: out.join("\n"),
|
|
308
|
+
problem: problems.length > 0 ? problems.join("; ") : null,
|
|
309
|
+
moves,
|
|
310
|
+
};
|
|
312
311
|
}
|
|
313
312
|
/** The prompt text without its `[image attached: …]` reference lines, trailing blank lines
|
|
314
313
|
* trimmed — the editable body an editor should show. The reference block is plumbing the edit
|
|
@@ -322,8 +321,8 @@ export function stripImageReferenceLines(text) {
|
|
|
322
321
|
return kept.join("\n");
|
|
323
322
|
}
|
|
324
323
|
/** Drop the `[image attached: …]` lines whose files are no longer on disk from a prompt being
|
|
325
|
-
* re-queued after a
|
|
326
|
-
* so a re-queued copy would otherwise send the
|
|
324
|
+
* re-queued after a run whose attached image is gone (removed out of band, or by a cancel),
|
|
325
|
+
* so a re-queued copy would otherwise send the role's agent after files that are gone. Each
|
|
327
326
|
* vanished line is replaced by one honest note naming what
|
|
328
327
|
* happened; references whose files still exist are kept. Unchanged text comes back untouched
|
|
329
328
|
* (dropped: []) so the common no-images case costs nothing. */
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { logEventBestEffort } from "../events/events.js";
|
|
4
3
|
import { roleInboxDir } from "../paths.js";
|
|
@@ -7,8 +6,8 @@ import { gotSuffix } from "../text/text.js";
|
|
|
7
6
|
import { promptPreview, queuedFileAtPosition, queuedFiles, removeSameStemSiblings, takeQueuedFile } from "./inbox.js";
|
|
8
7
|
/** Read one addressed queue file and turn it into a cancel outcome — the one home of the
|
|
9
8
|
* race-policy-and-event pairing both cancel paths go through: { status: "gone" } when the
|
|
10
|
-
*
|
|
11
|
-
* a normal race, never an error), else one prompt_cancelled event under that
|
|
9
|
+
* role already dequeued or another cancel removed the file (takeQueuedFile's race policy,
|
|
10
|
+
* a normal race, never an error), else one prompt_cancelled event under that role (preview
|
|
12
11
|
* via promptPreview, exactly like the prompt_enqueued sibling) logged, best-effort, only
|
|
13
12
|
* after a successful removal — the removal is the durable effect, so an unwritable feed must
|
|
14
13
|
* not report the cancel as failed;
|
|
@@ -19,14 +18,14 @@ function takeCancelledPrompt(root, role, file) {
|
|
|
19
18
|
const text = takeQueuedFile(file);
|
|
20
19
|
if (text === null)
|
|
21
20
|
return { status: "gone" };
|
|
22
|
-
// A cancelled prompt's image attachments go with it; a dequeue leaves them for the
|
|
21
|
+
// A cancelled prompt's image attachments go with it; a dequeue leaves them for the run to
|
|
23
22
|
// read and they are removed on fulfillment (src/inbox/pending-prompt.ts).
|
|
24
23
|
removeSameStemSiblings(root, role, file);
|
|
25
24
|
logEventBestEffort(root, { source: role, type: "prompt_cancelled", preview: promptPreview(text) });
|
|
26
25
|
return { status: "cancelled", text };
|
|
27
26
|
}
|
|
28
|
-
/** Remove the Nth prompt queued for one
|
|
29
|
-
* and log one prompt_cancelled event under that
|
|
27
|
+
/** Remove the Nth prompt queued for one role — 1-based, as shown by `tumwater prompt --list` —
|
|
28
|
+
* and log one prompt_cancelled event under that role (preview via promptPreview, exactly like
|
|
30
29
|
* its prompt_enqueued sibling). Throws for out-of-range positions with no side effects; returns
|
|
31
30
|
* { status: "gone" } when the file disappears between listing and removal instead of throwing.
|
|
32
31
|
* The race policy and event live in takeCancelledPrompt. */
|
|
@@ -34,8 +33,8 @@ export function cancelRolePrompt(root, role, position) {
|
|
|
34
33
|
return takeCancelledPrompt(root, role, queuedFileAtPosition(root, role, position));
|
|
35
34
|
}
|
|
36
35
|
/** Forward a resolution to a list-wide command's outcome: a miss shape returns verbatim, a
|
|
37
|
-
* found one calls `found` with the resolved
|
|
38
|
-
* action stays lazy — it runs only for the single
|
|
36
|
+
* found one calls `found` with the resolved role (so the command's own side-effecting queue
|
|
37
|
+
* action stays lazy — it runs only for the single role that holds the position). Both
|
|
39
38
|
* list-wide commands — cancelListedPrompt and editListedPrompt — are the call sites. */
|
|
40
39
|
export function listedQueueOutcome(resolved, found) {
|
|
41
40
|
switch (resolved.status) {
|
|
@@ -47,7 +46,7 @@ export function listedQueueOutcome(resolved, found) {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
/** Resolve a list-wide 1-based position (a `prompt --cancel`/`--edit` with no --role) to the
|
|
50
|
-
* one
|
|
49
|
+
* one role whose queue is long enough to hold it, in `scope` order — the semantics the
|
|
51
50
|
* ListedQueueResolution doc above specifies. Sizes only: no queue content is read. */
|
|
52
51
|
export function resolveListedQueue(root, scope, position) {
|
|
53
52
|
const candidates = scope.filter((role) => queuedFiles(root, role).length >= position);
|
|
@@ -62,7 +61,7 @@ export function resolveListedQueue(root, scope, position) {
|
|
|
62
61
|
return { status: "found", role };
|
|
63
62
|
}
|
|
64
63
|
/** Cancel by the position numbering `tumwater prompt --list` prints with no --role: the
|
|
65
|
-
* position resolves through the shared resolveListedQueue (above), then one
|
|
64
|
+
* position resolves through the shared resolveListedQueue (above), then one role's queue
|
|
66
65
|
* takes the cancel (cancelRolePrompt's race policy and event). */
|
|
67
66
|
export function cancelListedPrompt(root, scope, position) {
|
|
68
67
|
const resolved = resolveListedQueue(root, scope, position);
|
|
@@ -73,7 +72,7 @@ export function cancelListedPrompt(root, scope, position) {
|
|
|
73
72
|
}));
|
|
74
73
|
}
|
|
75
74
|
/** The queue-file-name guard for a file-addressed cancel: a name arriving over HTTP is
|
|
76
|
-
* trusted only as a plain basename inside the
|
|
75
|
+
* trusted only as a plain basename inside the role's queue directory — anything containing a
|
|
77
76
|
* path separator or equal to `..` could name a file elsewhere on disk, a NUL byte is not a
|
|
78
77
|
* character any filename can hold (the fs layer throws on it rather than answering ENOENT,
|
|
79
78
|
* so the endpoint's 400 contract needs it rejected before anything touches the disk), and a
|
|
@@ -8,20 +8,20 @@ import { assertQueueFileName, listedQueueOutcome, resolveListedQueue } from "./i
|
|
|
8
8
|
import { notBeforeMs, notBeforeMarker, stripNotBeforeMarker } from "./prompt-not-before.js";
|
|
9
9
|
import { roleInboxDir } from "../paths.js";
|
|
10
10
|
/** Rewrite one queued prompt's file in place — the shared core behind editRolePrompt (address
|
|
11
|
-
* by per-
|
|
11
|
+
* by per-role position) and editQueuedFile (address by queue-file basename): same queue file
|
|
12
12
|
* (so the filename's enqueue stamp and the entry's position behind older prompts are
|
|
13
13
|
* untouched), same atomic write enqueueRolePrompt uses
|
|
14
14
|
* (writeTextAtomic's temp file + rename, so a concurrent dequeue or another edit can never
|
|
15
15
|
* expose a half-written file), and the existing not-before deferral carried over — when the
|
|
16
16
|
* old file starts with the marker, the new text is written under the same marker; a
|
|
17
17
|
* non-deferred prompt stays marker-free even if the new text's first line resembles a
|
|
18
|
-
* marker. Logs one prompt_edited event under that
|
|
18
|
+
* marker. Logs one prompt_edited event under that role (preview via promptPreview, exactly
|
|
19
19
|
* like its prompt_cancelled sibling) after a successful write, best-effort: the rewrite is
|
|
20
20
|
* the durable effect, so an unwritable feed must not report the edit as failed.
|
|
21
21
|
*
|
|
22
22
|
* Carries an attached prompt's `[image attached: …]` reference lines (and so their sibling
|
|
23
23
|
* files) over the rewrite — the edit replaces the prompt's text, not its attachments, which
|
|
24
|
-
* stay beside the edited queue file until a cancel or the fulfilling
|
|
24
|
+
* stay beside the edited queue file until a cancel or the fulfilling run's sweep removes
|
|
25
25
|
* them. An optional `images` array (the CLI's
|
|
26
26
|
* `prompt --edit … --attach`) appends its newly saved reference lines after the carried ones,
|
|
27
27
|
* so one command corrects the text and the images in place instead of cancel-and-re-queue;
|
|
@@ -29,7 +29,7 @@ import { roleInboxDir } from "../paths.js";
|
|
|
29
29
|
* new files again.
|
|
30
30
|
*
|
|
31
31
|
* Enforces the same prefill length cap as a submission (promptLengthProblem): the edited
|
|
32
|
-
* text plus its carried image lines ride into the target
|
|
32
|
+
* text plus its carried image lines ride into the target run's prefill exactly like a
|
|
33
33
|
* queued prompt, so an edit to an over-cap value must not bypass the cap every enqueue path
|
|
34
34
|
* applies. The initial text check throws before any read or write; the composed check (body
|
|
35
35
|
* plus carried image lines) throws before the write when the body alone fit.
|
|
@@ -38,7 +38,7 @@ import { roleInboxDir } from "../paths.js";
|
|
|
38
38
|
* the image-address form throws for a name failing queueFileNameProblem before touching disk.
|
|
39
39
|
* The race policy pairs with the outcome: the file is read first, and a read that hits ENOENT
|
|
40
40
|
* returns { status: "gone" } —
|
|
41
|
-
* the
|
|
41
|
+
* the role dequeued it or a cancel removed it between listing and write, a normal race
|
|
42
42
|
* exactly like takeCancelledPrompt's; any other read error propagates (a permission failure
|
|
43
43
|
* is not a race). The write itself is unconditional once the read succeeded: a dequeue that
|
|
44
44
|
* lands between that read and the rename is a narrower race than takeQueuedFile's own
|
|
@@ -78,7 +78,7 @@ function editPromptAtFile(root, role, file, newText, images) {
|
|
|
78
78
|
throw new Error(saved.problem);
|
|
79
79
|
savedImagePaths = saved.paths;
|
|
80
80
|
}
|
|
81
|
-
// The composed text is what a
|
|
81
|
+
// The composed text is what a run's prefill receives, so the submission cap covers the
|
|
82
82
|
// body plus every reference line; the carried paths stay first, the new ones append after
|
|
83
83
|
// them in one reference block.
|
|
84
84
|
composed = text + imageReferenceLines([...carriedPaths, ...savedImagePaths]);
|
|
@@ -96,7 +96,7 @@ function editPromptAtFile(root, role, file, newText, images) {
|
|
|
96
96
|
logEventBestEffort(root, { source: role, type: "prompt_edited", preview: promptPreview(composed) });
|
|
97
97
|
return { status: "edited", oldText, newText: composed };
|
|
98
98
|
}
|
|
99
|
-
/** Rewrite the Nth prompt queued for one
|
|
99
|
+
/** Rewrite the Nth prompt queued for one role — 1-based, as shown by `tumwater prompt
|
|
100
100
|
* --list` — in place, through the shared editPromptAtFile. See that helper for the full
|
|
101
101
|
* contract (deferral carry-over, attachment carry-over, the prompt_edited event, the race
|
|
102
102
|
* policy). */
|
|
@@ -118,7 +118,7 @@ export function editQueuedFile(root, role, name, newText, images) {
|
|
|
118
118
|
* edit affordance (PLANS.md 2026-10-11): stripNotBeforeMarker drops the deferral plumbing,
|
|
119
119
|
* stripImageReferenceLines drops the `[image attached: …]` reference block the edit carries
|
|
120
120
|
* over unchanged (so it is never shown in the editor and cannot be edited away), and trimEnd
|
|
121
|
-
* drops any blank lines left behind. null when the file is gone — the
|
|
121
|
+
* drops any blank lines left behind. null when the file is gone — the role dequeued it or
|
|
122
122
|
* another operator cancelled it, which the endpoint reports as { status: "gone" } rather than
|
|
123
123
|
* an error. Throws for a name failing queueFileNameProblem. Reading the one named file directly
|
|
124
124
|
* keeps the dashboard's lazy fetch off the per-poll path. */
|
|
@@ -131,7 +131,7 @@ export function readQueuedPromptBody(root, role, name) {
|
|
|
131
131
|
}
|
|
132
132
|
/** Edit by the position numbering `tumwater prompt --list` prints with no --role: the same
|
|
133
133
|
* candidate resolution cancelListedPrompt uses — resolveListedQueue, in inbox-cancel.ts, so
|
|
134
|
-
* the two cannot drift — followed by editRolePrompt in the resolved
|
|
134
|
+
* the two cannot drift — followed by editRolePrompt in the resolved role. One role holding
|
|
135
135
|
* the position resolves, several are an ambiguity the caller reports with a --role escape
|
|
136
136
|
* hatch, none is a miss carrying the largest queue length for the error's count. */
|
|
137
137
|
export function editListedPrompt(root, scope, position, newText, images) {
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import { enqueueRolePrompt, promptPreview } from "./inbox.js";
|
|
3
2
|
import { unlinkAllMissingTolerant } from "../files/files.js";
|
|
4
3
|
import { imageReferenceLines, promptImagesProblem, savePromptImages, } from "./inbox-attachments.js";
|
|
5
4
|
import { logEventBestEffort } from "../events/events.js";
|
|
6
|
-
import {
|
|
5
|
+
import { BRIEF_TEXT_MAX_CHARS } from "../brief/brief.js";
|
|
7
6
|
import { DIRECTOR_ROLE } from "../roles/roles.js";
|
|
8
7
|
import { tooLongMessage } from "../text/text.js";
|
|
9
8
|
import { defaultConfig, loadConfigSafe } from "../config/config.js";
|
|
10
9
|
import { roleImageInfo } from "../agent/image-input.js";
|
|
10
|
+
import { readPiListedModels } from "../pi/pi-listed-models.js";
|
|
11
11
|
import { readImageProbeCache } from "../doctor/image-probe.js";
|
|
12
12
|
import { roleLabel } from "../roles/role-labels.js";
|
|
13
13
|
/** The user-facing submission pipeline for the prompt queues (the store's mechanics —
|
|
14
14
|
* listing, peeking, dequeuing, cancelling, and their race policy — live in src/inbox/inbox.ts,
|
|
15
15
|
* and the image side of a submission in src/inbox/inbox-attachments.ts): the shared length cap,
|
|
16
16
|
* the validation, and the submit wrappers the TUI, GUI, and CLI all go through. */
|
|
17
|
-
/** Cap on a submitted prompt's length: the same ceiling for every
|
|
18
|
-
* role alike, because every prompt rides into its target
|
|
17
|
+
/** Cap on a submitted prompt's length: the same ceiling for every role's queue, director and
|
|
18
|
+
* role alike, because every prompt rides into its target run's prefill — a megabyte pasted
|
|
19
19
|
* into the TUI, a GUI POST, or a shell-mistaken `tumwater prompt $(cat …)` would otherwise
|
|
20
|
-
* ride into the next
|
|
20
|
+
* ride into the next run's context wholesale. submitPrompt and submitRolePrompt reject
|
|
21
21
|
* over-long text before it is queued, so no surface can enqueue it. */
|
|
22
|
-
export const DIRECTOR_PROMPT_MAX_CHARS =
|
|
23
|
-
/** The one length rule for a submitted prompt, scoped to the
|
|
22
|
+
export const DIRECTOR_PROMPT_MAX_CHARS = BRIEF_TEXT_MAX_CHARS;
|
|
23
|
+
/** The one length rule for a submitted prompt, scoped to the role it targets: the error
|
|
24
24
|
* message when the trimmed text exceeds DIRECTOR_PROMPT_MAX_CHARS, null when it fits. The
|
|
25
|
-
* message names the target
|
|
25
|
+
* message names the target role's run — `--role qa` must not be told its text rides into
|
|
26
26
|
* the director's prefill. submitPrompt/submitRolePrompt throw it before anything is queued
|
|
27
27
|
* or logged; the GUI asks it first so an over-long prompt answers 400 (a user-input error)
|
|
28
28
|
* while an unexpected submit failure (a broken inbox's EACCES) stays the 500 its
|
|
@@ -33,12 +33,12 @@ export function promptLengthProblem(text, role = DIRECTOR_ROLE) {
|
|
|
33
33
|
return null;
|
|
34
34
|
return tooLongMessage("the prompt", prompt.length, DIRECTOR_PROMPT_MAX_CHARS, `it rides into the ${roleLabel(role)} run's prefill`);
|
|
35
35
|
}
|
|
36
|
-
/** The image-input submit guidance for one
|
|
36
|
+
/** The image-input submit guidance for one role (plans/image-input.md "Surfaces" and
|
|
37
37
|
* "Attachments", part 5/5): what an image-carrying prompt to `role` means. `refusal` is the
|
|
38
|
-
* throw-and-submit-nothing message when the
|
|
39
|
-
* (an attachment no configured model can read is refused at submit, not discovered at
|
|
38
|
+
* throw-and-submit-nothing message when the role's seam reads no images and no peer tier can
|
|
39
|
+
* (an attachment no configured model can read is refused at submit, not discovered at run
|
|
40
40
|
* time); `notice` is what a surface prints/shows when the prompt will instead rerun on another
|
|
41
|
-
* tier. Both null when the
|
|
41
|
+
* tier. Both null when the role's own model reads images (or capability is unknown — an
|
|
42
42
|
* unknown seam is never assumed incapable, mirroring the doctor's `tier models` stance), so
|
|
43
43
|
* the common text-only or vision path says nothing. One resolver (roleImageInfo), shared with
|
|
44
44
|
* the status rows and role page, so the gate cannot disagree with what `tumwater role` or the
|
|
@@ -49,7 +49,7 @@ export function imagePromptGuidance(root, role) {
|
|
|
49
49
|
// The same probe cache the status rows, role page, and images alert read (roleImageInfo's
|
|
50
50
|
// fourth argument): a claimed pi model whose doctor probe failed reads text-only here too,
|
|
51
51
|
// so the gate never grants image capability that the surfaces report as absent.
|
|
52
|
-
const { imageInput, imageReroute } = roleImageInfo(cfg, role, undefined, readImageProbeCache(root));
|
|
52
|
+
const { imageInput, imageReroute } = roleImageInfo(cfg, role, undefined, readImageProbeCache(root), readPiListedModels(root));
|
|
53
53
|
if (imageInput !== false)
|
|
54
54
|
return {};
|
|
55
55
|
if (imageReroute) {
|
|
@@ -61,8 +61,8 @@ export function imagePromptGuidance(root, role) {
|
|
|
61
61
|
refusal: `${role}'s model can't read images and no configured model reads images for it — declare image input on a vision model in pi's models.json, or point a tier at one`,
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
-
/** A user submits a new prompt for one
|
|
65
|
-
* in the event log under that
|
|
64
|
+
/** A user submits a new prompt for one role (TUI, GUI, or CLI): enqueue it there and record it
|
|
65
|
+
* in the event log under that role, best-effort — the queue write is the durable effect the
|
|
66
66
|
* caller asked for, so an unwritable feed must not report the submit as failed. Returns the
|
|
67
67
|
* trimmed prompt that was queued. The logged
|
|
68
68
|
* preview goes through promptPreview — not a raw slice — so an over-long prompt is marked with
|
|
@@ -72,7 +72,7 @@ export function imagePromptGuidance(root, role) {
|
|
|
72
72
|
* operator. An optional images array (the GUI composer's drop/paste attachments) rides
|
|
73
73
|
* through savePromptImages beside the queue file, with one [image attached: …] reference line
|
|
74
74
|
* appended to the queued text; image problems throw before anything is queued — and so does
|
|
75
|
-
* an over-cap composed text: the reference lines ride into the
|
|
75
|
+
* an over-cap composed text: the reference lines ride into the run's prefill just like the
|
|
76
76
|
* prompt body, so the length cap is enforced on the final text (body + image lines), not the
|
|
77
77
|
* body alone.
|
|
78
78
|
* An optional `notBeforeMs` defers the prompt (PLANS.md "tumwater prompt --at <duration>"):
|
|
@@ -87,7 +87,7 @@ export function submitRolePrompt(root, role, text, images, notBeforeMs) {
|
|
|
87
87
|
throw new Error(imageProblem);
|
|
88
88
|
// Image input gate (plans/image-input.md "Attachments", part 5/5): an attachment no
|
|
89
89
|
// configured model can read is refused here, before anything is queued or written — the
|
|
90
|
-
//
|
|
90
|
+
// run would only fail discovering it. A role that reroutes a blind read is not refused;
|
|
91
91
|
// the surface prints/shows the notice instead (imagePromptGuidance is called again there).
|
|
92
92
|
const guidance = imagePromptGuidance(root, role);
|
|
93
93
|
if (guidance.refusal)
|
|
@@ -118,7 +118,7 @@ function submitPromptWithImages(root, role, text, images, notBeforeMs) {
|
|
|
118
118
|
throw new Error(saved.problem); // Unreachable: validated above.
|
|
119
119
|
savedImagePaths = saved.paths;
|
|
120
120
|
final = prompt + imageReferenceLines(saved.paths);
|
|
121
|
-
// The image reference lines ride into the target
|
|
121
|
+
// The image reference lines ride into the target run's prefill exactly like the
|
|
122
122
|
// prompt body, so the shared length cap applies to the composed text, not the body
|
|
123
123
|
// alone. This runs inside the decorate hook, before enqueueRolePrompt's one atomic
|
|
124
124
|
// write, so an over-cap composition queues nothing; the images saved above are
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, renameSync } from "node:fs";
|
|
4
3
|
import { logEventBestEffort, warnEventBestEffort } from "../events/events.js";
|
|
5
4
|
import { queueFileName, queueFileStamp, removeQueueFile } from "../files/file-queue.js";
|
|
6
5
|
import { readTextIfExists, writeTextAtomic } from "../files/files.js";
|
|
@@ -13,9 +12,11 @@ import { movePromptImages, stripVanishedImageReferences } from "./inbox-attachme
|
|
|
13
12
|
* `tumwater:not-before` marker, which rides in the text) byte-for-byte to the new stem, its
|
|
14
13
|
* image attachments moving to that new stem too (movePromptImages, with
|
|
15
14
|
* stripVanishedImageReferences dropping any already-gone reference) — and one `prompt_topped`
|
|
16
|
-
* event is logged under the
|
|
15
|
+
* event is logged under the role. Returns { status: "gone" } when the file disappears between
|
|
17
16
|
* the caller's listing and the read here (a concurrent pop is a normal race, never an
|
|
18
|
-
* error), sharing the store's takeQueuedFile convention.
|
|
17
|
+
* error), sharing the store's takeQueuedFile convention. A failed write of the new queue file
|
|
18
|
+
* moves the attachments back and rethrows, so the prompt stays queued at its old position with
|
|
19
|
+
* its images. */
|
|
19
20
|
function moveFileToTop(root, role, file) {
|
|
20
21
|
const text = readTextIfExists(file);
|
|
21
22
|
if (text === null)
|
|
@@ -40,18 +41,33 @@ function moveFileToTop(root, role, file) {
|
|
|
40
41
|
if (moved.problem !== null)
|
|
41
42
|
warnEventBestEffort(root, role, moved.problem);
|
|
42
43
|
const carried = stripVanishedImageReferences(moved.text).text;
|
|
43
|
-
|
|
44
|
+
try {
|
|
45
|
+
writeTextAtomic(newFile, carried);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// Put the attachments back under the old stem, so the prompt still queued there keeps its
|
|
49
|
+
// images; one failed rename must not stop the others or mask the write error.
|
|
50
|
+
for (const m of moved.moves) {
|
|
51
|
+
try {
|
|
52
|
+
renameSync(m.to, m.from);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Best effort: the write error is the one the caller sees.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
44
60
|
removeQueueFile(file);
|
|
45
61
|
logEventBestEffort(root, { source: role, type: "prompt_topped", preview: promptPreview(carried) });
|
|
46
62
|
return { status: "topped", preview: promptPreview(carried) };
|
|
47
63
|
}
|
|
48
|
-
/** Move the Nth prompt queued for one
|
|
49
|
-
* to the head of its queue, so the
|
|
64
|
+
/** Move the Nth prompt queued for one role — 1-based, as shown by `tumwater prompt --list` —
|
|
65
|
+
* to the head of its queue, so the role dequeues it next. Inbox order is the queue-file name
|
|
50
66
|
* (`queueFileName`'s timestamp stamp), so the only way to make one prompt next is to give it a
|
|
51
67
|
* stamp one below the queue's minimum; the file's text (and its `tumwater:not-before` marker,
|
|
52
68
|
* which rides in the text) is carried byte-for-byte to the new stem, its image attachments
|
|
53
69
|
* move to that new stem too (movePromptImages, with stripVanishedImageReferences dropping any
|
|
54
|
-
* already-gone reference), and one `prompt_topped` event is logged under the
|
|
70
|
+
* already-gone reference), and one `prompt_topped` event is logged under the role.
|
|
55
71
|
*
|
|
56
72
|
* Throws for out-of-range positions and for position 1 ("already first"), both with no side
|
|
57
73
|
* effects — the moveEntryToTop precedent. Returns { status: "gone" } when the file disappears
|
|
@@ -68,7 +84,7 @@ export function movePromptToTop(root, role, position) {
|
|
|
68
84
|
* queue — the file-addressed twin of movePromptToTop, for the dashboard's per-row Move to
|
|
69
85
|
* top affordance: the address is the file itself, so a stale poll can never reorder the
|
|
70
86
|
* wrong prompt. Throws for a name failing queueFileNameProblem (re-checked through
|
|
71
|
-
* assertQueueFileName, so a caller that skips the guard fails closed); a file the
|
|
87
|
+
* assertQueueFileName, so a caller that skips the guard fails closed); a file the role
|
|
72
88
|
* already dequeued answers "gone", and the queue head answers "already_first" with no write.
|
|
73
89
|
* Reuses moveFileToTop's rename-and-event pairing, so both address forms cannot drift. */
|
|
74
90
|
export function topQueuedFile(root, role, name) {
|