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
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
/** The seam's answer to "who in this team can read images" (plans/image-input.md
|
|
2
|
-
* "Capability"). One resolver, `seamImageInput`, is the only place that rule lives
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
import { configForRole
|
|
8
|
-
import { BORROW_ORDER, resolveTierFallbacks } from "../config/config-fallbacks.js";
|
|
2
|
+
* "Capability"). One resolver, `seamImageInput`, is the only place that rule lives, so the
|
|
3
|
+
* answer is never reached two ways. It lives at runtime level because it needs the backend
|
|
4
|
+
* registry: a claude seam is always capable through the AgentBackend.imageInput hook, a pi
|
|
5
|
+
* seam reads the model's `input` in models.json, and any other backend answers null unless it
|
|
6
|
+
* declares a hook. */
|
|
7
|
+
import { configForRole } from "../config/config-views.js";
|
|
9
8
|
import { seamAgent } from "../config/model-selector.js";
|
|
10
|
-
import {
|
|
11
|
-
import { piModelImageInput, piPairFree, piModelsPath, readPiProviders } from "../pi/pi-models.js";
|
|
9
|
+
import { piModelImageInput, piModelsPath, readPiProviders } from "../pi/pi-models.js";
|
|
12
10
|
import { piListedImageInput } from "../pi/pi-listed-models.js";
|
|
13
11
|
import { backendById } from "./agent-backend.js";
|
|
14
|
-
import { imageProbeEntryFresh, imageProbeKey } from "../doctor/image-probe.js";
|
|
15
12
|
/** One seam's image-input capability (plans/image-input.md "Capability"): `true` when the
|
|
16
13
|
* seam's backend declares image support, `false` when it is text-only, `null` when it cannot
|
|
17
14
|
* be answered from what the team knows (a pair neither models.json nor the pi listing names, a
|
|
@@ -21,11 +18,10 @@ import { imageProbeEntryFresh, imageProbeKey } from "../doctor/image-probe.js";
|
|
|
21
18
|
* the default pi backend leaves it to models.json's `input` field, where absent means text-only
|
|
22
19
|
* (pi's own default for a custom model); a pair absent from models.json answers what
|
|
23
20
|
* `pi --list-models` last said (src/pi/pi-listed-models.ts), else null.
|
|
24
|
-
*
|
|
25
|
-
* reads
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
export function seamImageInput(seam, providers, probes, listed = null) {
|
|
21
|
+
* A claim in models.json is trusted without a live probe (the doctor's live probe was
|
|
22
|
+
* subtracted): a claimed pi seam reads true on the catalog. claude seams are answered by the
|
|
23
|
+
* hook. */
|
|
24
|
+
export function seamImageInput(seam, providers, listed = null) {
|
|
29
25
|
const backendId = seamAgent(seam);
|
|
30
26
|
const backend = backendById(backendId);
|
|
31
27
|
// A backend with an imageInput hook owns the answer outright (claude: true for every model).
|
|
@@ -34,79 +30,14 @@ export function seamImageInput(seam, providers, probes, listed = null) {
|
|
|
34
30
|
// An unknown backend without a hook has no catalog to consult — unknown, never assume.
|
|
35
31
|
if (backendId !== "pi")
|
|
36
32
|
return null;
|
|
37
|
-
|
|
38
|
-
if (base !== true)
|
|
39
|
-
return base;
|
|
40
|
-
// A claimed pi seam whose live probe failed (fresh, claim unchanged) reads false. Only pi is
|
|
41
|
-
// probed, so the probe key carries the pi backend.
|
|
42
|
-
const entry = probes?.[imageProbeKey(backendId, seam.provider, seam.model)];
|
|
43
|
-
if (entry?.verdict === "fail" && imageProbeEntryFresh(entry))
|
|
44
|
-
return false;
|
|
45
|
-
return true;
|
|
33
|
+
return piModelImageInput(providers, seam.provider, seam.model) ?? piListedImageInput(listed, seam.provider, seam.model);
|
|
46
34
|
}
|
|
47
|
-
/**
|
|
48
|
-
* (
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
* and takes the first tier whose model reads images. `regime` picks the pair set (see
|
|
52
|
-
* RerouteRegime); `modelsPath` and `account` are injectable so tests need no real pi install
|
|
53
|
-
* or account. */
|
|
54
|
-
export function imageReroute(config, role, regime = "primary", modelsPath = piModelsPath(), account = null, probes, providers = null, listed = null) {
|
|
55
|
-
const tier = roleSeamTier(config, role);
|
|
56
|
-
const candidates = BORROW_ORDER[tier].filter((t) => t !== tier);
|
|
57
|
-
if (candidates.length === 0)
|
|
58
|
-
return null;
|
|
59
|
-
const providerMap = providers ?? readPiProviders(modelsPath);
|
|
60
|
-
const capable = (rc) => seamImageInput(rc, providerMap, probes, listed) === true;
|
|
61
|
-
if (regime === "primary") {
|
|
62
|
-
for (const t of candidates) {
|
|
63
|
-
const rc = withModelOverrides(config, t, {});
|
|
64
|
-
if (capable(rc))
|
|
65
|
-
return { tier: t, config: rc };
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
// Fallback regime: only the tiers' free fallback pairs qualify (resolveTierFallbacks over the
|
|
70
|
-
// price check), never a priced primary model — a reroute must not spend past the cap.
|
|
71
|
-
const resolved = resolveTierFallbacks(config, (p) =>
|
|
72
|
-
// The free-ness lookup reads the one claude account held here (part 2a/9).
|
|
73
|
-
piPairFree(providerMap, p, (b) => (b === "claude" ? account : null)));
|
|
74
|
-
for (const t of candidates) {
|
|
75
|
-
const pair = resolved[t].pair;
|
|
76
|
-
if (!pair)
|
|
77
|
-
continue;
|
|
78
|
-
const rc = withModelOverrides(config, t, {
|
|
79
|
-
provider: pair.provider,
|
|
80
|
-
model: pair.model,
|
|
81
|
-
thinking: pair.thinking,
|
|
82
|
-
});
|
|
83
|
-
if (capable(rc))
|
|
84
|
-
return { tier: t, config: rc };
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
/** The config of imageReroute, for callers that want only the target model. */
|
|
89
|
-
export function imageRerouteTarget(config, role, regime = "primary", modelsPath = piModelsPath(), account = null, probes, providers = null, listed = null) {
|
|
90
|
-
return imageReroute(config, role, regime, modelsPath, account, probes, providers, listed)?.config ?? null;
|
|
91
|
-
}
|
|
92
|
-
/** A seam's model as one readable token, the same label the doctor's `image input` check uses
|
|
93
|
-
* (`claude/<id>` for a claude seam, `provider/model` for pi). The one home of the label, so
|
|
94
|
-
* the surface strings (the images alert, the role page, the status rows) and the doctor report
|
|
95
|
-
* cannot drift, and delegates to backend-traits' modelLabel. */
|
|
96
|
-
export function seamImageLabel(backend, provider, model) {
|
|
97
|
-
return modelLabel(backend, provider, model);
|
|
98
|
-
}
|
|
99
|
-
/** Compute RoleImageInfo for one role. `modelsPath` and `probes` are injectable; the providers
|
|
100
|
-
* catalog is read (stat-cached) here and also handed to imageReroute so a poll that asks many
|
|
101
|
-
* roles still parses the catalog once. All reads mirror the doctor's `image input` check, so
|
|
102
|
-
* the surface and the check agree about capability. */
|
|
103
|
-
export function roleImageInfo(config, role, modelsPath = piModelsPath(), probes, listed = null) {
|
|
35
|
+
/** Compute RoleImageInfo for one role. `modelsPath` and `listed` are injectable; the providers
|
|
36
|
+
* catalog is read (stat-cached) here. The reroute field is a literal null — the peer-tier image
|
|
37
|
+
* reroute was subtracted (part 5g/6), so no role names a reroute target; 5h removes the field. */
|
|
38
|
+
export function roleImageInfo(config, role, modelsPath = piModelsPath(), listed = null) {
|
|
104
39
|
const providers = readPiProviders(modelsPath);
|
|
105
40
|
const rc = configForRole(config, role);
|
|
106
|
-
const imageInput = seamImageInput(rc, providers,
|
|
107
|
-
|
|
108
|
-
const imageRerouteStr = rr
|
|
109
|
-
? `${rr.tier} (${seamImageLabel(rr.config.agent, rr.config.provider, rr.config.model)})`
|
|
110
|
-
: null;
|
|
111
|
-
return { imageInput, imageReroute: imageRerouteStr };
|
|
41
|
+
const imageInput = seamImageInput(rc, providers, listed);
|
|
42
|
+
return { imageInput, imageReroute: null };
|
|
112
43
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Installing the per-team `tumwater-notes` shim every backend's run finds on its PATH
|
|
2
|
-
* (plans/notes-command.md "Reaching it from a run"): run-
|
|
2
|
+
* (plans/notes-command.md "Reaching it from a run"): run-spawn calls `ensureNotesShim(root)`
|
|
3
3
|
* before building each invocation, and the backend prepends the returned dir to the run's
|
|
4
4
|
* PATH (backendBaseEnv's `binDir`, src/process/run-marker.ts). The shim is a two-line `sh`
|
|
5
5
|
* script that execs the compiled notes CLI, so a model's shell — pi's, Claude Code's or
|
|
@@ -20,7 +20,7 @@ export const NOTES_SHIM_NAME = "tumwater-notes";
|
|
|
20
20
|
/** Install the `tumwater-notes` shim under `<root>/.tumwater/bin` and return that directory.
|
|
21
21
|
* The shim is written atomically with mode 0755, only when its content differs from what is
|
|
22
22
|
* already there (a redeploy that changes `<dist>` rewrites it). Never throws: a failure to
|
|
23
|
-
* write is the caller's to absorb — run-
|
|
23
|
+
* write is the caller's to absorb — run-spawn logs one warning and runs without the shim. */
|
|
24
24
|
export function ensureNotesShim(root, execPath = process.execPath) {
|
|
25
25
|
const dir = notesShimDir(root);
|
|
26
26
|
const target = path.join(dir, NOTES_SHIM_NAME);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** The opencode adapter for the agent seam (plans/opencode-backend.md "The invocation",
|
|
2
2
|
* "Sessions", §6). It shapes one run's argv/stdin/env, translates opencode's `run --format
|
|
3
3
|
* json` into the canonical stream, runs the authoritative session export after the process
|
|
4
|
-
* exits, and classifies the exit's stderr — everything backend-specific. src/agent/run-
|
|
5
|
-
*
|
|
4
|
+
* exits, and classifies the exit's stderr — everything backend-specific. src/agent/run-spawn.ts
|
|
5
|
+
* spawns the run and records this run in the session ledger; opencode mints its own session id
|
|
6
6
|
* (a `ses_…` id, or the resumed record's id), which `invocation` returns so the ledger can
|
|
7
7
|
* carry it.
|
|
8
8
|
*
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* `.tumwater/state/opencode-models.json` that survives a failed fetch, and
|
|
5
5
|
* `opencodeModelFree` as the one predicate part 6b/7's seam reads. No seam is wired here: that
|
|
6
6
|
* is part 6b/7 (the budget gate), part 6c/7 (a billedAt demotion on an observed cost) and
|
|
7
|
-
* part 7/7 (a startup-cache seed and doctor), so this module
|
|
8
|
-
*
|
|
7
|
+
* part 7/7 (a startup-cache seed and doctor), so this module only publishes the data and
|
|
8
|
+
* predicates those seam consumers read. */
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
10
|
import { isJsonObject, validEntries } from "../../files/json-object.js";
|
|
11
11
|
import { readJsonFile, writeJsonAtomic } from "../../files/json-files.js";
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
21
|
-
import { readTextOrNull } from "../../files/files.js";
|
|
21
|
+
import { readTextOrNull, xdgConfigHomeDir } from "../../files/files.js";
|
|
22
22
|
import { isJsonObject } from "../../files/json-object.js";
|
|
23
23
|
/** The operator's global opencode config lives in `$XDG_CONFIG_HOME/opencode/` (or
|
|
24
24
|
* `~/.config/opencode/`), in `config.json`, `opencode.json` or `opencode.jsonc`. */
|
|
@@ -123,7 +123,7 @@ function stripJsonc(src) {
|
|
|
123
123
|
* parse; doctor reports a broken one in part 7/7. `homeDir` is the operator's home directory;
|
|
124
124
|
* the process's own `XDG_CONFIG_HOME` env, when set, wins over `~/.config` (the XDG default). */
|
|
125
125
|
export function operatorMcpNames(env, homeDir) {
|
|
126
|
-
const configHome = env
|
|
126
|
+
const configHome = xdgConfigHomeDir(env, homeDir);
|
|
127
127
|
const dir = path.join(configHome, "opencode");
|
|
128
128
|
for (const file of OPERATOR_CONFIG_FILES) {
|
|
129
129
|
const text = readTextOrNull(path.join(dir, file));
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* `opencode_free_tier_refused` is added there once when any step was refused. `--thinking` is
|
|
17
17
|
* never passed, so no `reasoning` event appears; the mapping below still recognizes one so a
|
|
18
18
|
* future `--thinking` run degrades gracefully. */
|
|
19
|
+
import { transientProseKind } from "../backend-traits.js";
|
|
19
20
|
import { canonicalUsageBlock, isJsonObject, nonNegativeNumber, objectOrEmpty, parseJsonObject } from "../../files/json-object.js";
|
|
20
21
|
/** opencode's tool names → the canonical pi names the readers already know. `shell`→`bash` and
|
|
21
22
|
* `patch`→`edit` (pi has one edit tool); anything else is lower-cased so a future tool still
|
|
@@ -66,10 +67,9 @@ function classifyError(err) {
|
|
|
66
67
|
// Transient server, connection and timeout failures.
|
|
67
68
|
if (/\b(500|501|502|503|504|529)\b|internal server error|bad gateway|service unavailable|gateway timeout|overloaded/i.test(message))
|
|
68
69
|
return { kind: "transientBackend", backendKind: "server" };
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return { kind: "transientBackend", backendKind: "timeout" };
|
|
70
|
+
const prose = transientProseKind(message);
|
|
71
|
+
if (prose)
|
|
72
|
+
return prose;
|
|
73
73
|
return {};
|
|
74
74
|
}
|
|
75
75
|
/** The step's token counts and cost → pi's canonical usage block: the four counts, their sum
|
|
@@ -117,14 +117,7 @@ export function opencodeTranslator(opts = {}) {
|
|
|
117
117
|
}
|
|
118
118
|
return out;
|
|
119
119
|
};
|
|
120
|
-
const
|
|
121
|
-
const parsed = parseJsonObject(line);
|
|
122
|
-
if (!parsed) {
|
|
123
|
-
// A malformed / non-JSON line on stdout is skipped with one warning for the run.
|
|
124
|
-
record("malformed");
|
|
125
|
-
return [];
|
|
126
|
-
}
|
|
127
|
-
const event = parsed;
|
|
120
|
+
const announceSession = (event) => {
|
|
128
121
|
// The first event with a non-empty `sessionID` announces the session and the agent start.
|
|
129
122
|
// An error raised before a session exists has `sessionID: ""` (or none) and emits neither.
|
|
130
123
|
let out = [];
|
|
@@ -140,92 +133,108 @@ export function opencodeTranslator(opts = {}) {
|
|
|
140
133
|
out.push(JSON.stringify({ type: "agent_start" }));
|
|
141
134
|
}
|
|
142
135
|
}
|
|
136
|
+
return out;
|
|
137
|
+
};
|
|
138
|
+
const handleStepStart = () => {
|
|
139
|
+
step = { content: [], calls: [] };
|
|
140
|
+
return [];
|
|
141
|
+
};
|
|
142
|
+
const handleText = (event) => {
|
|
143
|
+
// A content block with no open buffer opens one: `step_start` is sometimes missing, and
|
|
144
|
+
// edit-run.jsonl's final `text` lands after its last `step_finish`, so a block dropped
|
|
145
|
+
// for want of a buffer would lose the run's last turn.
|
|
146
|
+
step ??= { content: [], calls: [] };
|
|
147
|
+
step.content.push({ type: "text", text: objectOrEmpty(event.part).text ?? "" });
|
|
148
|
+
return [];
|
|
149
|
+
};
|
|
150
|
+
const handleReasoning = (event) => {
|
|
151
|
+
step ??= { content: [], calls: [] };
|
|
152
|
+
const thinking = objectOrEmpty(event.part).text ?? "";
|
|
153
|
+
step.content.push({ type: "thinking", thinking: typeof thinking === "string" ? thinking : "" });
|
|
154
|
+
return [];
|
|
155
|
+
};
|
|
156
|
+
const handleToolUse = (event) => {
|
|
157
|
+
// As above, a tool reported with no open buffer opens one.
|
|
158
|
+
step ??= { content: [], calls: [] };
|
|
159
|
+
const partValue = objectOrEmpty(event.part);
|
|
160
|
+
const state = objectOrEmpty(partValue.state);
|
|
161
|
+
const input = objectOrEmpty(state.input);
|
|
162
|
+
// `arguments` is the tool's `input` plus a `path` alias of `filePath` —
|
|
163
|
+
// describeToolCall reads either.
|
|
164
|
+
const args = { ...input };
|
|
165
|
+
if (typeof input.filePath === "string")
|
|
166
|
+
args.path = input.filePath;
|
|
167
|
+
// V2 2.0.32 emits the native tool name in `part.tool` (not `part.name`); the live
|
|
168
|
+
// captures agree.
|
|
169
|
+
const name = typeof partValue.tool === "string" ? partValue.tool : "";
|
|
170
|
+
// Own-property test, not a bare index: a tool name like `constructor` or
|
|
171
|
+
// `__proto__` would otherwise inherit a function from the table's prototype chain and
|
|
172
|
+
// bypass the lower-cased fallback (the same guard claude-stream.ts applies).
|
|
173
|
+
const canonicalName = Object.hasOwn(TOOL_NAMES, name) ? (TOOL_NAMES[name] ?? name.toLowerCase()) : name.toLowerCase();
|
|
174
|
+
const id = typeof partValue.id === "string" ? partValue.id : "";
|
|
175
|
+
step.content.push({ type: "toolCall", id, name: canonicalName, arguments: args });
|
|
176
|
+
step.calls.push({ id, name: canonicalName, args, isError: state.status === "error" });
|
|
177
|
+
return [];
|
|
178
|
+
};
|
|
179
|
+
const handleStepFinish = (event) => {
|
|
180
|
+
if (!step)
|
|
181
|
+
return [];
|
|
182
|
+
const part = objectOrEmpty(event.part);
|
|
183
|
+
const tokens = objectOrEmpty(part.tokens);
|
|
184
|
+
const cache = objectOrEmpty(tokens.cache);
|
|
185
|
+
step.usage = canonicalUsage({
|
|
186
|
+
input: tokens.input,
|
|
187
|
+
output: tokens.output,
|
|
188
|
+
cache: { read: cache.read, write: cache.write },
|
|
189
|
+
}, nonNegativeNumber(part.cost, 0));
|
|
190
|
+
return flush(true);
|
|
191
|
+
};
|
|
192
|
+
const handleError = (event) => {
|
|
193
|
+
// Errors carry their payload in `event.error` (`{type, message, status?, response?}`,
|
|
194
|
+
// plan finding 8); classification reads type first, message second.
|
|
195
|
+
const err = objectOrEmpty(event.error);
|
|
196
|
+
const cls = classifyError(err);
|
|
197
|
+
if (cls.freeTier)
|
|
198
|
+
sawFreeTier = true;
|
|
199
|
+
const message = typeof err.message === "string" ? err.message : "";
|
|
200
|
+
// The open step (if any) is cut off by the error and flushed without usage first.
|
|
201
|
+
const out = flush(false);
|
|
202
|
+
const m = { role: "assistant", content: [], stopReason: "error" };
|
|
203
|
+
if (message !== "")
|
|
204
|
+
m.errorMessage = message;
|
|
205
|
+
// tumwaterKind/backendKind ride the canonical EVENT (not its message): the parser
|
|
206
|
+
// reads event.tumwaterKind and trusts it before its text regexes.
|
|
207
|
+
out.push(JSON.stringify({
|
|
208
|
+
type: "message_end",
|
|
209
|
+
message: m,
|
|
210
|
+
...(cls.kind !== undefined ? { tumwaterKind: cls.kind } : {}),
|
|
211
|
+
...(cls.backendKind !== undefined ? { backendKind: cls.backendKind } : {}),
|
|
212
|
+
...(cls.retryAfterSeconds !== undefined ? { retryAfterSeconds: cls.retryAfterSeconds } : {}),
|
|
213
|
+
}));
|
|
214
|
+
return out;
|
|
215
|
+
};
|
|
216
|
+
const translate = (line) => {
|
|
217
|
+
const parsed = parseJsonObject(line);
|
|
218
|
+
if (!parsed) {
|
|
219
|
+
// A malformed / non-JSON line on stdout is skipped with one warning for the run.
|
|
220
|
+
record("malformed");
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
const event = parsed;
|
|
224
|
+
const out = announceSession(event);
|
|
143
225
|
const type = event.type;
|
|
144
226
|
switch (type) {
|
|
145
|
-
case "step_start":
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
case "
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// for want of a buffer would lose the run's last turn.
|
|
152
|
-
step ??= { content: [], calls: [] };
|
|
153
|
-
step.content.push({ type: "text", text: objectOrEmpty(event.part).text ?? "" });
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
case "reasoning": {
|
|
157
|
-
step ??= { content: [], calls: [] };
|
|
158
|
-
const thinking = objectOrEmpty(event.part).text ?? "";
|
|
159
|
-
step.content.push({ type: "thinking", thinking: typeof thinking === "string" ? thinking : "" });
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
case "tool_use": {
|
|
163
|
-
// As above, a tool reported with no open buffer opens one.
|
|
164
|
-
step ??= { content: [], calls: [] };
|
|
165
|
-
const partValue = objectOrEmpty(event.part);
|
|
166
|
-
const state = objectOrEmpty(partValue.state);
|
|
167
|
-
const input = objectOrEmpty(state.input);
|
|
168
|
-
// `arguments` is the tool's `input` plus a `path` alias of `filePath` —
|
|
169
|
-
// describeToolCall reads either.
|
|
170
|
-
const args = { ...input };
|
|
171
|
-
if (typeof input.filePath === "string")
|
|
172
|
-
args.path = input.filePath;
|
|
173
|
-
// V2 2.0.32 emits the native tool name in `part.tool` (not `part.name`); the live
|
|
174
|
-
// captures agree.
|
|
175
|
-
const name = typeof partValue.tool === "string" ? partValue.tool : "";
|
|
176
|
-
// Own-property test, not a bare index: a tool name like `constructor` or
|
|
177
|
-
// `__proto__` would otherwise inherit a function from the table's prototype chain and
|
|
178
|
-
// bypass the lower-cased fallback (the same guard claude-stream.ts applies).
|
|
179
|
-
const canonicalName = Object.hasOwn(TOOL_NAMES, name) ? (TOOL_NAMES[name] ?? name.toLowerCase()) : name.toLowerCase();
|
|
180
|
-
const id = typeof partValue.id === "string" ? partValue.id : "";
|
|
181
|
-
step.content.push({ type: "toolCall", id, name: canonicalName, arguments: args });
|
|
182
|
-
step.calls.push({ id, name: canonicalName, args, isError: state.status === "error" });
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
case "step_finish": {
|
|
186
|
-
if (!step)
|
|
187
|
-
break;
|
|
188
|
-
const part = objectOrEmpty(event.part);
|
|
189
|
-
const tokens = objectOrEmpty(part.tokens);
|
|
190
|
-
const cache = objectOrEmpty(tokens.cache);
|
|
191
|
-
step.usage = canonicalUsage({
|
|
192
|
-
input: tokens.input,
|
|
193
|
-
output: tokens.output,
|
|
194
|
-
cache: { read: cache.read, write: cache.write },
|
|
195
|
-
}, nonNegativeNumber(part.cost, 0));
|
|
196
|
-
out.push(...flush(true));
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
case "error": {
|
|
200
|
-
// Errors carry their payload in `event.error` (`{type, message, status?, response?}`,
|
|
201
|
-
// plan finding 8); classification reads type first, message second.
|
|
202
|
-
const err = objectOrEmpty(event.error);
|
|
203
|
-
const cls = classifyError(err);
|
|
204
|
-
if (cls.freeTier)
|
|
205
|
-
sawFreeTier = true;
|
|
206
|
-
const message = typeof err.message === "string" ? err.message : "";
|
|
207
|
-
// The open step (if any) is cut off by the error and flushed without usage first.
|
|
208
|
-
out.push(...flush(false));
|
|
209
|
-
const m = { role: "assistant", content: [], stopReason: "error" };
|
|
210
|
-
if (message !== "")
|
|
211
|
-
m.errorMessage = message;
|
|
212
|
-
// tumwaterKind/backendKind ride the canonical EVENT (not its message): the parser
|
|
213
|
-
// reads event.tumwaterKind and trusts it before its text regexes.
|
|
214
|
-
out.push(JSON.stringify({
|
|
215
|
-
type: "message_end",
|
|
216
|
-
message: m,
|
|
217
|
-
...(cls.kind !== undefined ? { tumwaterKind: cls.kind } : {}),
|
|
218
|
-
...(cls.backendKind !== undefined ? { backendKind: cls.backendKind } : {}),
|
|
219
|
-
...(cls.retryAfterSeconds !== undefined ? { retryAfterSeconds: cls.retryAfterSeconds } : {}),
|
|
220
|
-
}));
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
227
|
+
case "step_start": return [...out, ...handleStepStart()];
|
|
228
|
+
case "text": return [...out, ...handleText(event)];
|
|
229
|
+
case "reasoning": return [...out, ...handleReasoning(event)];
|
|
230
|
+
case "tool_use": return [...out, ...handleToolUse(event)];
|
|
231
|
+
case "step_finish": return [...out, ...handleStepFinish(event)];
|
|
232
|
+
case "error": return [...out, ...handleError(event)];
|
|
223
233
|
default:
|
|
224
234
|
// Record the unknown type ("" for a type-less line), once, in first-seen order.
|
|
225
235
|
record(typeof type === "string" ? type : "unknown");
|
|
226
|
-
|
|
236
|
+
return out;
|
|
227
237
|
}
|
|
228
|
-
return out;
|
|
229
238
|
};
|
|
230
239
|
return {
|
|
231
240
|
translate,
|