tumwater 0.4.2 → 0.4.3
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 +6 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +6 -6
- package/dist/src/agent/{run-agent.js → agent-run.js} +25 -12
- package/dist/src/agent/{run-settle.js → agent-settle.js} +48 -24
- package/dist/src/agent/{run-spawn.js → agent-spawn.js} +21 -12
- package/dist/src/agent/{run-stream.js → agent-stream.js} +4 -4
- package/dist/src/agent/backend-traits.js +2 -2
- package/dist/src/agent/claude/claude-account.js +1 -8
- package/dist/src/agent/claude/claude-backend.js +3 -3
- package/dist/src/agent/claude/claude-env.js +27 -31
- package/dist/src/agent/claude/claude-stream.js +111 -54
- package/dist/src/agent/codex/codex-account.js +9 -0
- package/dist/src/agent/codex/codex-backend.js +2 -2
- package/dist/src/agent/codex/codex-env.js +28 -15
- package/dist/src/agent/codex/codex-hook-trust.js +39 -20
- package/dist/src/agent/codex/codex-rollout.js +93 -56
- package/dist/src/agent/codex/codex-stream.js +1 -1
- package/dist/src/agent/env-scrub.js +18 -0
- package/dist/src/agent/image-input.js +68 -11
- package/dist/src/agent/notes-shim.js +2 -2
- package/dist/src/agent/opencode/opencode-backend.js +2 -2
- package/dist/src/agent/opencode/opencode-catalog.js +50 -27
- package/dist/src/agent/opencode/opencode-config.js +32 -14
- package/dist/src/agent/opencode/opencode-env.js +12 -13
- package/dist/src/agent/opencode/opencode-stream.js +47 -25
- package/dist/src/agent/session-ledger.js +1 -1
- package/dist/src/agent/subscription-account.js +3 -3
- package/dist/src/backlog/backlog-cli-view.js +81 -41
- package/dist/src/backlog/backlog-cli.js +57 -26
- package/dist/src/backlog/backlog-eligibility.js +58 -102
- package/dist/src/backlog/backlog-md.js +5 -5
- package/dist/src/backlog/backlog-parts.js +65 -27
- package/dist/src/backlog/backlog-release.js +12 -12
- package/dist/src/backlog/backlog-structure.js +114 -61
- package/dist/src/backlog/backlog.js +1 -1
- package/dist/src/baseline/main-baseline.js +148 -84
- package/dist/src/budget/budget-fallback-view.js +100 -243
- package/dist/src/budget/budget-wiring.js +29 -65
- package/dist/src/budget/fallback-breaker.js +2 -2
- package/dist/src/build/build-check-detect.js +43 -14
- package/dist/src/build/build-check-events.js +2 -2
- package/dist/src/build/build-check-scoped.js +40 -16
- package/dist/src/build/build-check.js +13 -5
- package/dist/src/cli/cli-command-args.js +89 -52
- package/dist/src/cli/cli-flag-specs.js +42 -22
- package/dist/src/cli/cli-init.js +34 -12
- package/dist/src/cli/cli-query-commands.js +1 -1
- package/dist/src/cli/cli-repo-gate.js +2 -2
- package/dist/src/cli/cli-start.js +91 -58
- package/dist/src/cli/completion.js +82 -46
- package/dist/src/cli/config-commands.js +82 -71
- package/dist/src/cli/help.js +4 -6
- package/dist/src/cli/log-commands.js +43 -23
- package/dist/src/cli/notify-command.js +2 -2
- package/dist/src/cli/question-commands.js +86 -60
- package/dist/src/cli.js +181 -135
- package/dist/src/collections.js +31 -16
- package/dist/src/concurrency/check-slots.js +70 -0
- package/dist/src/concurrency/lock.js +22 -0
- package/dist/src/concurrency/run-permit.js +49 -0
- package/dist/src/concurrency/semaphore.js +46 -250
- package/dist/src/config/config-editable-keys.js +4 -7
- package/dist/src/config/config-fallbacks.js +81 -101
- package/dist/src/config/config-field-checks.js +66 -37
- package/dist/src/config/config-keys.js +5 -20
- package/dist/src/config/config-live.js +4 -28
- package/dist/src/config/{model-selector.js → config-model-selector.js} +4 -4
- package/dist/src/config/config-request.js +153 -102
- package/dist/src/config/config-schema.js +9 -11
- package/dist/src/config/config-selector-checks.js +1 -1
- package/dist/src/config/config-validation-agents.js +2 -2
- package/dist/src/config/config-validation-roles.js +4 -52
- package/dist/src/config/config-validation.js +4 -38
- package/dist/src/config/config-views.js +9 -34
- package/dist/src/config/config-write.js +6 -23
- package/dist/src/config/config.js +4 -5
- package/dist/src/doctor/doctor-backlog-bugs.js +7 -6
- package/dist/src/doctor/doctor-backlog-plans.js +24 -24
- package/dist/src/doctor/doctor-backlog.js +7 -5
- package/dist/src/doctor/doctor-check-names.js +3 -6
- package/dist/src/doctor/doctor-checks.js +5 -5
- package/dist/src/doctor/doctor-claude-checks.js +44 -28
- package/dist/src/doctor/doctor-codex-checks.js +6 -6
- package/dist/src/doctor/doctor-contention-checks.js +44 -112
- package/dist/src/doctor/doctor-fallback-checks.js +2 -2
- package/dist/src/doctor/doctor-holds.js +6 -45
- package/dist/src/doctor/doctor-install.js +1 -1
- package/dist/src/doctor/doctor-launch-services.js +1 -1
- package/dist/src/doctor/doctor-model-checks.js +178 -94
- package/dist/src/doctor/doctor-notify.js +2 -2
- package/dist/src/doctor/doctor-opencode-checks.js +5 -4
- package/dist/src/doctor/doctor-orphans.js +120 -20
- package/dist/src/doctor/{outcome.js → doctor-outcome.js} +7 -0
- package/dist/src/doctor/doctor-pi-checks.js +2 -2
- package/dist/src/doctor/doctor-principles.js +3 -3
- package/dist/src/doctor/doctor-project-checks.js +3 -3
- package/dist/src/doctor/doctor-role-notes.js +1 -1
- package/dist/src/doctor/doctor.js +8 -11
- package/dist/src/events/event-format.js +30 -54
- package/dist/src/events/event-labels.js +1 -5
- package/dist/src/events/event-legacy.js +1 -3
- package/dist/src/events/{notify.js → event-notify.js} +2 -2
- package/dist/src/events/event-read.js +1 -1
- package/dist/src/failure/failure-cluster.js +6 -6
- package/dist/src/failure/failure-data.js +8 -4
- package/dist/src/failure/failure-render.js +44 -34
- package/dist/src/failure/failure-state-change.js +4 -34
- package/dist/src/failure/{time-spend.js → failure-time-spend.js} +117 -84
- package/dist/src/files/files.js +30 -0
- package/dist/src/files/json-object.js +8 -8
- package/dist/src/files/stat-cache.js +12 -8
- package/dist/src/gates/budget-gates.js +85 -84
- package/dist/src/gates/gate-polls.js +27 -68
- package/dist/src/gates/pause-gates.js +16 -2
- package/dist/src/gates/readiness.js +1 -1
- package/dist/src/gates/role-cap-gates.js +44 -35
- package/dist/src/gates/{startup-gate.js → startup-gates.js} +7 -7
- package/dist/src/gates/{streak-gate.js → streak-gates.js} +75 -52
- package/dist/src/git/git.js +75 -45
- package/dist/src/git/worktree-pool.js +3 -6
- package/dist/src/gui/gui-admin-endpoints.js +3 -3
- package/dist/src/gui/gui-backlog-commands.js +4 -4
- package/dist/src/gui/gui-backlog-endpoints.js +61 -41
- package/dist/src/gui/gui-command.js +43 -24
- package/dist/src/gui/gui-history-endpoints.js +100 -52
- package/dist/src/gui/gui-operator-commands.js +22 -23
- package/dist/src/gui/gui-prompt-commands.js +23 -23
- package/dist/src/gui/gui-report-endpoints.js +1 -1
- package/dist/src/history/history-data.js +71 -44
- package/dist/src/history/history-queued.js +49 -27
- package/dist/src/history/history.js +51 -33
- package/dist/src/inbox/prompt-commands.js +59 -42
- package/dist/src/init/init.js +88 -40
- package/dist/src/merge/merge-backlog-conflicts.js +1 -1
- package/dist/src/merge/merge-core.js +1 -1
- package/dist/src/merge/merge-drain.js +8 -11
- package/dist/src/merge/merge-pipeline.js +6 -7
- package/dist/src/merge/merge-post-review-drain.js +10 -13
- package/dist/src/merge/merge-requeue.js +1 -1
- package/dist/src/merge/merge-slot.js +0 -8
- package/dist/src/merge/merge-verify.js +8 -18
- package/dist/src/notes-cli.js +52 -42
- package/dist/src/operator/operator-commands.js +76 -44
- package/dist/src/operator/operator-intent.js +3 -3
- package/dist/src/orchestrator/orchestrator-launch.js +31 -63
- package/dist/src/orchestrator/orchestrator-poll.js +27 -32
- package/dist/src/orchestrator/orchestrator.js +8 -21
- package/dist/src/orchestrator/scheduling-admit.js +101 -68
- package/dist/src/orchestrator/scheduling-scope.js +1 -1
- package/dist/src/orchestrator/scheduling-skip.js +1 -1
- package/dist/src/orchestrator/startup-migrations.js +1 -1
- package/dist/src/paths.js +8 -7
- package/dist/src/pi/pi-args.js +5 -2
- package/dist/src/pi/pi-backend.js +2 -2
- package/dist/src/pi/pi-bin.js +3 -3
- package/dist/src/pi/pi-event-line.js +1 -1
- package/dist/src/pi/pi-models.js +43 -29
- package/dist/src/pi/pi-settings.js +43 -20
- package/dist/src/pi/pi-stream.js +32 -6
- package/dist/src/pi/pi-watchdogs.js +12 -5
- package/dist/src/pi/pi.js +2 -2
- package/dist/src/pi-extension/context-shake.js +32 -20
- package/dist/src/pi-extension/kill-guard.js +69 -0
- package/dist/src/process/process-group.js +49 -5
- package/dist/src/process/process.js +8 -8
- package/dist/src/process/run-marker.js +31 -13
- package/dist/src/process/supervisor.js +40 -28
- package/dist/src/prompt/dialect.js +1 -1
- package/dist/src/prompt/gate-notes.js +2 -2
- package/dist/src/prompt/notes-backlog.js +66 -37
- package/dist/src/prompt/principles.js +6 -4
- package/dist/src/prompt/prompt-followup.js +12 -4
- package/dist/src/prompt/prompt.js +88 -38
- package/dist/src/report/report-data.js +53 -37
- package/dist/src/report/report-fold.js +16 -9
- package/dist/src/report/report-render.js +21 -194
- package/dist/src/report/report-since-data.js +4 -3
- package/dist/src/report/report-since-render.js +173 -0
- package/dist/src/report/report-usage-fold.js +8 -8
- package/dist/src/report/report.js +46 -22
- package/dist/src/review/post-merge-review-render.js +9 -3
- package/dist/src/review/post-merge-review.js +1 -1
- package/dist/src/review/review-precheck.js +11 -1
- package/dist/src/review/review.js +40 -153
- package/dist/src/review/secret-leaks.js +26 -16
- package/dist/src/roles/role-catalog.js +11 -3
- package/dist/src/roles/role-guidance.js +16 -12
- package/dist/src/roles/role-render.js +1 -14
- package/dist/src/roles/role-view.js +9 -21
- package/dist/src/run/acceptance-greps.js +88 -45
- package/dist/src/run/run-apply.js +12 -6
- package/dist/src/run/run-detail-data.js +121 -68
- package/dist/src/run/run-detail-render.js +92 -35
- package/dist/src/run/run-detail.js +72 -41
- package/dist/src/run/run-prompt.js +63 -50
- package/dist/src/run/run-resume.js +76 -29
- package/dist/src/run/run-stage.js +135 -84
- package/dist/src/run/run-timing.js +8 -2
- package/dist/src/run/run-usage.js +31 -1
- package/dist/src/run/run-verdict.js +119 -50
- package/dist/src/run/stage-check.js +49 -32
- package/dist/src/run/stage-checks-shared.js +28 -0
- package/dist/src/run/stage-name-checks.js +111 -92
- package/dist/src/run/stage-path-checks.js +69 -42
- package/dist/src/run/stage-prose-checks.js +55 -40
- package/dist/src/runner/runner-outcome.js +6 -1
- package/dist/src/runner/runner-pi.js +3 -10
- package/dist/src/runner/runner-recovery.js +1 -1
- package/dist/src/runner/{revision.js → runner-revision.js} +1 -1
- package/dist/src/runner/runner-run.js +97 -35
- package/dist/src/runner/runner-state-load.js +12 -9
- package/dist/src/runner/runner.js +41 -25
- package/dist/src/scheduling/claims.js +0 -4
- package/dist/src/scheduling/scheduling.js +53 -66
- package/dist/src/status/status-data.js +2 -2
- package/dist/src/status/status-role-notes.js +2 -2
- package/dist/src/status/status-roles.js +9 -17
- package/dist/src/team/{error-storm.js → team-error-storm.js} +1 -1
- package/dist/src/team/{failure-spread.js → team-failure-spread.js} +5 -5
- package/dist/src/team/team-hold.js +98 -65
- package/dist/src/team/{orchestrator-info.js → team-orchestrator-info.js} +2 -15
- package/dist/src/team/team-polls.js +28 -126
- package/dist/src/team/{reclaim.js → team-reclaim.js} +61 -31
- package/dist/src/team/team-state.js +4 -13
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +20 -15
- package/dist/src/text/text.js +3 -3
- package/dist/src/ui/alert-feeds.js +49 -73
- package/dist/src/ui/badges.js +3 -13
- package/dist/src/ui/gui/gui-client-boot.js +2 -2
- package/dist/src/ui/gui/gui-client-composer.js +1 -1
- package/dist/src/ui/gui/gui-client-drawer-config.js +7 -17
- package/dist/src/ui/gui/gui-client-drawer.js +1 -1
- package/dist/src/ui/gui/gui-client-history.js +28 -14
- package/dist/src/ui/gui/gui-client-model.js +0 -12
- package/dist/src/ui/gui/gui-client-pending.js +7 -6
- package/dist/src/ui/gui/gui-client-report-chart.js +5 -5
- package/dist/src/ui/gui/gui-client-report.js +1 -1
- package/dist/src/ui/gui/gui-client-roles.js +0 -1
- package/dist/src/ui/gui/gui-client-settings.js +43 -84
- package/dist/src/ui/gui/gui-client-team.js +4 -4
- package/dist/src/ui/gui/gui-client.js +7 -1
- package/dist/src/ui/progress-data.js +53 -40
- package/dist/src/ui/run-progress-model.js +9 -2
- package/dist/src/ui/status-model.js +2 -24
- package/dist/src/ui/status-payload.js +3 -7
- package/dist/src/ui/status-render.js +5 -9
- package/dist/src/ui/team-alert-backlog.js +6 -6
- package/dist/src/ui/team-alert-status.js +9 -170
- package/dist/src/ui/team-alert-tail.js +19 -215
- package/dist/src/ui/team-alerts.js +7 -8
- package/dist/src/ui/transcript-run.js +27 -16
- package/dist/src/ui/tui/tui-editor-answer.js +12 -10
- package/dist/src/ui/tui/tui-editor-attach.js +4 -3
- package/dist/src/ui/tui/tui-editor-backends.js +4 -3
- package/dist/src/ui/tui/tui-editor-brief.js +6 -9
- package/dist/src/ui/tui/tui-editor-budget.js +4 -3
- package/dist/src/ui/tui/tui-editor-config.js +80 -68
- package/dist/src/ui/tui/tui-editor-entry.js +12 -20
- package/dist/src/ui/tui/tui-editor-pause.js +51 -57
- package/dist/src/ui/tui/tui-editor-prompt.js +9 -13
- package/dist/src/ui/tui/tui-editor-queued.js +11 -14
- package/dist/src/ui/tui/tui-editor-quiet.js +4 -3
- package/dist/src/ui/tui/tui-editor-release.js +4 -3
- package/dist/src/ui/tui/tui-editor-retire.js +7 -7
- package/dist/src/ui/tui/tui-editor-role-enabled.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-file.js +4 -4
- package/dist/src/ui/tui/tui-editor-role-key.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-model.js +97 -15
- package/dist/src/ui/tui/tui-editor-search.js +4 -3
- package/dist/src/ui/tui/tui-editor-session.js +17 -4
- package/dist/src/ui/tui/tui-editors.js +1 -7
- package/dist/src/ui/tui/tui-frame.js +182 -134
- package/dist/src/ui/tui/tui-guard.js +20 -0
- package/dist/src/ui/tui/tui-input.js +159 -92
- package/dist/src/ui/tui/tui-keymap.js +51 -29
- package/dist/src/ui/tui/tui-keys.js +1 -1
- package/dist/src/ui/tui/tui-pane-activity.js +3 -3
- package/dist/src/ui/tui/tui-pane-backlog.js +74 -36
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +252 -228
- package/dist/src/ui/tui/tui-pane-history.js +61 -70
- package/dist/src/ui/tui/tui-pane-pending.js +284 -247
- package/dist/src/ui/tui/tui-pane-queued.js +25 -27
- package/dist/src/ui/tui/tui-pane-report.js +2 -1
- package/dist/src/ui/tui/tui-pane-session.js +11 -7
- package/dist/src/ui/tui/tui-pane-transcript.js +19 -31
- package/dist/src/ui/tui/tui-pane.js +14 -11
- package/dist/src/ui/tui/tui-panes.js +29 -32
- package/dist/src/ui/tui/tui.js +3 -4
- package/dist/src/verdict/fix-claim.js +76 -54
- package/dist/src/verdict/refusal.js +1 -1
- package/package.json +5 -5
- package/dist/src/concurrency/provider-lease.js +0 -105
- package/dist/src/runner/model-fallback.js +0 -76
- package/dist/src/runner/runner-fallback.js +0 -66
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +0 -24
- package/dist/src/ui/tui/tui-editor-provider-caps.js +0 -27
- /package/dist/src/backlog/{question-answer.js → backlog-question-answer.js} +0 -0
- /package/dist/src/doctor/{probe.js → doctor-probe.js} +0 -0
- /package/dist/src/gates/{disk-gate.js → disk-gates.js} +0 -0
- /package/dist/src/operator/{retire.js → operator-retire.js} +0 -0
- /package/dist/src/operator/{revert.js → operator-revert.js} +0 -0
- /package/dist/src/runner/{leftover.js → runner-leftover.js} +0 -0
|
@@ -1,173 +1,6 @@
|
|
|
1
|
-
/** The tail team alerts
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
* regressed, a stopped tumwater, and roles that cannot or may not read images. One of the
|
|
5
|
-
* per-domain builders split out of src/ui/team-alerts.ts; the shared types and wording helpers
|
|
6
|
-
* live in team-alert-shared.ts. */
|
|
7
|
-
import { listRoleIds, listRoles, openRoleActions, seeFailures } from "./team-alert-shared.js";
|
|
8
|
-
import { stalePinPhrase } from "../doctor/doctor-contention-checks.js";
|
|
9
|
-
import { quietWindowEnd } from "../scheduling/quiet-hours.js";
|
|
10
|
-
import { pauseCountdown } from "./badges.js";
|
|
11
|
-
import { agree, pauseReasonSuffix, plural } from "../text/phrases.js";
|
|
12
|
-
import { humanUntil } from "../text/datetime.js";
|
|
13
|
-
import { squash } from "../text/text.js";
|
|
14
|
-
function pushQuietAlert(out, snap) {
|
|
15
|
-
// Quiet hours (plans: "Quiet hours … part 2/2, observability") informs rather than asks:
|
|
16
|
-
// the team holding to a schedule is not a problem, but an operator watching idle roles
|
|
17
|
-
// inside the window needs the same explanation the pause alert gives a paused team —
|
|
18
|
-
// why nothing runs, and when it starts again. The window end comes from quiet-hours.ts's
|
|
19
|
-
// quietWindowEnd, the same helper quietBadge uses for the header.
|
|
20
|
-
if (snap.inQuietHours && snap.quietHours) {
|
|
21
|
-
const end = quietWindowEnd(snap.quietHours);
|
|
22
|
-
out.push({
|
|
23
|
-
key: "quiet",
|
|
24
|
-
tone: "blue",
|
|
25
|
-
title: `Quiet hours — roles start no new runs until ${end}`,
|
|
26
|
-
detail: `The schedule (${snap.quietHours} local time, quietHours in tumwater.json) holds them. Runs in progress finish, and the director still runs your prompts.`,
|
|
27
|
-
actions: [],
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function pushMaintenanceAlert(out, snap) {
|
|
32
|
-
// The maintenance allowance (plans/work-ratio.md, part 1b/4): by design and self-clearing —
|
|
33
|
-
// the scheduler holds maintenance roles at the rolling 24 h allowance until merges free room,
|
|
34
|
-
// then they run again on their own — so it informs (gray, no actions), the quiet alert's
|
|
35
|
-
// tone, rather than amber like the failed/refused asks. An operator watching idle maintenance
|
|
36
|
-
// roles gets the same explanation the hold's own row carries, team-wide.
|
|
37
|
-
if (snap.maintenanceQuota.held.length > 0) {
|
|
38
|
-
const mq = snap.maintenanceQuota;
|
|
39
|
-
out.push({
|
|
40
|
-
key: "maintenance",
|
|
41
|
-
tone: "gray",
|
|
42
|
-
title: "Maintenance is held at its rolling allowance",
|
|
43
|
-
detail: `${mq.used} of ${mq.allowance} maintenance allowance used over the last 24 h; ${listRoleIds(mq.held)} ${agree(mq.held.length, "resumes", "resume")} as work merges.`,
|
|
44
|
-
actions: [],
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function pushProviderHoldAlert(out, snap, now) {
|
|
49
|
-
// A standing provider failure hold (src/team/team-hold.ts `standingHolds`, published through
|
|
50
|
-
// the orchestrator info file): a provider storm holds roles on that provider, so an operator
|
|
51
|
-
// watching idle roles with no log open sees why nothing new starts there. Self-clearing by
|
|
52
|
-
// design — the hold re-opens at its own deadline — so it informs (blue, no actions), the
|
|
53
|
-
// quiet and maintenance alerts' tone, rather than asking. The snapshot carries the holds only
|
|
54
|
-
// while a live orchestrator publishes one.
|
|
55
|
-
if (snap.providerHolds && snap.providerHolds.length > 0) {
|
|
56
|
-
const phrased = snap.providerHolds
|
|
57
|
-
.map((h) => `${h.provider ?? "pi's default backend"} (${h.kind}) until it re-opens in ${humanUntil(h.until, now)}`)
|
|
58
|
-
.join("; ");
|
|
59
|
-
out.push({
|
|
60
|
-
key: "provider-hold",
|
|
61
|
-
tone: "blue",
|
|
62
|
-
title: agree(snap.providerHolds.length, "Provider limit is holding new runs", "Provider limits are holding new runs"),
|
|
63
|
-
detail: `${phrased}. Runs in progress finish; roles on a held provider start no new runs until it re-opens.`,
|
|
64
|
-
actions: [],
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function pushProviderCapsAlert(out, providerCaps) {
|
|
69
|
-
// A `maxConcurrentPerProvider` map that cannot do its job — the always-on twin of the doctor's
|
|
70
|
-
// `provider concurrency` row (doctor-contention-checks.ts checkProviderConcurrency): a cap key
|
|
71
|
-
// no seam resolves to, a cap at or above maxConcurrent, or a keyless seam in a capped team.
|
|
72
|
-
// The provider-hold and contention alerts cover a cap that is merely too small; this one
|
|
73
|
-
// names the static misconfiguration itself, which the doctor row is the only other surface
|
|
74
|
-
// that exposes. alert-feeds.ts's providerCapProblems feeds it the row's own warning strings,
|
|
75
|
-
// so the alert and the row cannot drift on the trigger or the wording.
|
|
76
|
-
if (providerCaps.length > 0) {
|
|
77
|
-
out.push({
|
|
78
|
-
key: "provider-caps",
|
|
79
|
-
tone: "amber",
|
|
80
|
-
title: "maxConcurrentPerProvider is misconfigured",
|
|
81
|
-
detail: `${providerCaps.join("; ")} — fix the map in tumwater.json (each key must name a provider the team runs on, and each cap must sit below maxConcurrent); the provider-hold and contention alerts cover a cap that is merely too small.`,
|
|
82
|
-
actions: [],
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function pushPausedAlert(out, snap, now) {
|
|
87
|
-
if (snap.paused) {
|
|
88
|
-
const left = pauseCountdown(snap.pausedUntil, now);
|
|
89
|
-
out.push({
|
|
90
|
-
key: "paused",
|
|
91
|
-
tone: "amber",
|
|
92
|
-
// The operator's pause reason (`pause --reason <text>`) states verbatim, so an
|
|
93
|
-
// operator returning to a paused team knows why without digging through history.
|
|
94
|
-
title: left
|
|
95
|
-
? `The team is paused and resumes in ${left}${pauseReasonSuffix(snap.pauseReason)}`
|
|
96
|
-
: `The team is paused${pauseReasonSuffix(snap.pauseReason)}`,
|
|
97
|
-
detail: "Roles start no new runs. Runs in progress finish, and the director still runs your prompts.",
|
|
98
|
-
actions: [{ label: "Resume now", act: "resume" }],
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function pushContentionAlert(out, contention) {
|
|
103
|
-
// Last day's capacity contention — the always-on twin of the doctor's `workspaces`,
|
|
104
|
-
// `provider contention` and `check contention` rows (doctor-contention-checks.ts). The
|
|
105
|
-
// instantaneous badges show only current waits, and the 24 h total is the tuning signal that
|
|
106
|
-
// says a cap is too small; alert-feeds.ts's capacityContention feeds it from the same
|
|
107
|
-
// `*_wait` events and ten-minute thresholds the doctor sums, so the alert and the doctor
|
|
108
|
-
// cannot drift on the trigger or the knob-raising advice.
|
|
109
|
-
if (contention.length > 0) {
|
|
110
|
-
out.push({
|
|
111
|
-
key: "contention",
|
|
112
|
-
tone: "amber",
|
|
113
|
-
title: plural(contention.length, "capacity bottleneck in the last day", "capacity bottlenecks in the last day"),
|
|
114
|
-
detail: contention.map((c) => `${c.name}: ${squash(c.detail, 200)}`).join(" · "),
|
|
115
|
-
actions: [],
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function pushWorkspacePinsAlert(out, workspacePins) {
|
|
120
|
-
// A stale workspace pin — the always-on twin of the doctor's `worktree pool` row's stuck-pin
|
|
121
|
-
// clause (doctor-contention-checks.ts checkWorktreePool): a slot pinned past a day by a run
|
|
122
|
-
// that never resumed silently shrinks the pool, and no other observer surface names it until
|
|
123
|
-
// an operator runs `tumwater doctor`. alert-feeds.ts feeds it the same staleWorkspacePins
|
|
124
|
-
// predicate the doctor's row uses, so the alert and the row cannot drift on what is stale;
|
|
125
|
-
// the "Open <role>" buttons reach the role drawer like the other role alerts.
|
|
126
|
-
if (workspacePins.length > 0) {
|
|
127
|
-
out.push({
|
|
128
|
-
key: "workspace-pins",
|
|
129
|
-
tone: "amber",
|
|
130
|
-
title: plural(workspacePins.length, "workspace pinned past a day", "workspaces pinned past a day"),
|
|
131
|
-
detail: `${workspacePins
|
|
132
|
-
.map(stalePinPhrase)
|
|
133
|
-
.join("; ")} — a pinned workspace is held out of the pool; resume the role, or retire its leftovers and run \`tumwater doctor\``,
|
|
134
|
-
actions: openRoleActions(workspacePins),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
function pushInstancePoolAlert(out, instancePool) {
|
|
139
|
-
// An enabled multi-instance role sharing a workspace pool smaller than the run cap — the
|
|
140
|
-
// always-on twin of the doctor `parallel instances` row's pool-mismatch clause
|
|
141
|
-
// (doctor-contention-checks.ts smallPoolProblems): instances then contend for the same
|
|
142
|
-
// pooled checkouts, unseen until an operator runs `tumwater doctor`. alert-feeds.ts feeds
|
|
143
|
-
// it the same smallPoolProblems selector the row uses, so the alert and the row cannot
|
|
144
|
-
// drift on the trigger, the role list, or the wording.
|
|
145
|
-
if (instancePool.length > 0) {
|
|
146
|
-
out.push({
|
|
147
|
-
key: "instance-pool",
|
|
148
|
-
tone: "amber",
|
|
149
|
-
title: "Parallel instances share a workspace pool smaller than the run cap",
|
|
150
|
-
detail: `${instancePool.join("; ")} — runs wait for a pooled checkout; raise worktreeSlots to at least maxConcurrent, or lower maxConcurrent.`,
|
|
151
|
-
actions: [],
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function pushRegressedFixesAlert(out, regressedFixes) {
|
|
156
|
-
// A Fixed record whose quoted test failed again in the trailing day — the always-on twin of
|
|
157
|
-
// the doctor's `regressed fixes` row (doctor-backlog-bugs.ts checkRegressedFixes).
|
|
158
|
-
// alert-feeds.ts's regressedFixesAlert feeds it the same candidate selection, failureMentions
|
|
159
|
-
// match and verbatim sentence, so the alert and the doctor cannot drift on the trigger or the
|
|
160
|
-
// wording; the Failures view shows the naming events.
|
|
161
|
-
if (regressedFixes) {
|
|
162
|
-
out.push({
|
|
163
|
-
key: "regressed-fixes",
|
|
164
|
-
tone: "amber",
|
|
165
|
-
title: plural(regressedFixes.count, "Fixed test has regressed", "Fixed tests have regressed"),
|
|
166
|
-
detail: regressedFixes.detail,
|
|
167
|
-
actions: seeFailures(),
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
1
|
+
/** The tail team alerts: a stopped tumwater. One of the per-domain builders split out of
|
|
2
|
+
* src/ui/team-alerts.ts; the shared types and wording helpers live in team-alert-shared.ts. */
|
|
3
|
+
import { plural } from "../text/phrases.js";
|
|
171
4
|
function pushStoppedAlert(out, snap) {
|
|
172
5
|
if (!snap.running) {
|
|
173
6
|
out.push({
|
|
@@ -179,60 +12,31 @@ function pushStoppedAlert(out, snap) {
|
|
|
179
12
|
});
|
|
180
13
|
}
|
|
181
14
|
}
|
|
182
|
-
function
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
const noTarget = textOnly.filter((l) => l.imageReroute == null || l.imageReroute === "");
|
|
191
|
-
if (noTarget.length > 0) {
|
|
15
|
+
function pushOrphansAlert(out, orphans) {
|
|
16
|
+
// Orphaned worktree processes — the always-on twin of the doctor's `orphans` row
|
|
17
|
+
// (doctor-orphans.ts checkOrphans): a detected orphan holds no lock, writes no event and
|
|
18
|
+
// touches no state file, so nothing else on the always-on surfaces names it until an operator
|
|
19
|
+
// runs `tumwater doctor`, and it then burns CPU for days unnoticed. alert-feeds.ts's
|
|
20
|
+
// getOrphanAlert feeds it the doctor's own orphanFailDetail sentence and count, so the alert
|
|
21
|
+
// and the row cannot drift on the trigger, the cpu, or the remediation.
|
|
22
|
+
if (orphans) {
|
|
192
23
|
out.push({
|
|
193
|
-
key: "
|
|
24
|
+
key: "orphans",
|
|
194
25
|
tone: "amber",
|
|
195
|
-
title:
|
|
196
|
-
detail:
|
|
197
|
-
actions: openRoleActions(noTarget),
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
else if (textOnly.length > 0) {
|
|
201
|
-
const targets = [...new Set(textOnly.map((l) => l.imageReroute ?? ""))].filter(Boolean);
|
|
202
|
-
out.push({
|
|
203
|
-
key: "images",
|
|
204
|
-
tone: "blue",
|
|
205
|
-
title: `${listRoles(textOnly)} ${agree(textOnly.length, "runs", "run")} on models that can't read images`,
|
|
206
|
-
detail: `A run that reads an image stops and reruns on ${targets.join(", ")}.`,
|
|
26
|
+
title: `${plural(orphans.count, "orphaned process", "orphaned processes")} are burning CPU (PPID 1)`,
|
|
27
|
+
detail: orphans.detail,
|
|
207
28
|
actions: [],
|
|
208
29
|
});
|
|
209
30
|
}
|
|
210
31
|
}
|
|
211
|
-
/** The
|
|
212
|
-
* instance-pool, stale-workspace-pin, regressed-fix, stopped and image-input alerts, in
|
|
213
|
-
* emission order: a run of quiet hours
|
|
214
|
-
* holding the team, a
|
|
215
|
-
* maintenance allowance held at its rolling cap, a standing provider failure hold, an operator
|
|
216
|
-
* pause with its countdown, the last day's capacity contention, a stale workspace pin, a
|
|
217
|
-
* fixed test that regressed, a
|
|
218
|
-
* stopped tumwater, and roles that cannot or may not read images. Extracted from teamAlerts
|
|
32
|
+
/** The stopped-tumwater alert, in emission order: a stopped team. Extracted from teamAlerts
|
|
219
33
|
* (part 5/5 of the teamAlerts split) so the shared module stays small; the input names exactly
|
|
220
|
-
* the fields this run reads
|
|
221
|
-
*
|
|
222
|
-
* occupy, so the emitted key order is unchanged. */
|
|
34
|
+
* the fields this run reads. The caller pushes the returned alerts at the position this run
|
|
35
|
+
* used to occupy, so the emitted key order is unchanged. */
|
|
223
36
|
export function buildTailAlerts(input) {
|
|
224
37
|
const out = [];
|
|
225
|
-
const { snap,
|
|
226
|
-
|
|
227
|
-
pushMaintenanceAlert(out, snap);
|
|
228
|
-
pushProviderHoldAlert(out, snap, now);
|
|
229
|
-
pushProviderCapsAlert(out, providerCaps);
|
|
230
|
-
pushPausedAlert(out, snap, now);
|
|
231
|
-
pushContentionAlert(out, contention);
|
|
232
|
-
pushWorkspacePinsAlert(out, workspacePins);
|
|
233
|
-
pushInstancePoolAlert(out, instancePool);
|
|
234
|
-
pushRegressedFixesAlert(out, regressedFixes);
|
|
38
|
+
const { snap, orphans } = input;
|
|
39
|
+
pushOrphansAlert(out, orphans);
|
|
235
40
|
pushStoppedAlert(out, snap);
|
|
236
|
-
pushImagesAlert(out, roles);
|
|
237
41
|
return out;
|
|
238
42
|
}
|
|
@@ -15,22 +15,21 @@ import { buildStatusAlerts } from "./team-alert-status.js";
|
|
|
15
15
|
import { buildBacklogAlerts } from "./team-alert-backlog.js";
|
|
16
16
|
import { buildTailAlerts } from "./team-alert-tail.js";
|
|
17
17
|
/** Everything about the team that needs the operator, most urgent first: a spent budget, a red
|
|
18
|
-
* main, roles failing run after run, roles that look stuck,
|
|
18
|
+
* main, roles failing run after run, roles that look stuck, disk and install drift, open
|
|
19
19
|
* questions, refused entries, a plan stalled by a Needs
|
|
20
20
|
* review note, the team
|
|
21
21
|
* waiting on capacity slots (workspace, provider or
|
|
22
|
-
* check),
|
|
22
|
+
* check), a plan
|
|
23
23
|
* filed under the wrong backlog section, a duplicated
|
|
24
24
|
* backlog section heading, finished history still under a live backlog file's `## Done` or
|
|
25
|
-
* `## Fixed`, a Fixed record with a bad Validation gap trace,
|
|
25
|
+
* `## Fixed`, a Fixed record with a bad Validation gap trace, and a
|
|
26
26
|
* stopped team.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export function teamAlerts(snap, questions, roles, now, refused = [], planDependency = null, stalledEntries = [], strandedPlans = [], duplicatedHeadings = [], validationGaps = [], contention = [], regressedFixes = null, staleClaims = [], staleQuestions = [], fixClaims = [], workspacePins = [], liveFinished = [], instancePool = [], providerCaps = []) {
|
|
27
|
+
*/
|
|
28
|
+
export function teamAlerts(snap, questions, roles, _now, refused = [], planDependency = null, stalledEntries = [], strandedPlans = [], duplicatedHeadings = [], validationGaps = [], staleQuestions = [], fixClaims = [], liveFinished = [], orphans = null) {
|
|
30
29
|
const out = [];
|
|
31
|
-
out.push(...buildStatusAlerts({ snap, roles
|
|
30
|
+
out.push(...buildStatusAlerts({ snap, roles }));
|
|
32
31
|
out.push(...buildBacklogAlerts({ questions, staleQuestions, refused, stalledEntries, planDependency, strandedPlans, duplicatedHeadings, liveFinished, validationGaps, fixClaims }));
|
|
33
|
-
out.push(...buildTailAlerts({ snap,
|
|
32
|
+
out.push(...buildTailAlerts({ snap, orphans }));
|
|
34
33
|
return out;
|
|
35
34
|
}
|
|
36
35
|
/** Does this alert ask something of the operator (red, amber, indigo), rather than inform? */
|
|
@@ -8,6 +8,31 @@ import { parsePiEventLine } from "../pi/pi-event-line.js";
|
|
|
8
8
|
* renderer stamps its separator from). A segment with neither is skipped. */
|
|
9
9
|
const MARKER_ONLY = new Set(["tumwater_run"]);
|
|
10
10
|
const MESSAGE_ONLY = new Set(["message_end"]);
|
|
11
|
+
function segmentsInText(text) {
|
|
12
|
+
const segments = [];
|
|
13
|
+
for (const line of text.split("\n")) {
|
|
14
|
+
if (line === "")
|
|
15
|
+
continue;
|
|
16
|
+
if (line.includes('"tumwater_run"') && parsePiEventLine(line, MARKER_ONLY))
|
|
17
|
+
segments.push([line]);
|
|
18
|
+
else
|
|
19
|
+
segments.at(-1)?.push(line);
|
|
20
|
+
}
|
|
21
|
+
return segments;
|
|
22
|
+
}
|
|
23
|
+
function keepInWindow(segments, fromTs, toTs) {
|
|
24
|
+
const kept = [];
|
|
25
|
+
for (const seg of segments) {
|
|
26
|
+
const start = segmentStart(seg);
|
|
27
|
+
if (start !== null && start >= fromTs && start <= (toTs ?? Infinity))
|
|
28
|
+
kept.push(...seg);
|
|
29
|
+
}
|
|
30
|
+
return kept;
|
|
31
|
+
}
|
|
32
|
+
function applyTranscriptGrep(entries, grep) {
|
|
33
|
+
const keep = createTranscriptFilter(grep);
|
|
34
|
+
return entries.flatMap((e) => keep(e) ?? []);
|
|
35
|
+
}
|
|
11
36
|
function segmentStart(lines) {
|
|
12
37
|
const marker = parsePiEventLine(lines[0], MARKER_ONLY);
|
|
13
38
|
if (typeof marker?.ts === "number")
|
|
@@ -35,24 +60,10 @@ export function readRunTranscript(file, fromTs, toTs, opts = {}) {
|
|
|
35
60
|
catch {
|
|
36
61
|
continue;
|
|
37
62
|
}
|
|
38
|
-
|
|
39
|
-
for (const line of text.split("\n")) {
|
|
40
|
-
if (line === "")
|
|
41
|
-
continue;
|
|
42
|
-
if (line.includes('"tumwater_run"') && parsePiEventLine(line, MARKER_ONLY))
|
|
43
|
-
segments.push([line]);
|
|
44
|
-
else
|
|
45
|
-
segments.at(-1)?.push(line);
|
|
46
|
-
}
|
|
47
|
-
for (const seg of segments) {
|
|
48
|
-
const start = segmentStart(seg);
|
|
49
|
-
if (start !== null && start >= fromTs && start <= (toTs ?? Infinity))
|
|
50
|
-
kept.push(...seg);
|
|
51
|
-
}
|
|
63
|
+
kept.push(...keepInWindow(segmentsInText(text), fromTs, toTs));
|
|
52
64
|
}
|
|
53
65
|
const entries = formatTranscript(kept, opts);
|
|
54
66
|
if (opts.grep === undefined)
|
|
55
67
|
return entries.flat();
|
|
56
|
-
|
|
57
|
-
return entries.flatMap((e) => keep(e) ?? []);
|
|
68
|
+
return applyTranscriptGrep(entries, opts.grep);
|
|
58
69
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { questionAsker, tryAnswerQuestion, UNCLOSED_FENCE_MESSAGE, } from "../../backlog/question-answer.js";
|
|
1
|
+
import { questionAsker, tryAnswerQuestion, UNCLOSED_FENCE_MESSAGE, } from "../../backlog/backlog-question-answer.js";
|
|
2
2
|
import { wakeQuestionAsker } from "../../operator/operator-intent.js";
|
|
3
|
+
import { savedPromptDraft } from "./tui-editor-session.js";
|
|
4
|
+
import { guardError } from "./tui-guard.js";
|
|
3
5
|
/** The backlog pane's answer editor (Ctrl+A on a selected open question) on the shared prompt
|
|
4
6
|
* line. It owns its target (`answerFor`), its saved-draft pair, its exit helper, and the
|
|
5
7
|
* tryAnswerQuestion / wake arms that record the decision. Mutually exclusive with the other
|
|
6
8
|
* editors via the session's held registry. */
|
|
7
9
|
export function createAnswerEditor(session) {
|
|
8
10
|
let answerFor = null;
|
|
9
|
-
|
|
11
|
+
const draft = savedPromptDraft(session);
|
|
10
12
|
// Leave the backlog pane's answer editor (Esc, a recorded answer, or Ctrl+T): restore the
|
|
11
13
|
// saved prompt text and reset the recall state, mirroring exitQueuedEdit.
|
|
12
14
|
const exitAnswerMode = () => {
|
|
@@ -14,7 +16,7 @@ export function createAnswerEditor(session) {
|
|
|
14
16
|
return;
|
|
15
17
|
answerFor = null;
|
|
16
18
|
session.held.answer = null;
|
|
17
|
-
|
|
19
|
+
draft.restore();
|
|
18
20
|
};
|
|
19
21
|
// Ctrl+A opens the answer editor on the selected open question (mnemonic: **A**nswer);
|
|
20
22
|
// Enter records the decision through tryAnswerQuestion, the core `tumwater questions
|
|
@@ -23,26 +25,26 @@ export function createAnswerEditor(session) {
|
|
|
23
25
|
const answerOpenEdit = (ctx) => {
|
|
24
26
|
if (!session.anyEditorHeld() &&
|
|
25
27
|
ctx.view === ctx.roleIds.length + 1 && ctx.selectedEntry !== null) {
|
|
26
|
-
|
|
28
|
+
const selected = ctx.selectedEntry;
|
|
29
|
+
if (selected === null)
|
|
30
|
+
return false;
|
|
31
|
+
guardError(session, () => {
|
|
27
32
|
const entries = session.flat();
|
|
28
|
-
const sel = Math.min(
|
|
33
|
+
const sel = Math.min(selected, entries.length - 1);
|
|
29
34
|
const entry = entries[sel];
|
|
30
35
|
if (entry?.label !== "question") {
|
|
31
36
|
session.flashMessage("only an open question can be answered here");
|
|
32
37
|
}
|
|
33
38
|
else {
|
|
34
39
|
const position = sel - entries.filter((e) => e.label === "plan" || e.label === "bug").length + 1;
|
|
35
|
-
|
|
40
|
+
draft.save();
|
|
36
41
|
answerFor = { position, title: entry.title };
|
|
37
42
|
session.held.answer = answerFor;
|
|
38
43
|
session.input = "";
|
|
39
44
|
session.cursor = 0;
|
|
40
45
|
session.flashMessage(`answering question #${position} — Enter to record, Esc to cancel`);
|
|
41
46
|
}
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
session.flashError(err);
|
|
45
|
-
}
|
|
47
|
+
});
|
|
46
48
|
return true;
|
|
47
49
|
}
|
|
48
50
|
return false;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { imagePromptGuidance } from "../../inbox/inbox-submit.js";
|
|
3
3
|
import { promptImagesCountProblem, readPromptImageFiles } from "../../inbox/inbox-attachments.js";
|
|
4
|
+
import { savedPromptDraft } from "./tui-editor-session.js";
|
|
4
5
|
/** The Activity view's Ctrl+V image-attach editor on the shared prompt line, the terminal
|
|
5
6
|
* twin of `tumwater prompt --attach <path>` and the dashboard composer's drop/paste. It owns
|
|
6
7
|
* its `attachMode` flag, its saved-draft pair, and the handleKey arms for its key (the toggle
|
|
@@ -12,7 +13,7 @@ import { promptImagesCountProblem, readPromptImageFiles } from "../../inbox/inbo
|
|
|
12
13
|
* their views. */
|
|
13
14
|
export function createAttachEditor(session) {
|
|
14
15
|
let attachMode = false;
|
|
15
|
-
|
|
16
|
+
const draft = savedPromptDraft(session);
|
|
16
17
|
// Leave attach mode (Esc, Ctrl+V again, or Ctrl+T): restore the saved prompt text. The
|
|
17
18
|
// pending session.images are untouched — Esc cancels the path edit, not an attachment
|
|
18
19
|
// already saved.
|
|
@@ -21,7 +22,7 @@ export function createAttachEditor(session) {
|
|
|
21
22
|
return;
|
|
22
23
|
attachMode = false;
|
|
23
24
|
session.held.attach = false;
|
|
24
|
-
|
|
25
|
+
draft.restore();
|
|
25
26
|
};
|
|
26
27
|
// Ctrl+V toggles image-attach mode on the Activity view's prompt line (mnemonic: attach
|
|
27
28
|
// **v**ia the same key the reset-counters and move-to-top arms use on their own views):
|
|
@@ -44,7 +45,7 @@ export function createAttachEditor(session) {
|
|
|
44
45
|
// Mid-recall, hand the arrows' saved draft back before the attach editor takes the
|
|
45
46
|
// line — the same settle the role editors do — so leaving restores the
|
|
46
47
|
// draft, not a history entry, with no stale recall state riding along.
|
|
47
|
-
|
|
48
|
+
draft.save();
|
|
48
49
|
attachMode = true;
|
|
49
50
|
session.held.attach = true;
|
|
50
51
|
session.input = "";
|
|
@@ -3,6 +3,7 @@ import { setConfigKey, unsetConfigKey } from "../../config/config-write.js";
|
|
|
3
3
|
import { loadConfigSafe, parseJsonConfig } from "../../config/config.js";
|
|
4
4
|
import { isJsonObject } from "../../files/json-object.js";
|
|
5
5
|
import { CONFIG_BASENAME, configPath } from "../../paths.js";
|
|
6
|
+
import { savedPromptDraft } from "./tui-editor-session.js";
|
|
6
7
|
/** The Ctrl+X agent-backends editor on the Activity view's shared prompt line — the terminal
|
|
7
8
|
* twin of the dashboard Settings `Agent backends (JSON)` field and `tumwater config set
|
|
8
9
|
* backends`, completing the run-concurrency knob set `tumwater doctor` and the dashboard
|
|
@@ -19,14 +20,14 @@ import { CONFIG_BASENAME, configPath } from "../../paths.js";
|
|
|
19
20
|
* arm. */
|
|
20
21
|
export function createBackendsEditor(session) {
|
|
21
22
|
let backendsMode = false;
|
|
22
|
-
|
|
23
|
+
const draft = savedPromptDraft(session);
|
|
23
24
|
// Leave backends mode (Esc, Ctrl+X again, or Ctrl+T): restore the saved prompt text.
|
|
24
25
|
const exitBackendsMode = () => {
|
|
25
26
|
if (!backendsMode)
|
|
26
27
|
return;
|
|
27
28
|
backendsMode = false;
|
|
28
29
|
session.held.backends = false;
|
|
29
|
-
|
|
30
|
+
draft.restore();
|
|
30
31
|
};
|
|
31
32
|
// Ctrl+X edits the team's agent backends on the Activity view (view 0) — the terminal
|
|
32
33
|
// twin of the dashboard Settings `Agent backends (JSON)` field and `tumwater config set
|
|
@@ -48,7 +49,7 @@ export function createBackendsEditor(session) {
|
|
|
48
49
|
if (session.editorEntryDenied("backends"))
|
|
49
50
|
return true;
|
|
50
51
|
const existing = rawBackends(session.root);
|
|
51
|
-
|
|
52
|
+
draft.save();
|
|
52
53
|
backendsMode = true;
|
|
53
54
|
session.held.backends = true;
|
|
54
55
|
session.input = existing !== undefined ? JSON.stringify(existing) : "";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { editBriefText } from "../../brief/brief-edit.js";
|
|
2
|
-
import { readEditorPath } from "./tui-editor-session.js";
|
|
2
|
+
import { readEditorPath, savedPromptDraft } from "./tui-editor-session.js";
|
|
3
|
+
import { flashReject } from "./tui-guard.js";
|
|
3
4
|
/** The Brief view's Ctrl+E replace-the-brief-from-a-file editor on the shared prompt line, the
|
|
4
5
|
* terminal twin of `tumwater brief --file <path>` and the dashboard's Brief card. It owns its
|
|
5
6
|
* `briefEdit` flag, its saved-draft pair, and the handleKey arms for its key (the toggle with
|
|
@@ -14,14 +15,14 @@ import { readEditorPath } from "./tui-editor-session.js";
|
|
|
14
15
|
* re-entering. */
|
|
15
16
|
export function createBriefEditor(session) {
|
|
16
17
|
let briefEdit = false;
|
|
17
|
-
|
|
18
|
+
const draft = savedPromptDraft(session);
|
|
18
19
|
// Leave the brief editor (Esc or a successful Enter): restore the saved prompt text.
|
|
19
20
|
const exitBriefMode = () => {
|
|
20
21
|
if (!briefEdit)
|
|
21
22
|
return;
|
|
22
23
|
briefEdit = false;
|
|
23
24
|
session.held.brief = false;
|
|
24
|
-
|
|
25
|
+
draft.restore();
|
|
25
26
|
};
|
|
26
27
|
// Ctrl+E opens the replace-the-brief editor on the Brief view's prompt line (mnemonic:
|
|
27
28
|
// **e**dit brief — the same key the Health reclaim, Report window-cycle, Queued row-edit
|
|
@@ -39,7 +40,7 @@ export function createBriefEditor(session) {
|
|
|
39
40
|
// Mid-recall, hand the arrows' saved draft back before the brief editor takes the line —
|
|
40
41
|
// the same settle the other editors do — so leaving restores the draft, not a history
|
|
41
42
|
// entry, with no stale recall state riding along.
|
|
42
|
-
|
|
43
|
+
draft.save();
|
|
43
44
|
briefEdit = true;
|
|
44
45
|
session.held.brief = true;
|
|
45
46
|
session.input = "";
|
|
@@ -75,11 +76,7 @@ export function createBriefEditor(session) {
|
|
|
75
76
|
session.flashMessage(outcome.changed
|
|
76
77
|
? `brief updated in ${outcome.file}${outcome.committed ? " (committed to main)" : ""}`
|
|
77
78
|
: "brief unchanged");
|
|
78
|
-
}, (
|
|
79
|
-
// The read/replace/commit threw: flash the reason and stay in the editor so the
|
|
80
|
-
// operator can fix things, as the other editor Enter arms do.
|
|
81
|
-
session.flashError(err);
|
|
82
|
-
});
|
|
79
|
+
}, flashReject(session));
|
|
83
80
|
return true;
|
|
84
81
|
};
|
|
85
82
|
const handleKey = (_str, key, ctx) => {
|
|
@@ -5,6 +5,7 @@ import { DIRECTOR_ROLE } from "../../roles/roles.js";
|
|
|
5
5
|
import { baseRoleOf } from "../../roles/runner-ids.js";
|
|
6
6
|
import { parseBudgetInput } from "./tui-input.js";
|
|
7
7
|
import { viewedRoleOrNull } from "./tui-editor-ctx.js";
|
|
8
|
+
import { savedPromptDraft } from "./tui-editor-session.js";
|
|
8
9
|
/** The Ctrl+B budget editor on the shared prompt line. It owns its mode flag, its saved-draft
|
|
9
10
|
* pair, the snapshot-fed cap/free flags `sync` drives, and the handleKey arms for its key: the
|
|
10
11
|
* toggle (with the `heldEditorFlash(entering)` guard clause, which keeps the shared guard
|
|
@@ -12,7 +13,7 @@ import { viewedRoleOrNull } from "./tui-editor-ctx.js";
|
|
|
12
13
|
* the one prompt line via the session's held registry. */
|
|
13
14
|
export function createBudgetEditor(session) {
|
|
14
15
|
let budgetMode = false;
|
|
15
|
-
|
|
16
|
+
const draft = savedPromptDraft(session);
|
|
16
17
|
// The last snapshot's cap, synced by render so the Ctrl+B handler can pre-fill without
|
|
17
18
|
// re-reading config itself (render already polls snapshot every second).
|
|
18
19
|
let currentCapUsd = 0;
|
|
@@ -29,7 +30,7 @@ export function createBudgetEditor(session) {
|
|
|
29
30
|
return;
|
|
30
31
|
budgetMode = false;
|
|
31
32
|
session.held.budget = false;
|
|
32
|
-
|
|
33
|
+
draft.restore();
|
|
33
34
|
lockedRole = null;
|
|
34
35
|
};
|
|
35
36
|
// Enter budget-edit mode (the shared entry, whether the team cap or a role's own cap is
|
|
@@ -37,7 +38,7 @@ export function createBudgetEditor(session) {
|
|
|
37
38
|
// pre-fill the line from the cap (empty string when disabled — empty means "no cap"), and
|
|
38
39
|
// announce the mode. Only the cap source and the announcement differ between the two scopes.
|
|
39
40
|
const enterBudgetMode = (cap, message) => {
|
|
40
|
-
|
|
41
|
+
draft.save();
|
|
41
42
|
budgetMode = true;
|
|
42
43
|
session.held.budget = true;
|
|
43
44
|
session.input = cap > 0 ? String(cap) : "";
|