tumwater 0.4.0 → 0.4.2
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 +4 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +5 -2
- package/dist/src/agent/backend-traits.js +21 -0
- package/dist/src/agent/claude/claude-backend.js +1 -1
- package/dist/src/agent/claude/claude-stream.js +181 -182
- package/dist/src/agent/claude/{write-guard-hook.js → claude-write-guard-hook.js} +1 -1
- package/dist/src/agent/claude/claude-write-guard.js +5 -4
- package/dist/src/agent/codex/codex-backend.js +1 -1
- package/dist/src/agent/codex/codex-rollout.js +48 -45
- package/dist/src/agent/codex/codex-stream.js +269 -215
- package/dist/src/agent/codex/{write-guard-hook.js → codex-write-guard-hook.js} +2 -1
- package/dist/src/agent/codex/codex-write-guard.js +3 -3
- package/dist/src/agent/image-input.js +18 -87
- 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 +2 -2
- package/dist/src/agent/opencode/opencode-config.js +2 -2
- package/dist/src/agent/opencode/opencode-stream.js +101 -92
- package/dist/src/agent/run-agent.js +76 -382
- package/dist/src/agent/run-settle.js +25 -15
- package/dist/src/agent/run-spawn.js +226 -0
- package/dist/src/agent/run-stream.js +185 -0
- package/dist/src/agent/session-ledger.js +16 -0
- package/dist/src/agent/write-guard.js +5 -3
- package/dist/src/backlog/backlog-archive.js +86 -170
- package/dist/src/backlog/backlog-cli-view.js +89 -70
- package/dist/src/backlog/backlog-eligibility.js +51 -59
- package/dist/src/backlog/backlog-file-bugs.js +133 -0
- package/dist/src/backlog/backlog-index-data.js +159 -0
- package/dist/src/backlog/backlog-index.js +7 -5
- package/dist/src/backlog/backlog-md.js +39 -28
- package/dist/src/backlog/backlog-release.js +21 -36
- package/dist/src/backlog/backlog-render.js +1 -1
- package/dist/src/backlog/backlog-structure.js +14 -145
- package/dist/src/backlog/backlog-write.js +31 -18
- package/dist/src/backlog/backlog.js +14 -4
- package/dist/src/backlog/question-answer.js +33 -15
- package/dist/src/baseline/main-baseline.js +71 -26
- package/dist/src/baseline/main-guardrails.js +76 -15
- package/dist/src/baseline/main-red.js +45 -94
- package/dist/src/baseline/main-watch.js +9 -3
- package/dist/src/brief/brief.js +40 -0
- package/dist/src/budget/budget-fallback-view.js +50 -8
- package/dist/src/budget/budget-wiring.js +15 -11
- package/dist/src/budget/budget.js +3 -2
- package/dist/src/build/build-check-events.js +4 -2
- package/dist/src/build/build-check-report.js +2 -1
- package/dist/src/build/build-check-scoped.js +70 -41
- package/dist/src/change/change-data.js +4 -2
- package/dist/src/cli/cli-args.js +9 -0
- package/dist/src/cli/cli-command-args.js +199 -98
- package/dist/src/cli/cli-flag-specs.js +6 -4
- package/dist/src/cli/cli-marker-commands.js +2 -2
- package/dist/src/cli/cli-query-commands.js +41 -6
- package/dist/src/cli/cli-review-commands.js +2 -1
- package/dist/src/cli/cli-role-command.js +46 -92
- package/dist/src/cli/cli-start.js +123 -47
- package/dist/src/cli/follow-view.js +3 -1
- package/dist/src/cli/help.js +19 -12
- package/dist/src/cli/log-commands.js +128 -107
- package/dist/src/cli/question-commands.js +82 -63
- package/dist/src/cli.js +396 -343
- package/dist/src/collections.js +46 -9
- package/dist/src/config/config-fallbacks.js +0 -4
- package/dist/src/config/config-live.js +179 -147
- package/dist/src/config/config-request.js +176 -51
- package/dist/src/config/config-validation-roles.js +271 -0
- package/dist/src/config/config-validation.js +10 -205
- package/dist/src/config/config-views.js +2 -7
- package/dist/src/config/config-write.js +205 -175
- package/dist/src/config/config.js +2 -1
- package/dist/src/doctor/doctor-backlog-bugs.js +24 -122
- package/dist/src/doctor/doctor-backlog-plans.js +24 -25
- package/dist/src/doctor/doctor-backlog.js +34 -42
- package/dist/src/doctor/doctor-check-names.js +4 -2
- package/dist/src/doctor/doctor-checks.js +16 -4
- package/dist/src/doctor/doctor-claude-checks.js +2 -2
- package/dist/src/doctor/doctor-codex-checks.js +9 -20
- package/dist/src/doctor/doctor-contention-checks.js +52 -19
- package/dist/src/doctor/doctor-holds.js +22 -20
- package/dist/src/doctor/doctor-model-checks.js +8 -184
- package/dist/src/doctor/doctor-notify.js +2 -5
- package/dist/src/doctor/doctor-opencode-checks.js +90 -41
- package/dist/src/doctor/doctor-principles.js +26 -0
- package/dist/src/doctor/doctor-project-checks.js +69 -19
- package/dist/src/doctor/doctor-role-notes.js +33 -0
- package/dist/src/doctor/doctor.js +13 -6
- package/dist/src/doctor/probe.js +1 -1
- package/dist/src/events/event-format.js +307 -162
- package/dist/src/events/event-labels.js +0 -1
- package/dist/src/events/event-legacy.js +1 -1
- package/dist/src/events/event-window.js +1 -1
- package/dist/src/events/events.js +15 -2
- package/dist/src/events/notify.js +1 -1
- package/dist/src/failure/failure-cluster.js +4 -3
- package/dist/src/failure/failure-data.js +126 -94
- package/dist/src/failure/failure-render.js +58 -17
- package/dist/src/failure/failure-state-change.js +308 -27
- package/dist/src/failure/time-spend.js +139 -106
- package/dist/src/files/files.js +7 -0
- package/dist/src/files/json-object.js +15 -2
- package/dist/src/gates/budget-gates.js +90 -68
- package/dist/src/gates/gate-polls.js +92 -61
- package/dist/src/gates/role-cap-gates.js +1 -1
- package/dist/src/gates/startup-gate.js +28 -28
- package/dist/src/gates/streak-gate.js +1 -1
- package/dist/src/git/git.js +1 -1
- package/dist/src/git/slot-salvage.js +9 -9
- package/dist/src/git/slots-state.js +12 -3
- package/dist/src/git/worktree-pool-startup.js +127 -0
- package/dist/src/git/worktree-pool.js +1 -120
- package/dist/src/gui/gui-admin-endpoints.js +3 -4
- package/dist/src/gui/gui-args.js +1 -1
- package/dist/src/gui/gui-backlog-commands.js +20 -11
- package/dist/src/gui/gui-backlog-endpoints.js +2 -8
- package/dist/src/gui/gui-config-commands.js +51 -76
- package/dist/src/gui/gui-endpoint-commands.js +4 -2
- package/dist/src/gui/gui-endpoint-common.js +4 -3
- package/dist/src/gui/gui-history-endpoints.js +36 -27
- package/dist/src/gui/gui-operator-commands.js +43 -17
- package/dist/src/gui/gui-prompt-commands.js +27 -21
- package/dist/src/gui/gui-quarantine-commands.js +30 -0
- package/dist/src/gui/gui-report-endpoints.js +13 -9
- package/dist/src/gui/gui-server.js +5 -3
- package/dist/src/gui/gui-team-commands.js +1 -1
- package/dist/src/history/history-data.js +7 -1
- package/dist/src/history/history-render.js +15 -10
- package/dist/src/history/history.js +103 -68
- package/dist/src/inbox/inbox-submit.js +1 -5
- package/dist/src/inbox/pending-prompt.js +3 -2
- package/dist/src/inbox/prompt-commands.js +194 -161
- package/dist/src/init/init.js +104 -25
- package/dist/src/merge/merge-core.js +1 -1
- package/dist/src/merge/merge-drain.js +1 -1
- package/dist/src/merge/merge-git.js +5 -5
- package/dist/src/merge/{merge-post-review.js → merge-post-review-drain.js} +1 -1
- package/dist/src/merge/merge-queue.js +2 -1
- package/dist/src/merge/merge-to-main.js +1 -1
- package/dist/src/merge/merge-verify.js +3 -2
- package/dist/src/operator/operator-commands.js +84 -52
- package/dist/src/operator/operator-intent.js +26 -3
- package/dist/src/operator/retire.js +2 -1
- package/dist/src/operator/revert.js +18 -12
- package/dist/src/orchestrator/orchestrator-poll.js +2 -2
- package/dist/src/orchestrator/orchestrator.js +1 -1
- package/dist/src/orchestrator/retention.js +6 -5
- package/dist/src/paths.js +10 -10
- package/dist/src/pi/pi-bin.js +2 -2
- package/dist/src/pi/pi-models.js +9 -0
- package/dist/src/pi/pi-stream.js +63 -41
- package/dist/src/pi/pi-watchdogs.js +84 -49
- package/dist/src/pi-extension/context-shake.js +55 -31
- package/dist/src/pi-extension/full-output.js +2 -3
- package/dist/src/process/process.js +24 -1
- package/dist/src/process/run-marker.js +2 -2
- package/dist/src/process/supervisor.js +24 -5
- package/dist/src/prompt/gate-notes.js +41 -16
- package/dist/src/prompt/notes-backlog.js +22 -29
- package/dist/src/prompt/principles.js +11 -0
- package/dist/src/prompt/prompt-followup.js +4 -12
- package/dist/src/prompt/prompt.js +15 -12
- package/dist/src/redeploy/redeploy-probes.js +11 -1
- package/dist/src/redeploy/redeployer.js +54 -7
- package/dist/src/redeploy/self-reload.js +7 -2
- package/dist/src/report/report-data.js +224 -320
- package/dist/src/report/report-fold.js +23 -231
- package/dist/src/report/report-main-red.js +70 -3
- package/dist/src/report/report-render.js +181 -48
- package/dist/src/report/report-since-data.js +218 -0
- package/dist/src/report/report-usage-fold.js +288 -0
- package/dist/src/report/report.js +2 -1
- package/dist/src/review/known-flakes.js +22 -3
- package/dist/src/review/post-merge-review-accept.js +3 -2
- package/dist/src/review/post-merge-review.js +16 -14
- package/dist/src/review/quarantine-render.js +29 -11
- package/dist/src/review/review-precheck.js +2 -1
- package/dist/src/roles/role-catalog.js +30 -29
- package/dist/src/roles/role-guidance.js +26 -43
- package/dist/src/roles/role-render.js +19 -0
- package/dist/src/roles/role-view.js +19 -5
- package/dist/src/roles/roles.js +2 -22
- package/dist/src/run/ci-parity.js +5 -6
- package/dist/src/run/feedback-filings.js +10 -1
- package/dist/src/run/qa-coverage.js +4 -4
- package/dist/src/run/run-apply.js +14 -2
- package/dist/src/run/run-detail-data.js +50 -10
- package/dist/src/run/run-detail-render.js +32 -1
- package/dist/src/run/run-detail.js +44 -22
- package/dist/src/run/run-dismiss.js +43 -0
- package/dist/src/run/run-finalize.js +75 -46
- package/dist/src/run/run-prompt.js +271 -187
- package/dist/src/run/run-resume.js +32 -31
- package/dist/src/run/run-stage.js +116 -64
- package/dist/src/run/run-timing.js +18 -4
- package/dist/src/run/run-verdict.js +119 -83
- package/dist/src/run/stage-backlog-checks.js +4 -31
- package/dist/src/run/stage-check.js +4 -12
- package/dist/src/run/stage-checks-shared.js +15 -0
- package/dist/src/run/stage-name-checks.js +3 -13
- package/dist/src/run/stage-path-checks.js +26 -29
- package/dist/src/runner/leftover.js +13 -4
- package/dist/src/runner/revision.js +2 -2
- package/dist/src/runner/runner-fallback.js +1 -1
- package/dist/src/runner/runner-outcome.js +0 -5
- package/dist/src/runner/runner-pi.js +72 -49
- package/dist/src/runner/runner-run.js +19 -102
- package/dist/src/runner/runner-state-load.js +0 -10
- package/dist/src/runner/runner.js +1 -32
- package/dist/src/scheduling/claims.js +4 -11
- package/dist/src/status/status-data.js +140 -91
- package/dist/src/status/status-polls.js +41 -5
- package/dist/src/status/status-role-notes.js +53 -0
- package/dist/src/status/status-roles.js +10 -37
- package/dist/src/text/datetime.js +8 -1
- package/dist/src/text/format.js +13 -1
- package/dist/src/text/phrases.js +16 -2
- package/dist/src/text/text.js +12 -0
- package/dist/src/ui/alert-feeds.js +115 -0
- package/dist/src/ui/badges.js +3 -4
- package/dist/src/ui/gui/gui-client-boot.js +78 -57
- package/dist/src/ui/gui/gui-client-drawer-config.js +8 -3
- package/dist/src/ui/gui/gui-client-drawer.js +18 -2
- package/dist/src/ui/gui/gui-client-feed.js +3 -8
- package/dist/src/ui/gui/gui-client-history.js +16 -16
- package/dist/src/ui/gui/gui-client-model.js +9 -9
- package/dist/src/ui/gui/gui-client-operator.js +6 -10
- package/dist/src/ui/gui/gui-client-pending.js +4 -4
- package/dist/src/ui/gui/gui-client-report-chart.js +111 -10
- package/dist/src/ui/gui/gui-client-report.js +70 -11
- package/dist/src/ui/gui/gui-client-roles.js +52 -12
- package/dist/src/ui/gui/gui-client-settings.js +31 -26
- package/dist/src/ui/gui/gui-client-team.js +28 -2
- package/dist/src/ui/gui/gui-client.js +33 -4
- package/dist/src/ui/gui/gui-icons.js +7 -1
- package/dist/src/ui/progress-data.js +2 -2
- package/dist/src/ui/run-progress-model.js +2 -2
- package/dist/src/ui/status-model.js +26 -16
- package/dist/src/ui/status-payload.js +149 -326
- package/dist/src/ui/status-render.js +131 -80
- package/dist/src/ui/team-alert-backlog.js +236 -0
- package/dist/src/ui/team-alert-shared.js +92 -0
- package/dist/src/ui/team-alert-status.js +326 -0
- package/dist/src/ui/team-alert-tail.js +238 -0
- package/dist/src/ui/team-alerts.js +16 -984
- package/dist/src/ui/transcript-tail.js +137 -94
- package/dist/src/ui/tui/tui-backlog.js +1 -1
- package/dist/src/ui/tui/tui-editor-answer.js +54 -44
- package/dist/src/ui/tui/tui-editor-attach.js +74 -63
- package/dist/src/ui/tui/tui-editor-backends.js +62 -56
- package/dist/src/ui/tui/tui-editor-brief.js +64 -54
- package/dist/src/ui/tui/tui-editor-budget.js +106 -89
- package/dist/src/ui/tui/tui-editor-config.js +2 -4
- package/dist/src/ui/tui/tui-editor-ctx.js +12 -1
- package/dist/src/ui/tui/tui-editor-entry.js +171 -153
- package/dist/src/ui/tui/tui-editor-instances.js +2 -1
- package/dist/src/ui/tui/tui-editor-instructions.js +50 -107
- package/dist/src/ui/tui/tui-editor-interval.js +39 -112
- package/dist/src/ui/tui/tui-editor-notes.js +28 -84
- package/dist/src/ui/tui/tui-editor-pause.js +210 -183
- package/dist/src/ui/tui/tui-editor-prompt.js +46 -35
- package/dist/src/ui/tui/tui-editor-queued.js +74 -64
- package/dist/src/ui/tui/tui-editor-quiet.js +122 -108
- package/dist/src/ui/tui/tui-editor-release.js +85 -0
- package/dist/src/ui/tui/tui-editor-retire.js +57 -46
- package/dist/src/ui/tui/tui-editor-role-enabled.js +90 -80
- package/dist/src/ui/tui/tui-editor-role-file.js +87 -0
- package/dist/src/ui/tui/tui-editor-role-key.js +50 -42
- package/dist/src/ui/tui/tui-editor-search.js +70 -58
- package/dist/src/ui/tui/tui-editor-session.js +3 -0
- package/dist/src/ui/tui/tui-editors.js +4 -1
- package/dist/src/ui/tui/tui-frame.js +7 -3
- package/dist/src/ui/tui/tui-history.js +12 -10
- package/dist/src/ui/tui/tui-keys.js +6 -1
- package/dist/src/ui/tui/tui-pane-activity.js +62 -50
- package/dist/src/ui/tui/tui-pane-backlog.js +4 -4
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +45 -26
- package/dist/src/ui/tui/tui-pane-history.js +296 -264
- package/dist/src/ui/tui/tui-pane-report.js +74 -57
- package/dist/src/ui/tui/tui-pane-session.js +6 -2
- package/dist/src/ui/tui/tui-pane-transcript.js +158 -131
- package/dist/src/ui/tui/tui-pane.js +201 -174
- package/dist/src/ui/tui/tui-panes.js +211 -148
- package/dist/src/ui/tui/tui.js +4 -4
- package/dist/src/verdict/fix-claim.js +18 -6
- package/dist/src/verdict/refusal.js +32 -7
- package/package.json +1 -1
- package/dist/src/doctor/image-probe.js +0 -257
- /package/dist/src/gui/{http-body.js → gui-http-body.js} +0 -0
- /package/dist/src/merge/{backlog-conflicts.js → merge-backlog-conflicts.js} +0 -0
|
@@ -3,7 +3,7 @@ import { eventRole, eventRun, eventUsage } from "./event-read.js";
|
|
|
3
3
|
import { eventTypeLabel } from "./event-labels.js";
|
|
4
4
|
import { roleLabel } from "../roles/role-labels.js";
|
|
5
5
|
import { TUMWATER_SOURCE } from "./events.js";
|
|
6
|
-
import { orchestratorStopPhrase, backendKindPhrase, budgetPhrase, firstReason, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase, PAUSE_EXPIRED_SUFFIX } from "../text/phrases.js";
|
|
6
|
+
import { orchestratorStopPhrase, backendKindPhrase, budgetPhrase, firstReason, teamPausePhrase, teamResumePhrase, holdPhrase, plural, rolePausePhrase, roleResumePhrase, rolesPhrase, shortSpanPhrase, rendersAsBackendFailure, PAUSE_EXPIRED_SUFFIX } from "../text/phrases.js";
|
|
7
7
|
import { compactTokens, formatGB, shortSha, usd } from "../text/format.js";
|
|
8
8
|
import { padToWidth } from "../text/text-width.js";
|
|
9
9
|
import { isNonBlankString, joinWithMore, squash, textOr, truncate } from "../text/text.js";
|
|
@@ -54,7 +54,7 @@ function elapsed(ms) {
|
|
|
54
54
|
* missing or non-numeric reads as "0.0 GB", never "NaN" — the same corrupt-field rule
|
|
55
55
|
* budgetPhrase applies to spentUsd/capUsd, since HarnessEvent's index signature hands every
|
|
56
56
|
* field over unvalidated. The rounding and unit are text/format.ts's formatGB. */
|
|
57
|
-
function gigabytes(v) {
|
|
57
|
+
export function gigabytes(v) {
|
|
58
58
|
return formatGB(finiteNumber(v, 0));
|
|
59
59
|
}
|
|
60
60
|
/** A duration in whole minutes. A torn or hand-edited value that is missing or non-numeric
|
|
@@ -81,7 +81,7 @@ function pauseSuffix(until, reason) {
|
|
|
81
81
|
/** `, resets HH:MM` for a subscription-window event carrying resetsAt, or "" when the field is
|
|
82
82
|
* absent or non-numeric (a torn line, or a window the account did not stamp) — the same
|
|
83
83
|
* corrupt-field rule the other optional-field renderers apply. */
|
|
84
|
-
function windowResetPhrase(e) {
|
|
84
|
+
export function windowResetPhrase(e) {
|
|
85
85
|
const at = finiteNumber(e.resetsAt, null);
|
|
86
86
|
return at === null ? "" : `, resets ${formatClock(at)}`;
|
|
87
87
|
}
|
|
@@ -89,7 +89,7 @@ function windowResetPhrase(e) {
|
|
|
89
89
|
* the field is absent or blank (pollQuietHoursGate may send no window, so a routine hold must
|
|
90
90
|
* not fabricate a pair). Shared by the started and ended lines so the two cannot drift on the
|
|
91
91
|
* window's presence or format. */
|
|
92
|
-
function windowSuffix(e) {
|
|
92
|
+
export function windowSuffix(e) {
|
|
93
93
|
return isNonBlankString(e.window) ? ` (${textOr(e.window)})` : "";
|
|
94
94
|
}
|
|
95
95
|
/** The backend a hold event names, rendered as " at <provider>" when one is configured
|
|
@@ -108,7 +108,7 @@ function providerScope(e) {
|
|
|
108
108
|
* later backend's own id), falling back to "claude" for an event written before the window
|
|
109
109
|
* events carried a `backend`. A claude-only team's lines read exactly as the older
|
|
110
110
|
* `claude_window_*` lines did. */
|
|
111
|
-
function backendLabel(e) {
|
|
111
|
+
export function backendLabel(e) {
|
|
112
112
|
return typeof e.backend === "string" && e.backend !== "" ? e.backend : "claude";
|
|
113
113
|
}
|
|
114
114
|
/** The provider/model pair a fallback-ish line names, as "provider/model", defaulting each
|
|
@@ -182,10 +182,7 @@ export function eventFeedItem(e) {
|
|
|
182
182
|
export function eventMatchesGrep(e, patternLower) {
|
|
183
183
|
return `${e.type} ${formatEvent(e)}`.toLowerCase().includes(patternLower);
|
|
184
184
|
}
|
|
185
|
-
|
|
186
|
-
* activity feed renders the time and role itself (relative ages, role chips), so the payload
|
|
187
|
-
* ships this beside the event's type and role instead of making the page re-parse a line. */
|
|
188
|
-
export function eventMessage(e) {
|
|
185
|
+
function runEventMessage(e) {
|
|
189
186
|
switch (e.type) {
|
|
190
187
|
case "run_start":
|
|
191
188
|
return `run #${eventRun(e)} started${e.model !== undefined ? ` on ${textOr(e.model)}` : ""}`;
|
|
@@ -198,6 +195,53 @@ export function eventMessage(e) {
|
|
|
198
195
|
// Per-run usage: where the day's spend went.
|
|
199
196
|
return `run #${eventRun(e)} ${eventTypeLabel(outcomeText(e))}${extra}${usagePhrase(e)}`;
|
|
200
197
|
}
|
|
198
|
+
case "run_deferred":
|
|
199
|
+
// Routine state change (need-based prioritization), like counters_reset — no warning
|
|
200
|
+
// prefix. One per deferral episode; the run's own events cover the episode's end.
|
|
201
|
+
return `deferred — ${deferredReasonText(e)}`;
|
|
202
|
+
case "run_aborted":
|
|
203
|
+
// Routine state change (the user stopped one role's run), like counters_reset — no
|
|
204
|
+
// warning prefix. The resulting run_end line carries the user_aborted outcome.
|
|
205
|
+
return `run stopped by you`;
|
|
206
|
+
case "resume": {
|
|
207
|
+
// Two causes share the resume machinery; the line names the real one so an operator
|
|
208
|
+
// reading the feed can tell a restart from a role fighting the context ceiling, and a
|
|
209
|
+
// cross-agent handoff from either — the handoff keeps the worktree but starts fresh. A
|
|
210
|
+
// run that got a salvaged patch back (src/git/slot-salvage.ts) names the patch.
|
|
211
|
+
const restored = e.restored === undefined ? "" : ` — uncommitted edits restored from ${textOr(e.restored)}`;
|
|
212
|
+
return ((e.cause === "cut-off"
|
|
213
|
+
? "resuming the run cut off at the context ceiling (compacted pi session, same workspace)"
|
|
214
|
+
: e.cause === "handoff"
|
|
215
|
+
? "handing the interrupted run off to a fresh session on another agent (same workspace, uncommitted edits kept)"
|
|
216
|
+
: e.cause === "salvaged"
|
|
217
|
+
? "handing the work an earlier run left unsaved to a fresh session"
|
|
218
|
+
: "resuming the run a shutdown interrupted (same agent session and workspace)") + restored);
|
|
219
|
+
}
|
|
220
|
+
case "wake":
|
|
221
|
+
return `woke (${textOr(e.reason)})`;
|
|
222
|
+
case "counters_reset": {
|
|
223
|
+
// One role → the event is filed under that role; several → one harness-level event
|
|
224
|
+
// listing them.
|
|
225
|
+
const roles = rolesPhrase(e.roles, "");
|
|
226
|
+
const scope = roles ? ` for ${roles}` : "";
|
|
227
|
+
return `counters reset${scope} (runs, merges, tokens, cost)`;
|
|
228
|
+
}
|
|
229
|
+
case "claim":
|
|
230
|
+
// Parallel work instances (plans/parallel-work-instances.md "Observability", part 6/7):
|
|
231
|
+
// the assignment/release is the harness working as designed, so no warning prefix.
|
|
232
|
+
return e.action === "released"
|
|
233
|
+
? `released "${textOr(e.title)}" (${textOr(e.reason, "?")})`
|
|
234
|
+
: `assigned "${textOr(e.title)}"`;
|
|
235
|
+
case "permit_reserve":
|
|
236
|
+
// Priority headroom (part 7/7): a reserve appearing or vanishing is the scheduler working
|
|
237
|
+
// as designed; it names the permits withheld for the work tier so an operator can see why
|
|
238
|
+
// maintenance concurrency dipped.
|
|
239
|
+
return `${plural(finiteNumber(e.reserve, 0), "permit")} reserved for work roles that have work to take`;
|
|
240
|
+
default: return textOr(e.type);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function mergeEventMessage(e) {
|
|
244
|
+
switch (e.type) {
|
|
201
245
|
case "merged":
|
|
202
246
|
return `merged ${shortSha(e.commit)} to main — ${textOr(e.summary)}`;
|
|
203
247
|
case "merge_queued":
|
|
@@ -226,58 +270,46 @@ export function eventMessage(e) {
|
|
|
226
270
|
// The event names its reason itself (writeMergeOutcome); the review/check events still
|
|
227
271
|
// carry the full text. An old event without `reason` renders the bare outcome.
|
|
228
272
|
return `merge of ${shortSha(e.commit)} failed (${eventTypeLabel(outcomeText(e))}${isNonBlankString(e.reason) ? `: ${truncate(e.reason.trim(), 120)}` : ""})${elapsed(e.durationMs)}${isNonBlankString(e.summary) ? ` — ${truncate(e.summary, 80)}` : ""}`;
|
|
229
|
-
case "
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const titles = stringList(e.entries);
|
|
237
|
-
if (titles.length === 0)
|
|
238
|
-
return `entry refused: ${textOr(e.reason)} (${files})`;
|
|
239
|
-
const more = titles.length > 1 ? ` (and ${titles.length - 1} more)` : "";
|
|
240
|
-
return `entry refused: "${truncate(titles[0], 80)}"${more}: ${textOr(e.reason)} (${files})`;
|
|
241
|
-
case "issue_filed":
|
|
242
|
-
// The feedback observer's filing (plans/feedback-role.md 2/2) is a visible, auditable
|
|
243
|
-
// result — routine operation like question_posted, not a warning. The number is the
|
|
244
|
-
// issue the operator can open; a torn event without it reads "?" never "undefined".
|
|
245
|
-
return `filed issue #${typeof e.issue === "number" ? e.issue : "?"} ${textOr(e.title)} on znation/tumwater`;
|
|
246
|
-
case "wake":
|
|
247
|
-
return `woke (${textOr(e.reason)})`;
|
|
248
|
-
case "run_deferred":
|
|
249
|
-
// Routine state change (need-based prioritization), like counters_reset — no warning
|
|
250
|
-
// prefix. One per deferral episode; the run's own events cover the episode's end.
|
|
251
|
-
return `deferred — ${deferredReasonText(e)}`;
|
|
252
|
-
case "orchestrator_start":
|
|
253
|
-
return `tumwater started (pid ${textOr(e.pid)}${e.build ? `, version ${shortSha(e.build)}` : ""})`;
|
|
254
|
-
case "orchestrator_stop":
|
|
255
|
-
return orchestratorStopPhrase(e.cause);
|
|
256
|
-
case "prompt_enqueued":
|
|
257
|
-
return `message added to the inbox: ${textOr(e.preview)}`;
|
|
258
|
-
case "prompt_cancelled":
|
|
259
|
-
// Routine operation (the user removed a queued prompt), not a warning.
|
|
260
|
-
return `message removed from the inbox: ${textOr(e.preview)}`;
|
|
261
|
-
case "prompt_edited":
|
|
262
|
-
// Routine operation (the user corrected a queued prompt), not a warning.
|
|
263
|
-
return `inbox message edited: ${textOr(e.preview)}`;
|
|
264
|
-
case "prompt_topped":
|
|
265
|
-
// Routine operation (the user reordered a queued prompt), not a warning.
|
|
266
|
-
return `inbox message moved to the front: ${textOr(e.preview)}`;
|
|
267
|
-
case "prompt_taken":
|
|
268
|
-
// Routine: a run dequeued the operator's request to run it.
|
|
269
|
-
return `inbox message taken by run #${eventRun(e)}: ${textOr(e.preview)}`;
|
|
270
|
-
case "counters_reset": {
|
|
271
|
-
// One role → the event is filed under that role; several → one harness-level event
|
|
272
|
-
// listing them.
|
|
273
|
-
const roles = rolesPhrase(e.roles, "");
|
|
274
|
-
const scope = roles ? ` for ${roles}` : "";
|
|
275
|
-
return `counters reset${scope} (runs, merges, tokens, cost)`;
|
|
273
|
+
case "conflict_handback": {
|
|
274
|
+
// A change whose merges kept conflicting was handed back to its author with the markers
|
|
275
|
+
// in place (PLANS.md "Robust conflict landing, part 2/2"): queued, applied, or failed.
|
|
276
|
+
const files = stringList(e.conflicted);
|
|
277
|
+
const where = files.length > 0 ? `: ${files.join(", ")}` : "";
|
|
278
|
+
const why = e.reason === "revision" ? " (revision)" : "";
|
|
279
|
+
return `conflict hand-back ${textOr(e.action)} ${shortSha(e.sha)}${why}${where}`;
|
|
276
280
|
}
|
|
277
|
-
case "
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
+
case "operator_revert": {
|
|
282
|
+
const undid = typeof e.runner === "string" && e.runner !== "" ? `, undid ${e.runner}` : "";
|
|
283
|
+
return `reverted ${shortSha(String(e.commit ?? ""))} (by operator) as ${shortSha(String(e.revert ?? ""))}${undid}`; // vocabulary-ok: the deliberate-revert writer is the operator role
|
|
284
|
+
}
|
|
285
|
+
case "main_check": {
|
|
286
|
+
const failing = Array.isArray(e.failingTests) ? e.failingTests.map(String) : [];
|
|
287
|
+
// The commit count renders only when it is a real number over 1 — the corrupt-field rule
|
|
288
|
+
// this file applies to every event number (a hand-edited line's quoted "3" or "0x10" reads
|
|
289
|
+
// as no covered count, not as a coerced parenthetical).
|
|
290
|
+
const coveredN = finiteNumber(e.covered, null);
|
|
291
|
+
const covered = coveredN !== null && coveredN > 1 ? ` (${coveredN} commits)` : "";
|
|
292
|
+
const verdict = e.status === "failed" ? "failed" : "passed";
|
|
293
|
+
const names = failing.length > 0 ? `: ${joinWithMore(failing, 3, "; ")}` : "";
|
|
294
|
+
return `main ${shortSha(String(e.sha ?? ""))}${covered} full check ${verdict}${names}`;
|
|
295
|
+
}
|
|
296
|
+
case "main_culprit": {
|
|
297
|
+
const by = typeof e.runner === "string" && e.runner !== "" ? ` (merged by ${e.runner})` : "";
|
|
298
|
+
return `main turned red at ${shortSha(String(e.commit ?? ""))}${by}`;
|
|
299
|
+
}
|
|
300
|
+
case "main_red_hold":
|
|
301
|
+
// The message already names the SHA, script, and consequence; the problem tone comes
|
|
302
|
+
// from PROBLEM_EVENTS.
|
|
303
|
+
return textOr(e.message);
|
|
304
|
+
default: return textOr(e.type);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/** " after merge" when the review is a post-merge review (review.afterMerge), else "". */
|
|
308
|
+
function afterMergeSuffix(e) {
|
|
309
|
+
return e.afterMerge === true ? " after merge" : "";
|
|
310
|
+
}
|
|
311
|
+
function reviewEventMessage(e) {
|
|
312
|
+
switch (e.type) {
|
|
281
313
|
case "review_start": {
|
|
282
314
|
// A resumed review continues a timed-out reviewer's session (PLANS.md, review-retry part
|
|
283
315
|
// 3/3). The verb names that, because a bare "reviewing" line reads as a from-scratch run;
|
|
@@ -290,7 +322,7 @@ export function eventMessage(e) {
|
|
|
290
322
|
return `${e.resumed ? "review resumed" : "reviewing"} ${shortSha(e.head)}${revision}${tier}${model}`;
|
|
291
323
|
}
|
|
292
324
|
case "review_verdict":
|
|
293
|
-
return `review approved ${shortSha(e.head)}${e
|
|
325
|
+
return `review approved ${shortSha(e.head)}${afterMergeSuffix(e)}${e.reason ? ` — ${textOr(e.reason)}` : ""}${elapsed(e.durationMs)}`;
|
|
294
326
|
case "review_rejected": {
|
|
295
327
|
const reasons = stringList(e.reasons);
|
|
296
328
|
// Only the first reason renders; when more exist, say so instead of letting the
|
|
@@ -299,7 +331,7 @@ export function eventMessage(e) {
|
|
|
299
331
|
const more = reasons.length > 1
|
|
300
332
|
? ` (+${reasons.length - 1} more — the author's next run carries every reason)`
|
|
301
333
|
: "";
|
|
302
|
-
return `review rejected ${shortSha(e.head)}${e
|
|
334
|
+
return `review rejected ${shortSha(e.head)}${afterMergeSuffix(e)} — ${firstReason(reasons)}${more}${elapsed(e.durationMs)}`;
|
|
303
335
|
}
|
|
304
336
|
case "review_failed":
|
|
305
337
|
return `review failed for ${shortSha(e.head)}: ${textOr(e.message)} (commit kept for re-review)${elapsed(e.durationMs)}`;
|
|
@@ -307,28 +339,47 @@ export function eventMessage(e) {
|
|
|
307
339
|
// A rejected change's revision round (plans/revise-rejected.md): applied onto current main,
|
|
308
340
|
// conflicted with it, dropped by its author, or exhausted its rounds.
|
|
309
341
|
return `revision ${textOr(e.round)} ${textOr(e.action)} ${shortSha(e.sha)}`;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return `
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
//
|
|
325
|
-
return `
|
|
326
|
-
case "
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
342
|
+
default: return textOr(e.type);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function inboxEventMessage(e) {
|
|
346
|
+
switch (e.type) {
|
|
347
|
+
case "prompt_enqueued":
|
|
348
|
+
return `message added to the inbox: ${textOr(e.preview)}`;
|
|
349
|
+
case "prompt_cancelled":
|
|
350
|
+
// Routine operation (the user removed a queued prompt), not a warning.
|
|
351
|
+
return `message removed from the inbox: ${textOr(e.preview)}`;
|
|
352
|
+
case "prompt_edited":
|
|
353
|
+
// Routine operation (the user corrected a queued prompt), not a warning.
|
|
354
|
+
return `inbox message edited: ${textOr(e.preview)}`;
|
|
355
|
+
case "prompt_topped":
|
|
356
|
+
// Routine operation (the user reordered a queued prompt), not a warning.
|
|
357
|
+
return `inbox message moved to the front: ${textOr(e.preview)}`;
|
|
358
|
+
case "prompt_taken":
|
|
359
|
+
// Routine: a run dequeued the operator's request to run it.
|
|
360
|
+
return `inbox message taken by run #${eventRun(e)}: ${textOr(e.preview)}`;
|
|
361
|
+
case "question_posted":
|
|
362
|
+
// Routine operation (a role asked the user something), not a warning.
|
|
363
|
+
// The position is the `<n>` `questions answer` takes, so the notify page is actionable.
|
|
364
|
+
return `question posted: ${textOr(e.question)}${typeof e.position === "number" && Number.isInteger(e.position) && e.position >= 1 ? ` — question ${e.position} in \`tumwater questions\`; answer it with \`tumwater questions answer ${e.position} "<decision>"\`` : ""}`;
|
|
365
|
+
case "entry_refused":
|
|
366
|
+
// Routine operation (a refusal note merged), not a warning — like question_posted.
|
|
367
|
+
const files = Array.isArray(e.files) ? e.files.join(", ") : "";
|
|
368
|
+
const titles = stringList(e.entries);
|
|
369
|
+
if (titles.length === 0)
|
|
370
|
+
return `entry refused: ${textOr(e.reason)} (${files})`;
|
|
371
|
+
const more = titles.length > 1 ? ` (and ${titles.length - 1} more)` : "";
|
|
372
|
+
return `entry refused: "${truncate(titles[0], 80)}"${more}: ${textOr(e.reason)} (${files})`;
|
|
373
|
+
case "issue_filed":
|
|
374
|
+
// The feedback observer's filing (plans/feedback-role.md 2/2) is a visible, auditable
|
|
375
|
+
// result — routine operation like question_posted, not a warning. The number is the
|
|
376
|
+
// issue the operator can open; a torn event without it reads "?" never "undefined".
|
|
377
|
+
return `filed issue #${typeof e.issue === "number" ? e.issue : "?"} ${textOr(e.title)} on znation/tumwater`;
|
|
378
|
+
default: return textOr(e.type);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function budgetEventMessage(e) {
|
|
382
|
+
switch (e.type) {
|
|
332
383
|
case "budget_warning":
|
|
333
384
|
// The early page beside budget_paused: the cap is not reached yet, so say so — the
|
|
334
385
|
// operator still has room to raise it or fix the fallback before the team stops.
|
|
@@ -379,11 +430,27 @@ export function eventMessage(e) {
|
|
|
379
430
|
});
|
|
380
431
|
return `fallback: ${parts.join("; ")}`;
|
|
381
432
|
}
|
|
433
|
+
case "model_fallback_started":
|
|
434
|
+
// Routine-with-explanation, like rate_limit_hold: the switch IS the harness handling the
|
|
435
|
+
// failure. Names the pair the role now runs on and why the primary was abandoned.
|
|
436
|
+
return `model fallback — ${providerModelPair(e, "pi's default", "pi's default")} engaged (primary failing: ${textOr(e.reason, "backend failure")})`;
|
|
437
|
+
case "model_fallback_ended":
|
|
438
|
+
// The return counterpart: names the pair runs resume on and how long the episode ran.
|
|
439
|
+
return `model fallback ended — back on ${providerModelPair(e, "pi's default", "pi's default")} after ${minutes(e.durationMs)}m`;
|
|
440
|
+
case "opencode_free_model_billed":
|
|
441
|
+
// The promotion ended (plans/opencode-backend.md §7, part 6c/7): a run billed what the
|
|
442
|
+
// catalog called free, so the team now prices that model as paid from here on. No warning
|
|
443
|
+
// prefix — the demotion is the harness handling the spend — but it names the model and the
|
|
444
|
+
// cost so the operator sees why a badge stopped saying "cost n/a".
|
|
445
|
+
return `opencode ${textOr(e.model, "model")} billed ${usd(finiteNumber(e.costUsd, 0))} — demoted from free to priced`;
|
|
446
|
+
default: return textOr(e.type);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function holdEventMessage(e) {
|
|
450
|
+
switch (e.type) {
|
|
382
451
|
case "team_paused":
|
|
383
452
|
// Routine state change, like counters_reset — no warning prefix.
|
|
384
453
|
return `${teamPausePhrase()}${pauseSuffix(e.until, e.reason)} (director keeps running)`;
|
|
385
|
-
case "state_migrated":
|
|
386
|
-
return `state migrated — ${textOr(e.detail)}`;
|
|
387
454
|
case "team_resumed":
|
|
388
455
|
return teamResumePhrase() + (e.expired === true ? PAUSE_EXPIRED_SUFFIX : "");
|
|
389
456
|
case "role_paused":
|
|
@@ -408,25 +475,6 @@ export function eventMessage(e) {
|
|
|
408
475
|
return `role ${textOr(e.role)} paused — ${budgetPhrase(e.spentUsd, e.capUsd)} of its daily cap spent; it starts no new runs until the cap is raised or removed in tumwater.json or the local day rolls over`;
|
|
409
476
|
case "role_cap_resumed":
|
|
410
477
|
return `role ${textOr(e.role)} resumed — it is under its daily cap again and runs again`;
|
|
411
|
-
case "main_check": {
|
|
412
|
-
const failing = Array.isArray(e.failingTests) ? e.failingTests.map(String) : [];
|
|
413
|
-
const covered = Number(e.covered) > 1 ? ` (${textOr(e.covered)} commits)` : "";
|
|
414
|
-
const verdict = e.status === "failed" ? "failed" : "passed";
|
|
415
|
-
const names = failing.length > 0 ? `: ${joinWithMore(failing, 3, "; ")}` : "";
|
|
416
|
-
return `main ${shortSha(String(e.sha ?? ""))}${covered} full check ${verdict}${names}`;
|
|
417
|
-
}
|
|
418
|
-
case "main_culprit": {
|
|
419
|
-
const by = typeof e.runner === "string" && e.runner !== "" ? ` (merged by ${e.runner})` : "";
|
|
420
|
-
return `main turned red at ${shortSha(String(e.commit ?? ""))}${by}`;
|
|
421
|
-
}
|
|
422
|
-
case "operator_revert": {
|
|
423
|
-
const undid = typeof e.runner === "string" && e.runner !== "" ? `, undid ${e.runner}` : "";
|
|
424
|
-
return `reverted ${shortSha(String(e.commit ?? ""))} (by operator) as ${shortSha(String(e.revert ?? ""))}${undid}`; // vocabulary-ok: the deliberate-revert writer is the operator role
|
|
425
|
-
}
|
|
426
|
-
case "main_red_hold":
|
|
427
|
-
// The message already names the SHA, script, and consequence; the problem tone comes
|
|
428
|
-
// from PROBLEM_EVENTS.
|
|
429
|
-
return textOr(e.message);
|
|
430
478
|
case "maintenance_quota_hold":
|
|
431
479
|
// Routine state change, like role_cap_paused — no warning prefix: the hold IS the harness
|
|
432
480
|
// keeping maintenance merges in proportion to work. Names the window's counts so the
|
|
@@ -438,19 +486,16 @@ export function eventMessage(e) {
|
|
|
438
486
|
// Routine state change, like counters_reset — no warning prefix: the team just finished
|
|
439
487
|
// its bootstrap and the maintenance roles are admitted.
|
|
440
488
|
return `bootstrap complete — ${textOr(e.plansDone)}/${textOr(e.untilPlansDone)} plans done; the maintenance roles now run`;
|
|
489
|
+
default: return textOr(e.type);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function resourceEventMessage(e) {
|
|
493
|
+
switch (e.type) {
|
|
441
494
|
case "disk_low":
|
|
442
495
|
// Routine state change, like team_paused — no warning prefix: the hold IS the harness
|
|
443
496
|
// handling the low disk, and naming the free space and the floor tells the operator
|
|
444
497
|
// whether to clear space or lower diskHoldGB in tumwater.json.
|
|
445
498
|
return `waiting: low disk — ${gigabytes(e.freeGB)} free on the workspaces volume (floor ${gigabytes(e.holdGB)}); new work waits until space recovers`;
|
|
446
|
-
case "quiet_hours_started": {
|
|
447
|
-
// Routine hold, like disk_low — no warning prefix: the window is configured, and the line
|
|
448
|
-
// says what stops and what keeps running. pollQuietHoursGate may send no window.
|
|
449
|
-
return `waiting: quiet hours${windowSuffix(e)} — roles start no new runs until the window ends; the director still runs`;
|
|
450
|
-
}
|
|
451
|
-
case "quiet_hours_ended": {
|
|
452
|
-
return `quiet hours ended${windowSuffix(e)} — roles start runs again`;
|
|
453
|
-
}
|
|
454
499
|
case "disk_ok":
|
|
455
500
|
return `disk recovered — ${gigabytes(e.freeGB)} free on the workspaces volume; new work starts again`;
|
|
456
501
|
case "disk_reclaim": {
|
|
@@ -460,13 +505,21 @@ export function eventMessage(e) {
|
|
|
460
505
|
const from = names.length > 0 ? names.join(", ") : "?";
|
|
461
506
|
return `disk reclaim (${textOr(e.mode, "pressure")}) — freed ${gigabytes(e.freedGB)} from ${from}; ${gigabytes(e.freeGB)} free now`;
|
|
462
507
|
}
|
|
508
|
+
case "quiet_hours_started": {
|
|
509
|
+
// Routine hold, like disk_low — no warning prefix: the window is configured, and the line
|
|
510
|
+
// says what stops and what keeps running. pollQuietHoursGate may send no window.
|
|
511
|
+
return `waiting: quiet hours${windowSuffix(e)} — roles start no new runs until the window ends; the director still runs`;
|
|
512
|
+
}
|
|
513
|
+
case "quiet_hours_ended": {
|
|
514
|
+
return `quiet hours ended${windowSuffix(e)} — roles start runs again`;
|
|
515
|
+
}
|
|
463
516
|
case "rate_limit_hold": {
|
|
464
517
|
// Routine state change, like team_paused — no warning prefix: the hold IS the harness
|
|
465
518
|
// handling the storm. Names who saw the failure and when the team re-opens on its own.
|
|
466
519
|
// The rate-limit kind keeps the wording every historical event has; a backend-failure
|
|
467
520
|
// kind names itself instead, since "429" would be a lie about a connection error.
|
|
468
521
|
const roles = rolesPhrase(e.roles, "several roles");
|
|
469
|
-
if (e.kind
|
|
522
|
+
if (rendersAsBackendFailure(e.kind))
|
|
470
523
|
return `waiting: backend failing (${backendKindPhrase(e.kind)})${providerAt(e)} — ${roles} hit backend failures; roles on ${providerScope(e)} start no new runs ${holdPhrase(e.holdMs, e.escalation)} (director keeps running)`;
|
|
471
524
|
return `waiting: provider limit${providerAt(e)} — ${roles} rate-limited by the provider; roles on ${providerScope(e)} start no new runs ${holdPhrase(e.holdMs, e.escalation)} (director keeps running)`;
|
|
472
525
|
}
|
|
@@ -474,7 +527,7 @@ export function eventMessage(e) {
|
|
|
474
527
|
// The ended hold's kind rides the resumed event (pollTeamHold logs it), so the lift
|
|
475
528
|
// names what actually ended — the same split as the hold line above: "429 hold lifted"
|
|
476
529
|
// after a connection-error hold would be a lie about a connection error.
|
|
477
|
-
if (e.kind
|
|
530
|
+
if (rendersAsBackendFailure(e.kind))
|
|
478
531
|
return `backend recovered (${backendKindPhrase(e.kind)})${providerAt(e)} — roles on ${providerScope(e)} run again`;
|
|
479
532
|
return `provider limit lifted${providerAt(e)} — roles on ${providerScope(e)} run again`;
|
|
480
533
|
}
|
|
@@ -506,6 +559,11 @@ export function eventMessage(e) {
|
|
|
506
559
|
return `waiting: ${backendLabel(e)} subscription window ${textOr(e.windowType)} rejected — ${backendLabel(e)} model jobs wait${windowResetPhrase(e)} (director keeps running)`;
|
|
507
560
|
case "subscription_window_reset":
|
|
508
561
|
return `${backendLabel(e)} subscription window reset — ${backendLabel(e)} model jobs run again`;
|
|
562
|
+
default: return textOr(e.type);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function configEventMessage(e) {
|
|
566
|
+
switch (e.type) {
|
|
509
567
|
case "max_concurrent_changed": {
|
|
510
568
|
// Routine state change, like counters_reset — no warning prefix.
|
|
511
569
|
return `maxConcurrent changed: ${textOr(e.from)} → ${textOr(e.to)}`;
|
|
@@ -534,19 +592,17 @@ export function eventMessage(e) {
|
|
|
534
592
|
return e.found === false
|
|
535
593
|
? `warning: newly routed ${textOr(e.agent)} backend — its binary was not found`
|
|
536
594
|
: `newly routed ${textOr(e.agent)} backend checked — binary found`;
|
|
537
|
-
case "
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
return `
|
|
541
|
-
case "
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
// cost so the operator sees why a badge stopped saying "cost n/a".
|
|
549
|
-
return `opencode ${textOr(e.model, "model")} billed ${usd(finiteNumber(e.costUsd, 0))} — demoted from free to priced`;
|
|
595
|
+
case "state_migrated":
|
|
596
|
+
return `state migrated — ${textOr(e.detail)}`;
|
|
597
|
+
case "orchestrator_start":
|
|
598
|
+
return `tumwater started (pid ${textOr(e.pid)}${e.build ? `, version ${shortSha(e.build)}` : ""})`;
|
|
599
|
+
case "orchestrator_stop":
|
|
600
|
+
return orchestratorStopPhrase(e.cause);
|
|
601
|
+
default: return textOr(e.type);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function imageRestartEventMessage(e) {
|
|
605
|
+
switch (e.type) {
|
|
550
606
|
case "image_blind": {
|
|
551
607
|
// Routine-with-explanation like the model-fallback events — no warning prefix: the
|
|
552
608
|
// run was stopped (or a gate run noted) because the model cannot see the image. Names
|
|
@@ -558,13 +614,6 @@ export function eventMessage(e) {
|
|
|
558
614
|
: "";
|
|
559
615
|
return `blind image read: ${textOr(e.path)} on ${textOr(e.model, "the model")}${action}`;
|
|
560
616
|
}
|
|
561
|
-
case "image_reroute":
|
|
562
|
-
// The run's work moves to a model that can read the image (plans/image-input.md part
|
|
563
|
-
// 4/5). Routine-with-explanation — no warning prefix: names the hop the way the
|
|
564
|
-
// handoff reads, and whether the traits-of-attachment preflight avoided a failed run.
|
|
565
|
-
return e.preflight === true
|
|
566
|
-
? `image attachment routed to ${textOr(e.to, "the target")} (${textOr(e.tier, "a peer tier")}) to read its image — no failed run`
|
|
567
|
-
: `image rerouted to ${textOr(e.to, "the target")} (${textOr(e.tier, "a peer tier")}) — next run goes there after a blind read on ${textOr(e.from, "the prior model")}`;
|
|
568
617
|
case "build_stale":
|
|
569
618
|
// Self-hosting teams only (src/redeploy/redeploy.ts): the code main describes is not the
|
|
570
619
|
// code running. Not a warning prefix — a stale build is a state, and auto-restart resolves
|
|
@@ -574,7 +623,7 @@ export function eventMessage(e) {
|
|
|
574
623
|
return `restart pending — main ${shortSha(e.head)} is green; finishing current runs (no new runs start)`;
|
|
575
624
|
case "restart": {
|
|
576
625
|
const aborted = finiteNumber(e.abortedRuns, 0);
|
|
577
|
-
return `restarting onto update ${shortSha(e.to)} (finished current work in ${minutes(e.finishedMs)}m${aborted > 0 ? `, ${aborted
|
|
626
|
+
return `restarting onto update ${shortSha(e.to)} (finished current work in ${minutes(e.finishedMs)}m${aborted > 0 ? `, ${plural(aborted, "run")} resume after the restart` : ""})`;
|
|
578
627
|
}
|
|
579
628
|
case "restart_refused":
|
|
580
629
|
// The restart is refused, not failed: the running build stays and the gate is re-asked
|
|
@@ -593,21 +642,24 @@ export function eventMessage(e) {
|
|
|
593
642
|
const how = e.signal ? `was killed by ${textOr(e.signal)}` : `exited ${textOr(e.code)}`;
|
|
594
643
|
return `tumwater is down — restart ${textOr(e.generation)} ${how}${e.reason ? `: ${textOr(e.reason)}` : ""}; start it again with \`tumwater start\``;
|
|
595
644
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
645
|
+
default: return textOr(e.type);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function checkWaitEventMessage(e) {
|
|
649
|
+
switch (e.type) {
|
|
650
|
+
case "build_check":
|
|
651
|
+
// The deterministic check's cost, per run: scope names which gate paid (the pre-merge review
|
|
652
|
+
// gate, the red-main baseline check of main itself, or the merge lock's post-rebase re-check
|
|
653
|
+
// of the tree about to merge). script carries the npm script name for an npm check but the
|
|
654
|
+
// FULL configured command otherwise (checkScriptName in build/build-check.ts), so no "npm"
|
|
655
|
+
// prefix is asserted here — the bare name reads correctly for both kinds ("npm test" would
|
|
656
|
+
// misrender a cargo or make-based project's check).
|
|
657
|
+
return `check (${textOr(e.scope)}): ${textOr(e.script)} ${eventTypeLabel(outcomeText(e))}${elapsed(e.durationMs)}`;
|
|
658
|
+
case "dep_install": {
|
|
659
|
+
// The root checkout's install catching up with a merged lockfile change — routine on
|
|
660
|
+
// success; a failure also rides its own warning, which says what happens meanwhile.
|
|
661
|
+
const pkgs = stringList(e.packages).join(", ");
|
|
662
|
+
return `root install (${pkgs}) ${eventTypeLabel(outcomeText(e))}${e.error ? ` — ${textOr(e.error)}` : ""}${elapsed(e.durationMs)}`;
|
|
611
663
|
}
|
|
612
664
|
case "slot_wait": {
|
|
613
665
|
// Routine-with-explanation, like rate_limit_hold — no warning prefix: waiting is the pool
|
|
@@ -644,19 +696,112 @@ export function eventMessage(e) {
|
|
|
644
696
|
const behind = heldBy.length > 0 ? `; held by ${heldBy.join(", ")}${limit === null ? "" : ` of maxConcurrentChecks ${limit}`}` : "";
|
|
645
697
|
return `waited ${shortSpanPhrase(finiteNumber(e.waitedMs, 0))} for a check slot (${textOr(e.role)} ${textOr(e.scope)} check${behind})`;
|
|
646
698
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
: `assigned "${textOr(e.title)}"`;
|
|
653
|
-
case "permit_reserve":
|
|
654
|
-
// Priority headroom (part 7/7): a reserve appearing or vanishing is the scheduler working
|
|
655
|
-
// as designed; it names the permits withheld for the work tier so an operator can see why
|
|
656
|
-
// maintenance concurrency dipped.
|
|
657
|
-
return `${finiteNumber(e.reserve, 0)} permit(s) reserved for work roles that have work to take`;
|
|
699
|
+
default: return textOr(e.type);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function warningEventMessage(e) {
|
|
703
|
+
switch (e.type) {
|
|
658
704
|
case "warning":
|
|
659
705
|
return `warning: ${textOr(e.message)}`;
|
|
706
|
+
default: return textOr(e.type);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/** An event's message alone — formatEvent's line without the time and role columns. The GUI's
|
|
710
|
+
* activity feed renders the time and role itself (relative ages, role chips), so the payload
|
|
711
|
+
* ships this beside the event's type and role instead of making the page re-parse a line.
|
|
712
|
+
* Dispatch only: each event family's line is built by a module-private <family>EventMessage
|
|
713
|
+
* switch (runEventMessage, mergeEventMessage, reviewEventMessage, inboxEventMessage,
|
|
714
|
+
* budgetEventMessage, holdEventMessage, resourceEventMessage, configEventMessage,
|
|
715
|
+
* imageRestartEventMessage, checkWaitEventMessage, warningEventMessage). */
|
|
716
|
+
export function eventMessage(e) {
|
|
717
|
+
switch (e.type) {
|
|
718
|
+
case "run_start":
|
|
719
|
+
case "run_end":
|
|
720
|
+
case "run_deferred":
|
|
721
|
+
case "run_aborted":
|
|
722
|
+
case "resume":
|
|
723
|
+
case "wake":
|
|
724
|
+
case "counters_reset":
|
|
725
|
+
case "claim":
|
|
726
|
+
case "permit_reserve": return runEventMessage(e);
|
|
727
|
+
case "merged":
|
|
728
|
+
case "merge_queued":
|
|
729
|
+
case "merge_cancelled":
|
|
730
|
+
case "merge_topped":
|
|
731
|
+
case "merge_done":
|
|
732
|
+
case "merge_failed":
|
|
733
|
+
case "conflict_handback":
|
|
734
|
+
case "operator_revert":
|
|
735
|
+
case "main_check":
|
|
736
|
+
case "main_culprit":
|
|
737
|
+
case "main_red_hold": return mergeEventMessage(e);
|
|
738
|
+
case "review_start":
|
|
739
|
+
case "review_verdict":
|
|
740
|
+
case "review_rejected":
|
|
741
|
+
case "review_failed":
|
|
742
|
+
case "revision": return reviewEventMessage(e);
|
|
743
|
+
case "prompt_enqueued":
|
|
744
|
+
case "prompt_cancelled":
|
|
745
|
+
case "prompt_edited":
|
|
746
|
+
case "prompt_topped":
|
|
747
|
+
case "prompt_taken":
|
|
748
|
+
case "question_posted":
|
|
749
|
+
case "entry_refused":
|
|
750
|
+
case "issue_filed": return inboxEventMessage(e);
|
|
751
|
+
case "budget_warning":
|
|
752
|
+
case "budget_paused":
|
|
753
|
+
case "budget_fallback":
|
|
754
|
+
case "budget_resumed":
|
|
755
|
+
case "budget_handback":
|
|
756
|
+
case "fallback_switched":
|
|
757
|
+
case "model_fallback_started":
|
|
758
|
+
case "model_fallback_ended":
|
|
759
|
+
case "opencode_free_model_billed": return budgetEventMessage(e);
|
|
760
|
+
case "team_paused":
|
|
761
|
+
case "team_resumed":
|
|
762
|
+
case "role_paused":
|
|
763
|
+
case "role_resumed":
|
|
764
|
+
case "role_streak_paused":
|
|
765
|
+
case "role_cap_warning":
|
|
766
|
+
case "role_cap_paused":
|
|
767
|
+
case "role_cap_resumed":
|
|
768
|
+
case "maintenance_quota_hold":
|
|
769
|
+
case "maintenance_quota_resumed":
|
|
770
|
+
case "bootstrap_complete": return holdEventMessage(e);
|
|
771
|
+
case "disk_low":
|
|
772
|
+
case "disk_ok":
|
|
773
|
+
case "disk_reclaim":
|
|
774
|
+
case "quiet_hours_started":
|
|
775
|
+
case "quiet_hours_ended":
|
|
776
|
+
case "rate_limit_hold":
|
|
777
|
+
case "rate_limit_resumed":
|
|
778
|
+
case "subscription_window_warning":
|
|
779
|
+
case "subscription_window_reserved":
|
|
780
|
+
case "subscription_window_limited":
|
|
781
|
+
case "subscription_window_reset": return resourceEventMessage(e);
|
|
782
|
+
case "max_concurrent_changed":
|
|
783
|
+
case "provider_concurrency_changed":
|
|
784
|
+
case "retention_changed":
|
|
785
|
+
case "config_changed":
|
|
786
|
+
case "model_changed":
|
|
787
|
+
case "backend_binary":
|
|
788
|
+
case "state_migrated":
|
|
789
|
+
case "orchestrator_start":
|
|
790
|
+
case "orchestrator_stop": return configEventMessage(e);
|
|
791
|
+
case "image_blind":
|
|
792
|
+
case "build_stale":
|
|
793
|
+
case "restart_pending":
|
|
794
|
+
case "restart":
|
|
795
|
+
case "restart_refused":
|
|
796
|
+
case "restart_blocked":
|
|
797
|
+
case "restart_forced":
|
|
798
|
+
case "supervisor_exit": return imageRestartEventMessage(e);
|
|
799
|
+
case "build_check":
|
|
800
|
+
case "dep_install":
|
|
801
|
+
case "slot_wait":
|
|
802
|
+
case "provider_wait":
|
|
803
|
+
case "check_wait": return checkWaitEventMessage(e);
|
|
804
|
+
case "warning": return warningEventMessage(e);
|
|
660
805
|
default:
|
|
661
806
|
// An unrecognized type is normally a newer build's string and renders as-is; a torn or
|
|
662
807
|
// hand-edited event object with no type at all must read the "?" stand-in, never
|