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,25 +1,26 @@
|
|
|
1
|
-
// vocabulary: plain
|
|
2
1
|
/** The dashboard's detail drawer, browser-side: a sheet on the right that opens over the page
|
|
3
|
-
* without losing it. For a
|
|
2
|
+
* without losing it. For a role it shows the state, the role's controls (prompt, wake now or later,
|
|
4
3
|
* pause/resume, abort), what it is working on, its last result, the metrics the team table
|
|
5
|
-
* leaves out (tokens, context, cost, timings), its recent
|
|
6
|
-
*
|
|
7
|
-
* to the bottom while the reader is there
|
|
8
|
-
*
|
|
4
|
+
* leaves out (tokens, context, cost, timings), its recent runs (/api/history filtered to the
|
|
5
|
+
* role), and its live transcript (/api/transcript, re-fetched on the 1 s poll and kept pinned
|
|
6
|
+
* to the bottom while the reader is there; a "Show prompts" button adds each agent run's prompt
|
|
7
|
+
* with `&prompt=1`; a search box narrows it with `&grep=`). For a backlog entry it shows the entry
|
|
8
|
+
* in full (/api/backlog), rendered as Markdown, with a shortcut into the composer — answering a
|
|
9
9
|
* question, or asking the director about a plan or bug — and, for a plan or bug, a two-click
|
|
10
10
|
* Cancel that removes the entry (/api/backlog-cancel). Esc, the close button, or clicking the
|
|
11
11
|
* same row again closes it. Spliced into gui-client.ts's script, reaching its helpers through
|
|
12
12
|
* that concatenation. */
|
|
13
|
-
export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind: "
|
|
14
|
-
let
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
let
|
|
13
|
+
export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind: "role", role } | { kind: "entry", file, index } while open
|
|
14
|
+
let drawerRuns = null; // the open role's recent history rows (null until fetched)
|
|
15
|
+
let drawerRunsAt = 0;
|
|
16
|
+
let drawerChangeStat = false;
|
|
17
|
+
let drawerChange = null; // the open role's unmerged change: { role, state: "loading"|"ok"|"error", view, message }
|
|
18
|
+
let drawerRole = null; // the open role's /api/role payload: { role, state: "loading"|"ok"|"error", view, message }
|
|
18
19
|
const openEntryKey = () => (drawer && drawer.kind === "entry" ? drawer.file + ":" + drawer.index : null);
|
|
19
|
-
const
|
|
20
|
-
// One key/value row builder shared by the
|
|
21
|
-
// Configuration section (drawerConfigHtml): identical markup,
|
|
22
|
-
// local consts (kv and row) so the row shape could drift
|
|
20
|
+
const openRoleId = () => (drawer && drawer.kind === "role" ? drawer.role : null);
|
|
21
|
+
// One key/value row builder shared by the role-metrics table (renderRoleDrawer) and the
|
|
22
|
+
// Configuration section (drawerConfigHtml, in gui-client-drawer-config.ts): identical markup,
|
|
23
|
+
// formerly copy-pasted into two local consts (kv and row) so the row shape could drift.
|
|
23
24
|
const kvRow = (k, v, title) => "<div><dt>" + esc(k) + "</dt><dd title='" + esc(title || v) + "'>" + esc(v) + "</dd></div>";
|
|
24
25
|
// A diff block in the Change section (the committed change and the dirty-tree uncommitted
|
|
25
26
|
// diff, drawerChangeHtml's two arms): trim the trailing newline the diff carriage-returned,
|
|
@@ -35,7 +36,7 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
35
36
|
$("scrim").hidden = false;
|
|
36
37
|
}
|
|
37
38
|
function closeDrawer() {
|
|
38
|
-
const
|
|
39
|
+
const wasRole = openRoleId();
|
|
39
40
|
drawer = null;
|
|
40
41
|
entryEditing = null;
|
|
41
42
|
noteEditing = null;
|
|
@@ -46,8 +47,8 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
46
47
|
instancesEditing = null;
|
|
47
48
|
modelEditing = null;
|
|
48
49
|
disarmNoteClear();
|
|
49
|
-
if (
|
|
50
|
-
|
|
50
|
+
if (wasRole && (location.hash.startsWith("#role/") || location.hash.startsWith("#loop/"))) history.replaceState(null, "", "#" + activeView); // vocabulary-ok: legacy dashboard hash, still routed
|
|
51
|
+
drawerRuns = null;
|
|
51
52
|
drawerChange = null;
|
|
52
53
|
drawerRole = null;
|
|
53
54
|
$("drawer").hidden = true;
|
|
@@ -58,29 +59,36 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
58
59
|
if (lastStatus) renderTeam(lastStatus); // drop the row highlight
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
let transcriptPrompts = false;
|
|
63
|
+
let transcriptGrep = "";
|
|
64
|
+
function openRole(role) {
|
|
65
|
+
drawer = { kind: "role", role };
|
|
66
|
+
transcriptPrompts = false;
|
|
67
|
+
drawerChangeStat = false;
|
|
68
|
+
transcriptGrep = "";
|
|
63
69
|
const hash = "#role/" + encodeURIComponent(role);
|
|
64
70
|
if (location.hash !== hash) history.replaceState(null, "", hash);
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
drawerRuns = null;
|
|
72
|
+
drawerRunsAt = 0;
|
|
67
73
|
drawerChange = null;
|
|
68
74
|
drawerRole = null;
|
|
69
75
|
showDrawer("<div id='draweractions' class='drawer-actions'></div><div id='drawermeta'></div><div id='drawerrole'></div><section class='transcript-sec'><div class='sec-head'><h3>Live transcript</h3>" +
|
|
70
|
-
"<
|
|
76
|
+
"<input type='search' id='transcriptgrep' class='field' placeholder='Search transcript' aria-label='Search transcript' autocomplete='off' style='margin-left:auto'>" +
|
|
77
|
+
"<button type='button' class='btn btn-sm' id='transcriptprompts' data-transcript-prompts style='margin-left:8px'>Show prompts</button>" +
|
|
78
|
+
"<span class='muted' style='margin-left:12px;font-size:13px'>last 200 lines</span></div>" +
|
|
71
79
|
"<pre id='transcript' class='transcript'><span class='muted'>Loading…</span></pre></section>");
|
|
72
80
|
if (lastStatus) {
|
|
73
|
-
|
|
81
|
+
renderRoleDrawer(lastStatus);
|
|
74
82
|
renderTeam(lastStatus);
|
|
75
83
|
}
|
|
76
|
-
|
|
84
|
+
loadRoleRuns(true);
|
|
77
85
|
loadDrawerChange(role);
|
|
78
86
|
loadDrawerRole(role);
|
|
79
87
|
refreshTranscript(true);
|
|
80
88
|
}
|
|
81
|
-
function
|
|
82
|
-
if (
|
|
83
|
-
else
|
|
89
|
+
function toggleRole(role) {
|
|
90
|
+
if (openRoleId() === role) closeDrawer();
|
|
91
|
+
else openRole(role);
|
|
84
92
|
}
|
|
85
93
|
function openEntry(file, index) {
|
|
86
94
|
drawer = { kind: "entry", file, index: Number(index) };
|
|
@@ -101,8 +109,8 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
101
109
|
return change && change.summary ? change.summary : "";
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
function
|
|
105
|
-
const role =
|
|
112
|
+
function renderRoleDrawer(d) {
|
|
113
|
+
const role = openRoleId();
|
|
106
114
|
if (!role) return;
|
|
107
115
|
const l = (d.roles || []).find((x) => x.role === role);
|
|
108
116
|
if (!l) {
|
|
@@ -114,7 +122,7 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
114
122
|
return;
|
|
115
123
|
}
|
|
116
124
|
const info = phaseInfo(l.phase);
|
|
117
|
-
const paused = (d.pausedLoops || []).includes(l.role);
|
|
125
|
+
const paused = (d.pausedLoops || []).includes(l.role); // retired-name-ok: serialized /api key
|
|
118
126
|
const detail = phaseDetail(l, info, d);
|
|
119
127
|
paintPanel("drawerhead", "<div class='kicker'>Role" + (l.custom ? " · user-defined" : "") + "</div>" +
|
|
120
128
|
"<h2 id='drawertitle' class='mono'>" + esc(roleLabel(l.role)) + "</h2><div class='chips'>" + pill(info) +
|
|
@@ -124,7 +132,7 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
124
132
|
"' data-role='" + esc(l.role) + "'>" + icon(ic) + esc(label) + "</button>";
|
|
125
133
|
let actions = btn("prompt", "chat", "Message this role");
|
|
126
134
|
// The running set (info.live), not the permit-holder inFlight: the exempt running director
|
|
127
|
-
// has an abortable
|
|
135
|
+
// has an abortable run without holding a maxConcurrent permit (BUGS.md 2026-10-06).
|
|
128
136
|
if (info.live) actions += abortConfirming(l.role) ? btn("abort", "stop", "Click again to stop", "btn-danger confirming") : btn("abort", "stop", "Stop this run", "btn-danger");
|
|
129
137
|
else {
|
|
130
138
|
actions += btn("wake", "bolt", "Wake now");
|
|
@@ -163,17 +171,17 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
163
171
|
: next === "now" || next.startsWith("now ") ? next
|
|
164
172
|
: next.startsWith("sleeping ") ? "in " + next.slice(9) + " (sleeping)"
|
|
165
173
|
: "in " + next;
|
|
166
|
-
const
|
|
174
|
+
const runWord = info.live ? "this run" : "last run";
|
|
167
175
|
const stats = "<dl class='kv'>" + kvRow("Commits", String(l.commits)) + kvRow("Runs", String(l.runs)) +
|
|
168
|
-
kvRow("Tokens, " +
|
|
176
|
+
kvRow("Tokens, " + runWord, fmtTokens(l.generated)) + kvRow("Peak context, " + runWord, fmtTokens(l.peakCtx)) +
|
|
169
177
|
kvRow("Spent today", fmtUsd(l.todayUsd)) + kvRow("Spent in total", fmtUsd(l.costUsd)) +
|
|
170
|
-
kvRow("Last run ended", l.lastRunEndedAt ? fmtAgo(l.lastRunEndedAt) : "never",
|
|
178
|
+
kvRow("Last run ended", l.lastRunEndedAt ? fmtAgo(l.lastRunEndedAt) : "never", fmtLastRun(l.lastRunEndedAt)) +
|
|
171
179
|
kvRow("Last merged", l.lastMergedAt ? fmtAgo(l.lastMergedAt) : "never") +
|
|
172
180
|
kvRow("Next run", nextNote) + "</dl>";
|
|
173
|
-
const
|
|
181
|
+
const runs = drawerRuns === null
|
|
174
182
|
? "<p class='muted'>Loading…</p>"
|
|
175
|
-
:
|
|
176
|
-
? "<ul class='
|
|
183
|
+
: drawerRuns.length
|
|
184
|
+
? "<ul class='run-list'>" + drawerRuns.map((t) => {
|
|
177
185
|
const res = resultInfo(t.result);
|
|
178
186
|
return "<li><span class='when' title='" + esc(t.time) + "'>#" + esc(t.run) + " · " + esc(fmtAgo(t.ts)) + "</span>" +
|
|
179
187
|
"<span class='clamp1' title='" + esc(t.detail) + "'><span class='res t-" + res.tone + "'>" + esc(res.label) + "</span> " + esc(t.detail) + "</span>" +
|
|
@@ -181,16 +189,17 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
181
189
|
}).join("") + "</ul>"
|
|
182
190
|
: "<p class='muted'>No completed runs in the log yet.</p>";
|
|
183
191
|
paintPanel("draweractions", actions);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
//
|
|
188
|
-
//
|
|
192
|
+
const queued = drawerQueueHtml(d.mergeQueue, l.role, Date.now(), d.mergeQueueLabels);
|
|
193
|
+
paintPanel("drawermeta", now + last + queued + stats +
|
|
194
|
+
"<section><div class='sec-head'><h3>Recent runs</h3></div>" + runs + "</section>" + drawerChangeSection());
|
|
195
|
+
// The notes/prompt live in their own panel, outside drawermeta: the poll rewrites
|
|
196
|
+
// drawermeta whenever its countdown or run-age text changes, and an innerHTML rewrite
|
|
197
|
+
// would collapse an opened Next run prompt. This panel's markup is stable while its
|
|
189
198
|
// payload is unchanged, so paintPanel leaves it (and the disclosure's open state) alone.
|
|
190
199
|
paintPanel("drawerrole", drawerRoleSection());
|
|
191
200
|
}
|
|
192
201
|
|
|
193
|
-
// The open
|
|
202
|
+
// The open role's unmerged change, as /api/diff?role serves it: the full patch the CLI's
|
|
194
203
|
// tumwater diff --role prints. Degrades like the collector — a missing worktree, a missing
|
|
195
204
|
// base branch, or no work each get a plain statement, and an unreadable payload says so
|
|
196
205
|
// rather than rendering a half-view. Pure of side effects; the view paints what it returns.
|
|
@@ -200,10 +209,15 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
200
209
|
const dirty = Array.isArray(view.dirtyFiles) ? view.dirtyFiles : [];
|
|
201
210
|
if (view.state === "absent") return "<p class='muted'>No workspace yet — this role has not run.</p>";
|
|
202
211
|
if (view.state === "no-base") return "<p class='muted'>Main branch " + esc(view.mainBranch) + " does not exist.</p>";
|
|
212
|
+
const scoped = typeof view.path === "string";
|
|
213
|
+
if (scoped && view.state === "ready" && commits.length === 0 && dirty.length === 0) {
|
|
214
|
+
return "<p class='muted'>No pending change to " + esc(view.path) + ".</p>";
|
|
215
|
+
}
|
|
203
216
|
if (view.state !== "ready" || (view.ahead === 0 && dirty.length === 0)) return "<p class='muted'>No unlanded work.</p>";
|
|
204
217
|
let html = "<p class='note'>" + esc(view.branch) + " · " + esc(plural(view.ahead, "commit")) +
|
|
205
218
|
" ahead of " + esc(view.mainBranch) +
|
|
206
|
-
(dirty.length ? " · " + esc(plural(dirty.length, "uncommitted file")) : "") +
|
|
219
|
+
(dirty.length ? " · " + esc(plural(dirty.length, "uncommitted file")) : "") +
|
|
220
|
+
(scoped ? " · limited to " + esc(view.path) : "") + "</p>";
|
|
207
221
|
html += commitRowsHtml(commits);
|
|
208
222
|
if (view.diff) html += diffPre(view.diff);
|
|
209
223
|
if (dirty.length) {
|
|
@@ -218,37 +232,43 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
218
232
|
if (!c || c.state === "loading") body = "<p class='muted'>Loading…</p>";
|
|
219
233
|
else if (c.state === "error") body = "<p class='muted'>" + esc(c.message || "Pending change unavailable") + "</p>";
|
|
220
234
|
else body = drawerChangeHtml(c.view);
|
|
221
|
-
return "<section><div class='sec-head'><h3>Pending change</h3
|
|
235
|
+
return "<section><div class='sec-head'><h3>Pending change</h3>" +
|
|
236
|
+
"<button type='button' class='btn btn-sm' data-change-stat style='margin-left:auto'>" + (drawerChangeStat ? "Full patch" : "Stat only") + "</button></div>" + body + "</section>";
|
|
222
237
|
}
|
|
223
238
|
// Fetch one drawer section's payload once when the drawer opens, storing it in the
|
|
224
|
-
// caller's state cell. A stale answer (the drawer moved to another
|
|
239
|
+
// caller's state cell. A stale answer (the drawer moved to another role) is dropped;
|
|
225
240
|
// later status polls repaint from the stored view. The assign callback writes that cell
|
|
226
241
|
// (drawerChange / drawerRole), so the loading/ok/error contract lives here once for both
|
|
227
|
-
// the pending-change and
|
|
242
|
+
// the pending-change and notes sections.
|
|
228
243
|
async function loadDrawerSection(role, url, fallbackMessage, assign) {
|
|
229
244
|
if (!role) return;
|
|
230
245
|
assign({ role, state: "loading" });
|
|
231
246
|
try {
|
|
232
247
|
const d = await getJson(url + encodeURIComponent(role), pollSignal());
|
|
233
|
-
if (
|
|
248
|
+
if (openRoleId() !== role) return;
|
|
234
249
|
assign({ role, state: "ok", view: d });
|
|
235
250
|
} catch (err) {
|
|
236
|
-
if (
|
|
251
|
+
if (openRoleId() !== role) return;
|
|
237
252
|
assign({ role, state: "error", message: err && err.message ? err.message : fallbackMessage });
|
|
238
253
|
}
|
|
239
|
-
if (lastStatus)
|
|
254
|
+
if (lastStatus) renderRoleDrawer(lastStatus);
|
|
240
255
|
}
|
|
241
|
-
// The open
|
|
256
|
+
// The open role's unmerged change, as /api/diff?role serves it.
|
|
242
257
|
function loadDrawerChange(role) {
|
|
243
|
-
return loadDrawerSection(role, "/api/diff?role=", "Pending change unavailable", (v) => { drawerChange = v; });
|
|
258
|
+
return loadDrawerSection(role, "/api/diff?" + (drawerChangeStat ? "stat=1&" : "") + "role=", "Pending change unavailable", (v) => { drawerChange = v; });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function toggleDrawerChangeStat() {
|
|
262
|
+
drawerChangeStat = !drawerChangeStat;
|
|
263
|
+
return loadDrawerChange(openRoleId());
|
|
244
264
|
}
|
|
245
265
|
|
|
246
|
-
// The open
|
|
266
|
+
// The open role's notes and next-run prompt, as /api/role serves them (rolePayload):
|
|
247
267
|
// the same note and assembled prompt the CLI's role command shows, so the operator can
|
|
248
|
-
// diagnose a
|
|
268
|
+
// diagnose a role from the page. The note renders in a <pre>; the next prompt sits in a
|
|
249
269
|
// <details> so it does not push the rest of the drawer down. Both empty cases say so in a
|
|
250
270
|
// muted line. It paints into its own #drawerrole panel, not #drawermeta: the poll rewrites
|
|
251
|
-
// drawermeta whenever its countdown or
|
|
271
|
+
// drawermeta whenever its countdown or run-age text changes, and an innerHTML rewrite would
|
|
252
272
|
// collapse an opened <details>. This panel's string is stable while the payload is unchanged,
|
|
253
273
|
// so paintPanel skips it and the disclosure stays open.
|
|
254
274
|
function drawerRoleSection() {
|
|
@@ -272,14 +292,14 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
272
292
|
: note ? "<pre class='mono'>" + esc(note) + "</pre>" : "<p class='muted'>No notes yet.</p>";
|
|
273
293
|
body = "<div class='sec-head'><h3>Notes</h3>" + (editing ? "" : buttons) + "</div>" + noteBody +
|
|
274
294
|
"<details><summary>Next run's prompt</summary>" + (nextPrompt ? "<pre class='mono'>" + esc(nextPrompt) + "</pre>" : "<p class='muted'>Nothing to run (empty inbox).</p>") + "</details>" +
|
|
275
|
-
// The
|
|
276
|
-
// log), rendered like the Recent
|
|
277
|
-
// label, summary, and duration. Mirrors the Recent
|
|
295
|
+
// The role's recent run outcomes (rolePayload's recentOutcomes, read from the event
|
|
296
|
+
// log), rendered like the Recent runs list above: each with its relative age, result
|
|
297
|
+
// label, summary, and duration. Mirrors the Recent run outcomes section of
|
|
278
298
|
// tumwater role <id> — no backticks allowed here (this whole script is one big
|
|
279
|
-
// String.raw template), so the page and the CLI read one
|
|
299
|
+
// String.raw template), so the page and the CLI read one role's outcomes identically.
|
|
280
300
|
(outcomes.length ? "<section><div class='sec-head'><h3>Recent outcomes" +
|
|
281
301
|
"<span class='muted' style='font-size:13px'>· " + outcomes.length + "</span></h3></div>" +
|
|
282
|
-
"<ul class='
|
|
302
|
+
"<ul class='run-list'>" + outcomes.map((o) => {
|
|
283
303
|
const info = resultInfo(o.result);
|
|
284
304
|
const detail = typeof o.summary === "string" && o.summary.trim() !== "" ? o.summary : "";
|
|
285
305
|
return "<li><span class='when' title='" + esc(new Date(Number(o.ts)).toLocaleString()) + "'>" + esc(fmtAgo(Number(o.ts))) + "</span>" +
|
|
@@ -290,211 +310,7 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
290
310
|
}
|
|
291
311
|
return "<section>" + body + "</section>";
|
|
292
312
|
}
|
|
293
|
-
//
|
|
294
|
-
// <id> renders (rolePayload), so the drawer's and the terminal's rows read one document and
|
|
295
|
-
// cannot drift: what the loop RUNS ON (model/tier, provider cap, image reading, the
|
|
296
|
-
// fallback pairs, billing, min tick interval) and CARRIES (queued prompts, the instructions
|
|
297
|
-
// override, find text). Each row is guarded by a present/own check so an absent field
|
|
298
|
-
// renders no row — a cap-less team shows no Provider row, no fallback shows no fallback
|
|
299
|
-
// rows, and a notes-only payload adds an empty (omitted) section. Labels and wording mirror
|
|
300
|
-
// role-render.ts's family; no backticks allowed here (this whole script is one big
|
|
301
|
-
// String.raw template).
|
|
302
|
-
function drawerConfigHtml(view, role) {
|
|
303
|
-
const pair = (p, m) => [p, m].filter(Boolean).join("/");
|
|
304
|
-
const singleLine = (s) => String(s).replace(/\s+/g, " ").trim();
|
|
305
|
-
// A verbatim value's single-line clamped rendering (80 chars + ellipsis) with the full
|
|
306
|
-
// text riding the title tooltip — the same clamp+tooltip idiom the tick summaries use.
|
|
307
|
-
const clamp = (t) => { const one = singleLine(t); return one.length > 80 ? one.slice(0, 80) + "…" : one; };
|
|
308
|
-
const rows = [];
|
|
309
|
-
// A role that runs on pi's own saved default carries no provider/model but a piDefault
|
|
310
|
-
// selector (rolePayload's resolvedModelFields leaves both undefined), and the CLI's
|
|
311
|
-
// Model line names that default (<agent> default (<selector>), agentDefault). Mirror
|
|
312
|
-
// that branch so the drawer names the model a default pi team actually runs on.
|
|
313
|
-
const defaultLine = typeof view.piDefault === "string"
|
|
314
|
-
? (view.backend && view.backend !== "pi" ? view.backend : "pi") + " default (" + view.piDefault + ")"
|
|
315
|
-
: "";
|
|
316
|
-
const modelLine = defaultLine !== "" ? defaultLine : pair(view.provider, view.model);
|
|
317
|
-
if (modelLine !== "" || view.modelTier !== undefined) {
|
|
318
|
-
rows.push(kvRow("Model", (modelLine !== "" ? modelLine + " " : "") + (view.modelTier !== undefined ? "(" + view.modelTier + " tier)" : "").trim()));
|
|
319
|
-
}
|
|
320
|
-
// The per-role model triple (roles.<base>.provider/.model/.thinking, the file's own raw
|
|
321
|
-
// overrides rolePayload's providerOverride/modelOverride/thinkingOverride carry) — the
|
|
322
|
-
// one per-role write the Model row above still renders read-only, the dashboard half of
|
|
323
|
-
// the CLI's --provider/--model/--thinking. An always-shown row (every role, the director
|
|
324
|
-
// included, since the CLI writes its triple too) whose body names the payload's raw
|
|
325
|
-
// overrides, or no overrides, and whose control is a model-edit button (Edit/Set model);
|
|
326
|
-
// while the editor is open for this role, three text inputs prefilled with the raw
|
|
327
|
-
// overrides (empty when unset) plus Save/Discard replace the body, mirroring the interval
|
|
328
|
-
// editor's markup.
|
|
329
|
-
const rawTriple = [];
|
|
330
|
-
if (typeof view.providerOverride === "string" && view.providerOverride !== "") rawTriple.push("provider: " + view.providerOverride);
|
|
331
|
-
if (typeof view.modelOverride === "string" && view.modelOverride !== "") rawTriple.push("model: " + view.modelOverride);
|
|
332
|
-
if (typeof view.thinkingOverride === "string" && view.thinkingOverride !== "") rawTriple.push("thinking: " + view.thinkingOverride);
|
|
333
|
-
const modelBody = rawTriple.length ? rawTriple.join(" · ") : "no overrides";
|
|
334
|
-
const editModel = modelEditing && modelEditing.role === role;
|
|
335
|
-
const modelControl = editModel
|
|
336
|
-
? "<input id='modeleditprovider' type='text' placeholder='provider' value='" + esc(modelEditing.provider) + "'>" +
|
|
337
|
-
"<input id='modeleditmodel' type='text' placeholder='model' value='" + esc(modelEditing.model) + "'>" +
|
|
338
|
-
"<input id='modeleditthinking' type='text' placeholder='thinking' value='" + esc(modelEditing.thinking) + "'>" +
|
|
339
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='model-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
340
|
-
"<button type='button' class='btn btn-sm' data-act='model-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
341
|
-
: "<button type='button' class='btn btn-sm' data-act='model-edit' data-arg='" + esc(role) + "'>" + (rawTriple.length ? "Edit" : "Set model") + "</button>";
|
|
342
|
-
rows.push("<div><dt>Model overrides</dt><dd title='" + esc(modelBody) + "'>" + esc(modelBody) + modelControl + "</dd></div>");
|
|
343
|
-
// Context window: the resolved primary seam's declared window - the same seam '- Next run:'
|
|
344
|
-
// previews. Guard optional to keep an older payload that lacks the field rendering as before.
|
|
345
|
-
if (view.contextWindow !== undefined) {
|
|
346
|
-
rows.push(kvRow("Context window", view.contextWindow === null ? "unknown" : fmtTokens(view.contextWindow) + " tokens"));
|
|
347
|
-
}
|
|
348
|
-
if (view.providerKey !== undefined && view.providerCap !== undefined) {
|
|
349
|
-
rows.push(kvRow("Provider", view.providerKey + " (cap " + view.providerCap + ")"));
|
|
350
|
-
}
|
|
351
|
-
if (view.imageInput !== undefined) {
|
|
352
|
-
const images = view.imageInput === true ? "yes"
|
|
353
|
-
: view.imageInput === false ? (view.imageReroute ? "no — reruns on " + view.imageReroute : "no")
|
|
354
|
-
: "unknown";
|
|
355
|
-
rows.push(kvRow("Images", images));
|
|
356
|
-
}
|
|
357
|
-
if (view.modelFallback && typeof view.modelFallback === "object" && view.modelFallback.model !== undefined) {
|
|
358
|
-
const fb = view.modelFallback;
|
|
359
|
-
const when = typeof fb.since === "number" ? " since " + fmtAgo(fb.since) : "";
|
|
360
|
-
rows.push(kvRow("Model fallback", pair(fb.provider, fb.model) + " — on fallback" + when + " (primary failing: " + (fb.reason ?? "") + ")"));
|
|
361
|
-
}
|
|
362
|
-
if (view.fallback && typeof view.fallback === "object" && view.fallback.model !== undefined) {
|
|
363
|
-
rows.push(kvRow("Budget fallback", pair(view.fallback.provider, view.fallback.model) + " (" + (view.fallbackFree ? "free" : "priced") + ")"));
|
|
364
|
-
}
|
|
365
|
-
if (view.billing !== undefined) {
|
|
366
|
-
const window = view.billing === "subscription" && view.billingWindow ? " (" + view.billingWindow + " window)" : "";
|
|
367
|
-
rows.push(kvRow("Billing", (view.billing === "api" ? "API" : view.billing) + window));
|
|
368
|
-
}
|
|
369
|
-
// The min run interval (rolePayload's minRunIntervalSeconds), worded like role-render.ts's
|
|
370
|
-
// Min run interval line, plus a control to set/clear the per-role override — the last
|
|
371
|
-
// per-role hold the drawer edits. Shown whenever the payload names an interval (always —
|
|
372
|
-
// the effective value is the role's override or the team default); the body tags whether
|
|
373
|
-
// the shown value is the role's own override or the team default. While the editor is open
|
|
374
|
-
// for this role, an input prefilled with the override (empty when none) and Save/Discard
|
|
375
|
-
// replace the sentence, mirroring the daily-cap editor's markup; a role with no override
|
|
376
|
-
// still seeds an empty input, so an empty buffer on Save clears.
|
|
377
|
-
if (typeof view.minRunIntervalSeconds === "number") {
|
|
378
|
-
const editing = intervalEditing && intervalEditing.role === role;
|
|
379
|
-
const intervalBody = String(view.minRunIntervalSeconds) + "s" +
|
|
380
|
-
(typeof view.minRunIntervalOverride === "number" ? " (override)" : " (team default)");
|
|
381
|
-
const seed = intervalEditing ? intervalEditing.value : null;
|
|
382
|
-
const intervalControl = editing
|
|
383
|
-
? "<input id='intervaleditinput' type='number' min='0' step='1' value='" + (seed === "" || seed == null ? "" : String(seed)) + "' placeholder='team default'>" +
|
|
384
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='interval-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
385
|
-
"<button type='button' class='btn btn-sm' data-act='interval-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
386
|
-
: "<button type='button' class='btn btn-sm' data-act='interval-edit' data-arg='" + esc(role) + "'>" + (typeof view.minRunIntervalOverride === "number" ? "Edit" : "Set interval") + "</button>";
|
|
387
|
-
rows.push("<div><dt>Min run interval</dt><dd title='" + esc(intervalBody) + "'>" + esc(intervalBody) + intervalControl + "</dd></div>");
|
|
388
|
-
}
|
|
389
|
-
// The parallel-runner count (roles.<base>.instances), worded like role-render.ts's
|
|
390
|
-
// Instances line — the last per-role hold the drawer edits. Rendered only for a role that
|
|
391
|
-
// may split (its base, an instance strip, is feature or bugfix): the count is meaningless
|
|
392
|
-
// for any other role, which role-view never carries. The body reads the payload's numeric
|
|
393
|
-
// count (roles.<base>.instances) tagged (set) or falls back to 1 tagged (default) when the
|
|
394
|
-
// file leaves it unset — role-view carries instances only when the file sets it. While the
|
|
395
|
-
// editor is open for this role, an input prefilled with the count (empty when none) and
|
|
396
|
-
// Save/Discard
|
|
397
|
-
// replace the sentence, mirroring the interval editor's markup; a role with no count still
|
|
398
|
-
// seeds an empty input, so an empty buffer on Save clears.
|
|
399
|
-
const splitBase = role.replace(/-\d+$/, "");
|
|
400
|
-
if (splitBase === "feature" || splitBase === "bugfix") {
|
|
401
|
-
const hasInstances = typeof view.instances === "number";
|
|
402
|
-
const instCount = hasInstances ? view.instances : 1;
|
|
403
|
-
const instBody = String(instCount) + (hasInstances ? " (set)" : " (default)");
|
|
404
|
-
const editingInst = instancesEditing && instancesEditing.role === role;
|
|
405
|
-
const seed = instancesEditing ? instancesEditing.value : null;
|
|
406
|
-
const instControl = editingInst
|
|
407
|
-
? "<input id='instanceseditinput' type='number' min='1' max='8' step='1' value='" + (seed === "" || seed == null ? "" : String(seed)) + "' placeholder='default 1'>" +
|
|
408
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='instances-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
409
|
-
"<button type='button' class='btn btn-sm' data-act='instances-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
410
|
-
: "<button type='button' class='btn btn-sm' data-act='instances-edit' data-arg='" + esc(role) + "'>" + (hasInstances ? "Edit" : "Set instances") + "</button>";
|
|
411
|
-
rows.push("<div><dt>Instances</dt><dd title='" + esc(instBody) + "'>" + esc(instBody) + instControl + "</dd></div>");
|
|
412
|
-
}
|
|
413
|
-
// The windows governing the loop, worded like role-render.ts's Quiet hours line; the end time
|
|
414
|
-
// mirrors quietWindowEnd (browser code cannot import it). A non-director role's own window
|
|
415
|
-
// is an always-shown editable row (the Daily cap row's rationale: the control sits where
|
|
416
|
-
// the hold is shown) — including when the payload carries no role-scope window ("no own
|
|
417
|
-
// window"); any other window stays a read-only row worded exactly as before ("team window").
|
|
418
|
-
const own = Array.isArray(view.quietHours) ? view.quietHours.find(function (w) { return w && w.scope === "role"; }) : undefined;
|
|
419
|
-
const ownWindow = own && typeof own.window === "string" ? own.window : "";
|
|
420
|
-
if (role !== "director") {
|
|
421
|
-
const ownEnd = String(ownWindow).split("-")[1];
|
|
422
|
-
const ownState = own && own.inWindow
|
|
423
|
-
? "holding now — quiet until " + (ownEnd ? ownEnd.trim() : "")
|
|
424
|
-
: "not holding now";
|
|
425
|
-
const editQuiet = quietEditing && quietEditing.role === role;
|
|
426
|
-
const quietBody = ownWindow !== "" ? ownWindow + " (own window; " + ownState + ")" : "no own window";
|
|
427
|
-
const quietControl = editQuiet
|
|
428
|
-
? "<input id='quieteditinput' type='text' placeholder='23:00-07:00' value='" + esc(quietEditing.window) + "'>" +
|
|
429
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='quiet-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
430
|
-
"<button type='button' class='btn btn-sm' data-act='quiet-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
431
|
-
: "<button type='button' class='btn btn-sm' data-act='quiet-edit' data-arg='" + esc(role) + "'>" + (ownWindow !== "" ? "Edit" : "Set quiet hours") + "</button>";
|
|
432
|
-
rows.push("<div><dt>Quiet hours</dt><dd title='" + esc(quietBody) + "'>" + esc(quietBody) + quietControl + "</dd></div>");
|
|
433
|
-
}
|
|
434
|
-
if (Array.isArray(view.quietHours)) view.quietHours.forEach(function (w) {
|
|
435
|
-
if (!w || w.scope === "role" || typeof w.window !== "string") return;
|
|
436
|
-
const end = String(w.window).split("-")[1];
|
|
437
|
-
const state = w.inWindow ? "holding now — quiet until " + (end ? end.trim() : "") : "not holding now";
|
|
438
|
-
rows.push(kvRow("Quiet hours", w.window + " (team window; " + state + ")"));
|
|
439
|
-
});
|
|
440
|
-
if (typeof view.inboxCount === "number") rows.push(kvRow("Queued prompts", String(view.inboxCount)));
|
|
441
|
-
// The loop's own spend over its retained event window (rolePayload's usage, summed from the
|
|
442
|
-
// same run_end events recentOutcomes reads): how much the loop burned on tokens and cost,
|
|
443
|
-
// with a notional suffix only when a claude-subscription seam burned non-zero notional spend
|
|
444
|
-
// — mirroring role-render.ts's Spend line, so the drawer and tumwater role <id> read one
|
|
445
|
-
// loop's spend identically. Each numeric field is coerced (foreign shapes read as 0) and an
|
|
446
|
-
// all-zero usage is absent on the payload, so this row never renders for a never-costly loop.
|
|
447
|
-
if (view.usage && typeof view.usage === "object") {
|
|
448
|
-
const u = view.usage;
|
|
449
|
-
const real = Number(u.costUsd) || 0;
|
|
450
|
-
const tokens = Number(u.tokens) || 0;
|
|
451
|
-
const notional = Number(u.notionalCostUsd) || 0;
|
|
452
|
-
let spend = fmtUsd(real) + " · " + fmtTokens(tokens) + " tokens";
|
|
453
|
-
if (notional !== 0) spend += " · " + fmtUsd(notional) + " notional";
|
|
454
|
-
rows.push(kvRow("Spend", spend));
|
|
455
|
-
}
|
|
456
|
-
// The per-role daily cap (rolePayload's dailyCap), worded like role-render.ts's Daily cap line.
|
|
457
|
-
// The per-role daily cap (rolePayload's dailyCap), worded like role-render.ts's Daily cap
|
|
458
|
-
// line. Shown for every non-director role — always, cap or not — because the "Set a cap"/
|
|
459
|
-
// "Edit cap" control is the end of the amber role-cap alert's path: the alert's remedy
|
|
460
|
-
// leads here. A director shows no row (role-view.ts's guard exempts it from a cap). While
|
|
461
|
-
// the cap editor is open for this role, an input prefilled with the current cap (empty for
|
|
462
|
-
// none) and Save/Discard replace the sentence, mirroring the notebook editor's markup.
|
|
463
|
-
if (role !== "director") {
|
|
464
|
-
const dc = view.dailyCap && typeof view.dailyCap === "object" ? view.dailyCap : null;
|
|
465
|
-
const editing = capEditing && capEditing.role === role;
|
|
466
|
-
const capBody = dc
|
|
467
|
-
? fmtUsd(Number(dc.spentUsd) || 0) + " of " + fmtUsd(Number(dc.capUsd) || 0) + " spent today (" + (dc.over ? "holding now — no new runs until local midnight" : "not holding") + ")" +
|
|
468
|
-
(Array.isArray(dc.roleIds) && dc.roleIds.length > 1 ? " · shared by " + dc.roleIds.join(", ") : "")
|
|
469
|
-
: "no cap";
|
|
470
|
-
const capControl = editing
|
|
471
|
-
? "<input id='capeditinput' type='number' min='0' step='0.01' value='" + (capEditing.value ? String(capEditing.value) : "") + "' placeholder='no cap'>" +
|
|
472
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='cap-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
473
|
-
"<button type='button' class='btn btn-sm' data-act='cap-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
474
|
-
: "<button type='button' class='btn btn-sm' data-act='cap-edit' data-arg='" + esc(role) + "'>" + (dc ? "Edit cap" : "Set a cap") + "</button>";
|
|
475
|
-
rows.push("<div><dt>Daily cap</dt><dd title='" + esc(capBody) + "'>" + esc(capBody) + capControl + "</dd></div>");
|
|
476
|
-
}
|
|
477
|
-
// The standing instructions (roles.<base>.instructions, resolved through the base role),
|
|
478
|
-
// worded like role-render.ts's Instructions override. Shown for every role — always,
|
|
479
|
-
// override or not — because the Edit/Save control is how the director's and instance
|
|
480
|
-
// runs' instructions get written from the dashboard: the same always-shown rationale as
|
|
481
|
-
// the Daily cap row above. While the editor is open for this role, a textarea prefilled
|
|
482
|
-
// with the current text and Save/Discard replace the sentence, mirroring the notebook
|
|
483
|
-
// editor's markup; a role with no override still seeds an empty textarea, so an empty
|
|
484
|
-
// buffer on Save clears.
|
|
485
|
-
const instEditing = instructionsEditing && instructionsEditing.role === role;
|
|
486
|
-
const instText = typeof view.instructions === "string" ? view.instructions : "";
|
|
487
|
-
const instBody = instText.trim() !== "" ? clamp(instText) : "No standing instructions.";
|
|
488
|
-
const instControl = instEditing
|
|
489
|
-
? "<textarea id='instructionsedittext' rows='10' class='mono' style='width:100%'>" + esc(instructionsEditing.text) + "</textarea>" +
|
|
490
|
-
"<div><button type='button' class='btn btn-sm btn-primary' data-act='instructions-save' data-arg='" + esc(role) + "'>Save</button> " +
|
|
491
|
-
"<button type='button' class='btn btn-sm' data-act='instructions-discard' data-arg='" + esc(role) + "'>Discard</button></div>"
|
|
492
|
-
: "<button type='button' class='btn btn-sm' data-act='instructions-edit' data-arg='" + esc(role) + "'>Edit</button>";
|
|
493
|
-
rows.push("<div><dt>Instructions override</dt><dd title='" + esc(instText) + "'>" + esc(instBody) + instControl + "</dd></div>");
|
|
494
|
-
if (typeof view.find === "string" && view.find.trim() !== "") rows.push(kvRow("Find text", clamp(view.find), view.find));
|
|
495
|
-
return rows.length ? "<section><div class='sec-head'><h3>Configuration</h3></div><dl class='kv'>" + rows.join("") + "</dl></section>" : "";
|
|
496
|
-
}
|
|
497
|
-
// An open notebook edit holds the draft's starting text, not the live payload, so a poll
|
|
313
|
+
// An open notes edit holds the draft's starting text, not the live payload, so a poll
|
|
498
314
|
// refetch neither wipes the textarea nor changes the painted panel string. Clear waits for a
|
|
499
315
|
// confirming click like entry cancel: the first click arms it for 5 s, the second posts.
|
|
500
316
|
let noteEditing = null; // { role, text }
|
|
@@ -504,9 +320,9 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
504
320
|
let intervalEditing = null; // { role, value } — value is the seeded override (number) or "" when none
|
|
505
321
|
let instancesEditing = null; // { role, value } — value is the seeded count (number) or "" when none
|
|
506
322
|
let modelEditing = null; // { role, provider, model, thinking } — the seeded raw overrides ("" when unset)
|
|
507
|
-
function repaintDrawerRole() { if (lastStatus)
|
|
508
|
-
// The
|
|
509
|
-
// the shared confirmSlot factory (defined in the
|
|
323
|
+
function repaintDrawerRole() { if (lastStatus) renderRoleDrawer(lastStatus); }
|
|
324
|
+
// The notes clear and the backlog entry cancel each build a two-click confirm slot from
|
|
325
|
+
// the shared confirmSlot factory (defined in the roles splice, an earlier one), so the arm,
|
|
510
326
|
// guard, and disarm logic lives once.
|
|
511
327
|
const noteClear = confirmSlot(repaintDrawerRole);
|
|
512
328
|
const noteClearArmed = (role) => noteClear.armed(role);
|
|
@@ -526,24 +342,24 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
526
342
|
function stopModelEdit() { modelEditing = null; repaintDrawerRole(); }
|
|
527
343
|
function startQuietEdit(role, window) { quietEditing = { role, window: String(window ?? "") }; repaintDrawerRole(); }
|
|
528
344
|
function stopQuietEdit() { quietEditing = null; repaintDrawerRole(); }
|
|
529
|
-
// The
|
|
345
|
+
// The notes fetch shares loadDrawerSection; closing the drawer clears drawerRole, so
|
|
530
346
|
// reopening refetches.
|
|
531
347
|
function loadDrawerRole(role) {
|
|
532
348
|
return loadDrawerSection(role, "/api/role?role=", "Notes and prompt unavailable", (v) => { drawerRole = v; });
|
|
533
349
|
}
|
|
534
350
|
|
|
535
|
-
async function
|
|
536
|
-
const role =
|
|
537
|
-
if (!role || (!force && Date.now() -
|
|
538
|
-
|
|
351
|
+
async function loadRoleRuns(force) {
|
|
352
|
+
const role = openRoleId();
|
|
353
|
+
if (!role || (!force && Date.now() - drawerRunsAt < 5000)) return;
|
|
354
|
+
drawerRunsAt = Date.now();
|
|
539
355
|
try {
|
|
540
356
|
const d = await getJson("/api/history?n=8&role=" + encodeURIComponent(role), pollSignal());
|
|
541
|
-
if (
|
|
542
|
-
|
|
357
|
+
if (openRoleId() !== role) return;
|
|
358
|
+
drawerRuns = d && Array.isArray(d.rows) ? d.rows : [];
|
|
543
359
|
} catch {
|
|
544
|
-
if (
|
|
360
|
+
if (drawerRuns === null) drawerRuns = [];
|
|
545
361
|
}
|
|
546
|
-
if (lastStatus)
|
|
362
|
+
if (lastStatus) renderRoleDrawer(lastStatus);
|
|
547
363
|
}
|
|
548
364
|
|
|
549
365
|
// Transcript lines by kind (transcript.ts's rendering): run separators, tool calls, thinking,
|
|
@@ -555,24 +371,38 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
555
371
|
}).join("\n");
|
|
556
372
|
}
|
|
557
373
|
async function refreshTranscript(initial) {
|
|
558
|
-
const role =
|
|
374
|
+
const role = openRoleId();
|
|
559
375
|
if (!role) return;
|
|
376
|
+
const prompts = transcriptPrompts;
|
|
377
|
+
const grep = transcriptGrep;
|
|
560
378
|
let d;
|
|
561
379
|
try {
|
|
562
|
-
d = await getJson("/api/transcript?role=" + encodeURIComponent(role) + "&n=200", pollSignal());
|
|
380
|
+
d = await getJson("/api/transcript?role=" + encodeURIComponent(role) + "&n=200" + (prompts ? "&prompt=1" : "") + (grep ? "&grep=" + encodeURIComponent(grep) : ""), pollSignal());
|
|
563
381
|
} catch {
|
|
564
382
|
return; // keep the previous content on a failed poll
|
|
565
383
|
}
|
|
566
384
|
const pre = $("transcript");
|
|
567
|
-
if (
|
|
385
|
+
if (openRoleId() !== role || !pre || transcriptPrompts !== prompts || transcriptGrep !== grep) return;
|
|
568
386
|
const lines = Array.isArray(d.lines) ? d.lines : [];
|
|
569
|
-
const html = lines.length ? transcriptHtml(lines) : "<span class='muted'>No transcript yet — it fills in once this role's next run starts.</span>";
|
|
387
|
+
const html = lines.length ? transcriptHtml(lines) : grep ? "<span class='muted'>No transcript entries match \u201c" + esc(grep) + "\u201d.</span>" : "<span class='muted'>No transcript yet — it fills in once this role's next run starts.</span>";
|
|
570
388
|
// Stay pinned to the newest line while the reader is at the bottom; leave them be if they
|
|
571
389
|
// scrolled up to read.
|
|
572
390
|
const atBottom = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 12;
|
|
573
391
|
if (paintPanel("transcript", html) && (initial || atBottom)) pre.scrollTop = pre.scrollHeight;
|
|
574
392
|
}
|
|
575
393
|
|
|
394
|
+
function toggleTranscriptPrompts() {
|
|
395
|
+
transcriptPrompts = !transcriptPrompts;
|
|
396
|
+
const b = $("transcriptprompts");
|
|
397
|
+
if (b) b.textContent = transcriptPrompts ? "Hide prompts" : "Show prompts";
|
|
398
|
+
refreshTranscript(true);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function setTranscriptGrep(value) {
|
|
402
|
+
transcriptGrep = String(value ?? "").trim();
|
|
403
|
+
refreshTranscript(true);
|
|
404
|
+
}
|
|
405
|
+
|
|
576
406
|
// A plan/bug cancel waits for its confirming click, the confirmAbort shape: the first click
|
|
577
407
|
// arms it for 5 s, a second inside the window fires, and a timeout disarms. The slot records
|
|
578
408
|
// the title too: a list that shifted between the clicks must re-arm, not cancel whatever
|
|
@@ -621,7 +451,7 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
621
451
|
"<button type='button' class='btn btn-sm btn-primary' data-act='answer-record' data-arg='" + index + "'>" + icon("chat") + "Record answer</button>" +
|
|
622
452
|
"<button type='button' class='btn btn-sm' data-act='answer' data-arg='" + index + "'>Ask the director instead</button>"
|
|
623
453
|
: "<button type='button' class='btn btn-sm' data-act='mention' data-arg='" + esc(file + ":" + index) + "'>" + icon("chat") + "Ask the director about this</button>" +
|
|
624
|
-
(d.refused || d.waiting ? "<button type='button' class='btn btn-sm btn-primary' data-act='entry-release' data-arg='" + esc(file + ":" + index) + "'>Release this entry</button>" : "") +
|
|
454
|
+
(d.refused || d.waiting || d.heldForOperator ? "<button type='button' class='btn btn-sm btn-primary' data-act='entry-release' data-arg='" + esc(file + ":" + index) + "'>Release this entry</button>" : "") +
|
|
625
455
|
(index > 0 ? "<button type='button' class='btn btn-sm' data-act='entry-top' data-arg='" + esc(file + ":" + index) + "'>Move to top</button>" : "") +
|
|
626
456
|
"<button type='button' class='btn btn-sm' data-act='entry-edit' data-arg='" + esc(file + ":" + index) + "'>Edit this entry</button>" +
|
|
627
457
|
"<button type='button' class='btn btn-sm btn-danger' data-act='entry-cancel' data-arg='" + esc(file + ":" + index) + "'>" + (entryCancelArmed(file + ":" + index) ? "Click again to cancel" : "Cancel this entry") + "</button>";
|
|
@@ -632,15 +462,26 @@ export const GUI_CLIENT_DRAWER_JS = String.raw ` let drawer = null; // { kind:
|
|
|
632
462
|
|
|
633
463
|
async function refreshDrawer() {
|
|
634
464
|
if (!drawer) return;
|
|
635
|
-
if (drawer.kind === "
|
|
636
|
-
if (lastStatus)
|
|
465
|
+
if (drawer.kind === "role") {
|
|
466
|
+
if (lastStatus) renderRoleDrawer(lastStatus);
|
|
637
467
|
await refreshTranscript(false);
|
|
638
468
|
} else {
|
|
639
469
|
await refreshEntry();
|
|
640
470
|
}
|
|
641
471
|
}
|
|
642
472
|
$("scrim").addEventListener("click", closeDrawer);
|
|
473
|
+
$("drawer").addEventListener("input", (ev) => {
|
|
474
|
+
if (ev.target && ev.target.id === "transcriptgrep") setTranscriptGrep(ev.target.value);
|
|
475
|
+
});
|
|
643
476
|
$("drawer").addEventListener("click", (ev) => {
|
|
477
|
+
if (clickClosest(ev, "button[data-transcript-prompts]")) {
|
|
478
|
+
toggleTranscriptPrompts();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (clickClosest(ev, "button[data-change-stat]")) {
|
|
482
|
+
toggleDrawerChangeStat();
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
644
485
|
const b = clickClosest(ev, "button.rowaction");
|
|
645
486
|
if (b) rowAction(b.dataset.action, b.dataset.role, b.dataset.for === undefined ? undefined : Number(b.dataset.for));
|
|
646
487
|
});
|