tumwater 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/build-info.json +2 -2
- package/dist/src/agent/agent-backend.js +6 -6
- package/dist/src/agent/{run-agent.js → agent-run.js} +25 -12
- package/dist/src/agent/{run-settle.js → agent-settle.js} +48 -24
- package/dist/src/agent/{run-spawn.js → agent-spawn.js} +21 -12
- package/dist/src/agent/{run-stream.js → agent-stream.js} +4 -4
- package/dist/src/agent/backend-traits.js +2 -2
- package/dist/src/agent/claude/claude-account.js +1 -8
- package/dist/src/agent/claude/claude-backend.js +3 -3
- package/dist/src/agent/claude/claude-env.js +27 -31
- package/dist/src/agent/claude/claude-stream.js +111 -54
- package/dist/src/agent/codex/codex-account.js +9 -0
- package/dist/src/agent/codex/codex-backend.js +2 -2
- package/dist/src/agent/codex/codex-env.js +28 -15
- package/dist/src/agent/codex/codex-hook-trust.js +39 -20
- package/dist/src/agent/codex/codex-rollout.js +93 -56
- package/dist/src/agent/codex/codex-stream.js +1 -1
- package/dist/src/agent/env-scrub.js +18 -0
- package/dist/src/agent/image-input.js +68 -11
- package/dist/src/agent/notes-shim.js +2 -2
- package/dist/src/agent/opencode/opencode-backend.js +2 -2
- package/dist/src/agent/opencode/opencode-catalog.js +50 -27
- package/dist/src/agent/opencode/opencode-config.js +32 -14
- package/dist/src/agent/opencode/opencode-env.js +12 -13
- package/dist/src/agent/opencode/opencode-stream.js +47 -25
- package/dist/src/agent/session-ledger.js +1 -1
- package/dist/src/agent/subscription-account.js +3 -3
- package/dist/src/backlog/backlog-cli-view.js +81 -41
- package/dist/src/backlog/backlog-cli.js +57 -26
- package/dist/src/backlog/backlog-eligibility.js +58 -102
- package/dist/src/backlog/backlog-md.js +5 -5
- package/dist/src/backlog/backlog-parts.js +65 -27
- package/dist/src/backlog/backlog-release.js +12 -12
- package/dist/src/backlog/backlog-structure.js +114 -61
- package/dist/src/backlog/backlog.js +1 -1
- package/dist/src/baseline/main-baseline.js +148 -84
- package/dist/src/budget/budget-fallback-view.js +100 -243
- package/dist/src/budget/budget-wiring.js +29 -65
- package/dist/src/budget/fallback-breaker.js +2 -2
- package/dist/src/build/build-check-detect.js +43 -14
- package/dist/src/build/build-check-events.js +2 -2
- package/dist/src/build/build-check-scoped.js +40 -16
- package/dist/src/build/build-check.js +13 -5
- package/dist/src/cli/cli-command-args.js +89 -52
- package/dist/src/cli/cli-flag-specs.js +42 -22
- package/dist/src/cli/cli-init.js +34 -12
- package/dist/src/cli/cli-query-commands.js +1 -1
- package/dist/src/cli/cli-repo-gate.js +2 -2
- package/dist/src/cli/cli-start.js +91 -58
- package/dist/src/cli/completion.js +82 -46
- package/dist/src/cli/config-commands.js +82 -71
- package/dist/src/cli/help.js +4 -6
- package/dist/src/cli/log-commands.js +43 -23
- package/dist/src/cli/notify-command.js +2 -2
- package/dist/src/cli/question-commands.js +86 -60
- package/dist/src/cli.js +181 -135
- package/dist/src/collections.js +31 -16
- package/dist/src/concurrency/check-slots.js +70 -0
- package/dist/src/concurrency/lock.js +22 -0
- package/dist/src/concurrency/run-permit.js +49 -0
- package/dist/src/concurrency/semaphore.js +46 -250
- package/dist/src/config/config-editable-keys.js +4 -7
- package/dist/src/config/config-fallbacks.js +81 -101
- package/dist/src/config/config-field-checks.js +66 -37
- package/dist/src/config/config-keys.js +5 -20
- package/dist/src/config/config-live.js +4 -28
- package/dist/src/config/{model-selector.js → config-model-selector.js} +4 -4
- package/dist/src/config/config-request.js +153 -102
- package/dist/src/config/config-schema.js +9 -11
- package/dist/src/config/config-selector-checks.js +1 -1
- package/dist/src/config/config-validation-agents.js +2 -2
- package/dist/src/config/config-validation-roles.js +4 -52
- package/dist/src/config/config-validation.js +4 -38
- package/dist/src/config/config-views.js +9 -34
- package/dist/src/config/config-write.js +6 -23
- package/dist/src/config/config.js +4 -5
- package/dist/src/doctor/doctor-backlog-bugs.js +7 -6
- package/dist/src/doctor/doctor-backlog-plans.js +24 -24
- package/dist/src/doctor/doctor-backlog.js +7 -5
- package/dist/src/doctor/doctor-check-names.js +3 -6
- package/dist/src/doctor/doctor-checks.js +5 -5
- package/dist/src/doctor/doctor-claude-checks.js +44 -28
- package/dist/src/doctor/doctor-codex-checks.js +6 -6
- package/dist/src/doctor/doctor-contention-checks.js +44 -112
- package/dist/src/doctor/doctor-fallback-checks.js +2 -2
- package/dist/src/doctor/doctor-holds.js +6 -45
- package/dist/src/doctor/doctor-install.js +1 -1
- package/dist/src/doctor/doctor-launch-services.js +1 -1
- package/dist/src/doctor/doctor-model-checks.js +178 -94
- package/dist/src/doctor/doctor-notify.js +2 -2
- package/dist/src/doctor/doctor-opencode-checks.js +5 -4
- package/dist/src/doctor/doctor-orphans.js +120 -20
- package/dist/src/doctor/{outcome.js → doctor-outcome.js} +7 -0
- package/dist/src/doctor/doctor-pi-checks.js +2 -2
- package/dist/src/doctor/doctor-principles.js +3 -3
- package/dist/src/doctor/doctor-project-checks.js +3 -3
- package/dist/src/doctor/doctor-role-notes.js +1 -1
- package/dist/src/doctor/doctor.js +8 -11
- package/dist/src/events/event-format.js +30 -54
- package/dist/src/events/event-labels.js +1 -5
- package/dist/src/events/event-legacy.js +1 -3
- package/dist/src/events/{notify.js → event-notify.js} +2 -2
- package/dist/src/events/event-read.js +1 -1
- package/dist/src/failure/failure-cluster.js +6 -6
- package/dist/src/failure/failure-data.js +8 -4
- package/dist/src/failure/failure-render.js +44 -34
- package/dist/src/failure/failure-state-change.js +4 -34
- package/dist/src/failure/{time-spend.js → failure-time-spend.js} +117 -84
- package/dist/src/files/files.js +30 -0
- package/dist/src/files/json-object.js +8 -8
- package/dist/src/files/stat-cache.js +12 -8
- package/dist/src/gates/budget-gates.js +85 -84
- package/dist/src/gates/gate-polls.js +27 -68
- package/dist/src/gates/pause-gates.js +16 -2
- package/dist/src/gates/readiness.js +1 -1
- package/dist/src/gates/role-cap-gates.js +44 -35
- package/dist/src/gates/{startup-gate.js → startup-gates.js} +7 -7
- package/dist/src/gates/{streak-gate.js → streak-gates.js} +75 -52
- package/dist/src/git/git.js +75 -45
- package/dist/src/git/worktree-pool.js +3 -6
- package/dist/src/gui/gui-admin-endpoints.js +3 -3
- package/dist/src/gui/gui-backlog-commands.js +4 -4
- package/dist/src/gui/gui-backlog-endpoints.js +61 -41
- package/dist/src/gui/gui-command.js +43 -24
- package/dist/src/gui/gui-history-endpoints.js +100 -52
- package/dist/src/gui/gui-operator-commands.js +22 -23
- package/dist/src/gui/gui-prompt-commands.js +23 -23
- package/dist/src/gui/gui-report-endpoints.js +1 -1
- package/dist/src/history/history-data.js +71 -44
- package/dist/src/history/history-queued.js +49 -27
- package/dist/src/history/history.js +51 -33
- package/dist/src/inbox/prompt-commands.js +59 -42
- package/dist/src/init/init.js +88 -40
- package/dist/src/merge/merge-backlog-conflicts.js +1 -1
- package/dist/src/merge/merge-core.js +1 -1
- package/dist/src/merge/merge-drain.js +8 -11
- package/dist/src/merge/merge-pipeline.js +6 -7
- package/dist/src/merge/merge-post-review-drain.js +10 -13
- package/dist/src/merge/merge-requeue.js +1 -1
- package/dist/src/merge/merge-slot.js +0 -8
- package/dist/src/merge/merge-verify.js +8 -18
- package/dist/src/notes-cli.js +52 -42
- package/dist/src/operator/operator-commands.js +76 -44
- package/dist/src/operator/operator-intent.js +3 -3
- package/dist/src/orchestrator/orchestrator-launch.js +31 -63
- package/dist/src/orchestrator/orchestrator-poll.js +27 -32
- package/dist/src/orchestrator/orchestrator.js +8 -21
- package/dist/src/orchestrator/scheduling-admit.js +101 -68
- package/dist/src/orchestrator/scheduling-scope.js +1 -1
- package/dist/src/orchestrator/scheduling-skip.js +1 -1
- package/dist/src/orchestrator/startup-migrations.js +1 -1
- package/dist/src/paths.js +8 -7
- package/dist/src/pi/pi-args.js +5 -2
- package/dist/src/pi/pi-backend.js +2 -2
- package/dist/src/pi/pi-bin.js +3 -3
- package/dist/src/pi/pi-event-line.js +1 -1
- package/dist/src/pi/pi-models.js +43 -29
- package/dist/src/pi/pi-settings.js +43 -20
- package/dist/src/pi/pi-stream.js +32 -6
- package/dist/src/pi/pi-watchdogs.js +12 -5
- package/dist/src/pi/pi.js +2 -2
- package/dist/src/pi-extension/context-shake.js +32 -20
- package/dist/src/pi-extension/kill-guard.js +69 -0
- package/dist/src/process/process-group.js +49 -5
- package/dist/src/process/process.js +8 -8
- package/dist/src/process/run-marker.js +31 -13
- package/dist/src/process/supervisor.js +40 -28
- package/dist/src/prompt/dialect.js +1 -1
- package/dist/src/prompt/gate-notes.js +2 -2
- package/dist/src/prompt/notes-backlog.js +66 -37
- package/dist/src/prompt/principles.js +6 -4
- package/dist/src/prompt/prompt-followup.js +12 -4
- package/dist/src/prompt/prompt.js +88 -38
- package/dist/src/report/report-data.js +53 -37
- package/dist/src/report/report-fold.js +16 -9
- package/dist/src/report/report-render.js +21 -194
- package/dist/src/report/report-since-data.js +4 -3
- package/dist/src/report/report-since-render.js +173 -0
- package/dist/src/report/report-usage-fold.js +8 -8
- package/dist/src/report/report.js +46 -22
- package/dist/src/review/post-merge-review-render.js +9 -3
- package/dist/src/review/post-merge-review.js +1 -1
- package/dist/src/review/review-precheck.js +11 -1
- package/dist/src/review/review.js +40 -153
- package/dist/src/review/secret-leaks.js +26 -16
- package/dist/src/roles/role-catalog.js +11 -3
- package/dist/src/roles/role-guidance.js +16 -12
- package/dist/src/roles/role-render.js +1 -14
- package/dist/src/roles/role-view.js +9 -21
- package/dist/src/run/acceptance-greps.js +88 -45
- package/dist/src/run/run-apply.js +12 -6
- package/dist/src/run/run-detail-data.js +121 -68
- package/dist/src/run/run-detail-render.js +92 -35
- package/dist/src/run/run-detail.js +72 -41
- package/dist/src/run/run-prompt.js +63 -50
- package/dist/src/run/run-resume.js +76 -29
- package/dist/src/run/run-stage.js +135 -84
- package/dist/src/run/run-timing.js +8 -2
- package/dist/src/run/run-usage.js +31 -1
- package/dist/src/run/run-verdict.js +119 -50
- package/dist/src/run/stage-check.js +49 -32
- package/dist/src/run/stage-checks-shared.js +28 -0
- package/dist/src/run/stage-name-checks.js +111 -92
- package/dist/src/run/stage-path-checks.js +69 -42
- package/dist/src/run/stage-prose-checks.js +55 -40
- package/dist/src/runner/runner-outcome.js +6 -1
- package/dist/src/runner/runner-pi.js +3 -10
- package/dist/src/runner/runner-recovery.js +1 -1
- package/dist/src/runner/{revision.js → runner-revision.js} +1 -1
- package/dist/src/runner/runner-run.js +97 -35
- package/dist/src/runner/runner-state-load.js +12 -9
- package/dist/src/runner/runner.js +41 -25
- package/dist/src/scheduling/claims.js +0 -4
- package/dist/src/scheduling/scheduling.js +53 -66
- package/dist/src/status/status-data.js +2 -2
- package/dist/src/status/status-role-notes.js +2 -2
- package/dist/src/status/status-roles.js +9 -17
- package/dist/src/team/{error-storm.js → team-error-storm.js} +1 -1
- package/dist/src/team/{failure-spread.js → team-failure-spread.js} +5 -5
- package/dist/src/team/team-hold.js +98 -65
- package/dist/src/team/{orchestrator-info.js → team-orchestrator-info.js} +2 -15
- package/dist/src/team/team-polls.js +28 -126
- package/dist/src/team/{reclaim.js → team-reclaim.js} +61 -31
- package/dist/src/team/team-state.js +4 -13
- package/dist/src/text/markdown.js +1 -1
- package/dist/src/text/phrases.js +20 -15
- package/dist/src/text/text.js +3 -3
- package/dist/src/ui/alert-feeds.js +49 -73
- package/dist/src/ui/badges.js +3 -13
- package/dist/src/ui/gui/gui-client-boot.js +2 -2
- package/dist/src/ui/gui/gui-client-composer.js +1 -1
- package/dist/src/ui/gui/gui-client-drawer-config.js +7 -17
- package/dist/src/ui/gui/gui-client-drawer.js +1 -1
- package/dist/src/ui/gui/gui-client-history.js +28 -14
- package/dist/src/ui/gui/gui-client-model.js +0 -12
- package/dist/src/ui/gui/gui-client-pending.js +7 -6
- package/dist/src/ui/gui/gui-client-report-chart.js +5 -5
- package/dist/src/ui/gui/gui-client-report.js +1 -1
- package/dist/src/ui/gui/gui-client-roles.js +0 -1
- package/dist/src/ui/gui/gui-client-settings.js +43 -84
- package/dist/src/ui/gui/gui-client-team.js +4 -4
- package/dist/src/ui/gui/gui-client.js +7 -1
- package/dist/src/ui/progress-data.js +53 -40
- package/dist/src/ui/run-progress-model.js +9 -2
- package/dist/src/ui/status-model.js +2 -24
- package/dist/src/ui/status-payload.js +3 -7
- package/dist/src/ui/status-render.js +5 -9
- package/dist/src/ui/team-alert-backlog.js +6 -6
- package/dist/src/ui/team-alert-status.js +9 -170
- package/dist/src/ui/team-alert-tail.js +19 -215
- package/dist/src/ui/team-alerts.js +7 -8
- package/dist/src/ui/transcript-run.js +27 -16
- package/dist/src/ui/tui/tui-editor-answer.js +12 -10
- package/dist/src/ui/tui/tui-editor-attach.js +4 -3
- package/dist/src/ui/tui/tui-editor-backends.js +4 -3
- package/dist/src/ui/tui/tui-editor-brief.js +6 -9
- package/dist/src/ui/tui/tui-editor-budget.js +4 -3
- package/dist/src/ui/tui/tui-editor-config.js +80 -68
- package/dist/src/ui/tui/tui-editor-entry.js +12 -20
- package/dist/src/ui/tui/tui-editor-pause.js +51 -57
- package/dist/src/ui/tui/tui-editor-prompt.js +9 -13
- package/dist/src/ui/tui/tui-editor-queued.js +11 -14
- package/dist/src/ui/tui/tui-editor-quiet.js +4 -3
- package/dist/src/ui/tui/tui-editor-release.js +4 -3
- package/dist/src/ui/tui/tui-editor-retire.js +7 -7
- package/dist/src/ui/tui/tui-editor-role-enabled.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-file.js +4 -4
- package/dist/src/ui/tui/tui-editor-role-key.js +4 -3
- package/dist/src/ui/tui/tui-editor-role-model.js +97 -15
- package/dist/src/ui/tui/tui-editor-search.js +4 -3
- package/dist/src/ui/tui/tui-editor-session.js +17 -4
- package/dist/src/ui/tui/tui-editors.js +1 -7
- package/dist/src/ui/tui/tui-frame.js +182 -134
- package/dist/src/ui/tui/tui-guard.js +20 -0
- package/dist/src/ui/tui/tui-input.js +159 -92
- package/dist/src/ui/tui/tui-keymap.js +51 -29
- package/dist/src/ui/tui/tui-keys.js +1 -1
- package/dist/src/ui/tui/tui-pane-activity.js +3 -3
- package/dist/src/ui/tui/tui-pane-backlog.js +74 -36
- package/dist/src/ui/tui/tui-pane-ctx.js +1 -1
- package/dist/src/ui/tui/tui-pane-health.js +252 -228
- package/dist/src/ui/tui/tui-pane-history.js +61 -70
- package/dist/src/ui/tui/tui-pane-pending.js +284 -247
- package/dist/src/ui/tui/tui-pane-queued.js +25 -27
- package/dist/src/ui/tui/tui-pane-report.js +2 -1
- package/dist/src/ui/tui/tui-pane-session.js +11 -7
- package/dist/src/ui/tui/tui-pane-transcript.js +19 -31
- package/dist/src/ui/tui/tui-pane.js +14 -11
- package/dist/src/ui/tui/tui-panes.js +29 -32
- package/dist/src/ui/tui/tui.js +3 -4
- package/dist/src/verdict/fix-claim.js +76 -54
- package/dist/src/verdict/refusal.js +1 -1
- package/package.json +5 -5
- package/dist/src/concurrency/provider-lease.js +0 -105
- package/dist/src/runner/model-fallback.js +0 -76
- package/dist/src/runner/runner-fallback.js +0 -66
- package/dist/src/ui/tui/tui-editor-claude-reserve.js +0 -24
- package/dist/src/ui/tui/tui-editor-provider-caps.js +0 -27
- /package/dist/src/backlog/{question-answer.js → backlog-question-answer.js} +0 -0
- /package/dist/src/doctor/{probe.js → doctor-probe.js} +0 -0
- /package/dist/src/gates/{disk-gate.js → disk-gates.js} +0 -0
- /package/dist/src/operator/{retire.js → operator-retire.js} +0 -0
- /package/dist/src/operator/{revert.js → operator-revert.js} +0 -0
- /package/dist/src/runner/{leftover.js → runner-leftover.js} +0 -0
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ and is also outside `npm test`.
|
|
|
42
42
|
## Status
|
|
43
43
|
|
|
44
44
|
<!-- tumwater:status:start -->
|
|
45
|
-
**v0.4.
|
|
45
|
+
**v0.4.3**: working tumwater. The director and 15 of the 17 roles are enabled by default; `selfcheck`,
|
|
46
46
|
which files tumwater bugs from tumwater's own event log, and `feedback`, which files them as GitHub
|
|
47
47
|
issues on znation/tumwater, are opt-in (`roles.selfcheck.enabled`, `roles.feedback.enabled`).
|
|
48
48
|
|
|
@@ -75,9 +75,10 @@ and `tumwater pause`, `resume`, `wake`, and `stop` control the roles. `tumwater
|
|
|
75
75
|
so pair it with `--token <secret>`.
|
|
76
76
|
|
|
77
77
|
`tumwater prompt --attach <image>` gives a role an image to read at its next run. When a role's
|
|
78
|
-
model cannot read images, the command refuses the attachment up front (naming the fix)
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
model cannot read images, the command refuses the attachment up front (naming the fix) unless
|
|
79
|
+
the role reroutes a blind read to a vision model, in which case it says where the message will
|
|
80
|
+
run; the dashboards and `tumwater role <id>` say which roles read images and where a blind
|
|
81
|
+
run reroutes.
|
|
81
82
|
|
|
82
83
|
Every command, flag, and TUI key is in [docs/cli.md](docs/cli.md); `tumwater help` prints the
|
|
83
84
|
same list, and `tumwater completion bash|zsh|fish` prints shell completion. Settings live in
|
|
@@ -107,8 +108,7 @@ object with both, so one config can mix a Claude strong tier with pi providers:
|
|
|
107
108
|
|
|
108
109
|
The default tier runs on pi through Hugging Face, the strong model jobs (the director, the plan
|
|
109
110
|
role, the reviewer, and the conflict resolver) run on the claude CLI's included usage, and the fallback runs on pi through
|
|
110
|
-
oMLX — all concurrently, under
|
|
111
|
-
`tumwater status` shows how many runs each key has going out of its cap). An OpenCode tier is
|
|
111
|
+
oMLX — all concurrently, under the team's `maxConcurrent` cap. An OpenCode tier is
|
|
112
112
|
`{ "backend": "opencode", "model": "deepseek-v4.1-flash-free" }`, so its free tier can sit beside pi
|
|
113
113
|
and claude in one config; a codex tier is `{ "backend": "codex", "model": "gpt-6.1-sol" }`, so a ChatGPT
|
|
114
114
|
subscription can cover the team's default tier beside pi and claude. A Claude model that should stay on pi (billed per token) is the plain
|
package/dist/build-info.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/** The backend seam (plans/agent-backends.md "The seam"): what one non-interactive agent
|
|
2
2
|
* run needs from its backend — how to build the command, how to translate its native stdout
|
|
3
|
-
* into tumwater's canonical event stream, and how to classify its exit. src/agent/
|
|
4
|
-
* builds and spawns the run (the marker, parser, bin resolution and spawn); src/agent/run
|
|
3
|
+
* into tumwater's canonical event stream, and how to classify its exit. src/agent/agent-spawn.ts
|
|
4
|
+
* builds and spawns the run (the marker, parser, bin resolution and spawn); src/agent/agent-run.ts
|
|
5
5
|
* keeps the coordinator — the backend-neutral watchdogs, the abort wiring and the settlement-state
|
|
6
|
-
* assembly — while src/agent/
|
|
7
|
-
* split and the stderr window) and the settle/flush; the
|
|
6
|
+
* assembly — while src/agent/agent-stream.ts owns the streaming pipeline (the native-to-canonical
|
|
7
|
+
* stdout split and the stderr window) and the settle/flush; the
|
|
8
8
|
* post-run settlement — the group-kill and marker sweeps on exit, the spawn-error result and
|
|
9
9
|
* the close-time reconcile/classify that builds the `PiRunResult` — lives in
|
|
10
|
-
* src/agent/
|
|
10
|
+
* src/agent/agent-settle.ts. A backend registered here supplies the rest. src/agent/agent-run.ts
|
|
11
11
|
* registers the pi and claude adapters here.
|
|
12
12
|
*
|
|
13
|
-
* The registry is keyed by id and fed by explicit registration (src/agent/run
|
|
13
|
+
* The registry is keyed by id and fed by explicit registration (src/agent/agent-run.ts
|
|
14
14
|
* registers both adapters at load), so a test can register a fake backend without importing a
|
|
15
15
|
* real one. */
|
|
16
16
|
import { hasResumableSession } from "./session-ledger.js";
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* matcher, and the exit classification. This file keeps the coordinator — setup, the watchdog
|
|
7
7
|
* wiring, abort, the child's stream-attachment and the settlement-state assembly — and
|
|
8
8
|
* delegates the build-and-spawn stage (the marker,
|
|
9
|
-
* parser, bin resolution, `prepare`, invocation and spawn) to src/agent/
|
|
9
|
+
* parser, bin resolution, `prepare`, invocation and spawn) to src/agent/agent-spawn.ts, the
|
|
10
10
|
* streaming pipeline (native-to-canonical stdout split, the stderr window, the stream-minted
|
|
11
|
-
* session record and the settle/flush) to src/agent/
|
|
11
|
+
* session record and the settle/flush) to src/agent/agent-stream.ts, and the post-run settlement
|
|
12
12
|
* (spawn-error result, exit orphan sweep, close-time reconcile and
|
|
13
|
-
* classify) to src/agent/
|
|
13
|
+
* classify) to src/agent/agent-settle.ts. `runPi` (src/pi/pi.ts) is the thin alias the existing
|
|
14
14
|
* call sites use, so a caller and its fakes are untouched by this split. */
|
|
15
15
|
import fs from "node:fs";
|
|
16
16
|
import os from "node:os";
|
|
@@ -24,12 +24,12 @@ import { piBackend } from "../pi/pi-backend.js";
|
|
|
24
24
|
import { claudeBackend } from "./claude/claude-backend.js";
|
|
25
25
|
import { codexBackend } from "./codex/codex-backend.js";
|
|
26
26
|
import { opencodeBackend } from "./opencode/opencode-backend.js";
|
|
27
|
-
import { setupFailureResult, settleClose, settleError, settleExit } from "./
|
|
28
|
-
import { openAppendLog, spawnPiRun } from "./
|
|
29
|
-
import { createAgentStream } from "./
|
|
27
|
+
import { setupFailureResult, settleClose, settleError, settleExit } from "./agent-settle.js";
|
|
28
|
+
import { openAppendLog, spawnPiRun } from "./agent-spawn.js";
|
|
29
|
+
import { createAgentStream } from "./agent-stream.js";
|
|
30
30
|
// The streaming pipeline's over-long-native-line cap stays reachable from here, the historical
|
|
31
|
-
// home the test suite still imports it from, even though the value now lives in
|
|
32
|
-
export { MAX_NATIVE_LINE_CHARS } from "./
|
|
31
|
+
// home the test suite still imports it from, even though the value now lives in agent-stream.ts.
|
|
32
|
+
export { MAX_NATIVE_LINE_CHARS } from "./agent-stream.js";
|
|
33
33
|
// The seam's adapters. Registering here — where the lookup happens — keeps the registration
|
|
34
34
|
// explicit and lets a test register a fake under its own id.
|
|
35
35
|
registerAgentBackend(piBackend);
|
|
@@ -78,7 +78,7 @@ export function runAgent(opts) {
|
|
|
78
78
|
// The whole build-and-spawn stage — the marker write, parser construction, run marker,
|
|
79
79
|
// backend-bin resolution, notes shim, run spec, the backend's optional `prepare`, the
|
|
80
80
|
// session-ledger record, translator/decoders, and the child spawn — now lives in
|
|
81
|
-
// spawnPiRun (src/agent/
|
|
81
|
+
// spawnPiRun (src/agent/agent-spawn.ts) and returns the SpawnedRun bundle. A synchronous
|
|
82
82
|
// throw anywhere in it (a broken marker write, parser construction, backend.resolveBin,
|
|
83
83
|
// claude's invocation rejecting a run with no model) propagates out of the await and is
|
|
84
84
|
// rejected by the catch below, matching what a synchronous executor's throw used to get
|
|
@@ -133,7 +133,7 @@ export function runAgent(opts) {
|
|
|
133
133
|
// The streaming pipeline — native-to-canonical stdout split with the over-long-line
|
|
134
134
|
// discard, the stderr window and its fallback-clone warning matcher, the stream-minted
|
|
135
135
|
// session's second ledger record, and the settle/flush that resolves the run — now lives
|
|
136
|
-
// in src/agent/
|
|
136
|
+
// in src/agent/agent-stream.ts behind the AgentStream interface. agent-run only wires the
|
|
137
137
|
// child's `data` events to it and binds its getters into the settlement state below.
|
|
138
138
|
const stream = createAgentStream({
|
|
139
139
|
opts,
|
|
@@ -148,7 +148,7 @@ export function runAgent(opts) {
|
|
|
148
148
|
});
|
|
149
149
|
child.stdout?.on("data", (chunk) => stream.feedStdout(chunk));
|
|
150
150
|
child.stderr?.on("data", (chunk) => stream.feedStderr(chunk));
|
|
151
|
-
// The run state the settlement-half handlers (
|
|
151
|
+
// The run state the settlement-half handlers (agent-settle.ts) read when their event
|
|
152
152
|
// fires. The getters read through to the run's still-mutating feed state, so a handler
|
|
153
153
|
// sees the current value rather than a snapshot taken when its listener was registered.
|
|
154
154
|
const state = {
|
|
@@ -210,7 +210,7 @@ export function runAgent(opts) {
|
|
|
210
210
|
// run with no resolved model) rejects the run, preserving the contract a synchronous
|
|
211
211
|
// executor's throw used to get from the Promise constructor. No child was spawned, so no
|
|
212
212
|
// `error`/`close` ever fires and the settle path that removes the private TMPDIR
|
|
213
|
-
// (
|
|
213
|
+
// (agent-stream.ts's finish) never runs — remove it here, best-effort like finish does, so
|
|
214
214
|
// a broken run does not leave a `tumwater-run-*` temp dir behind.
|
|
215
215
|
try {
|
|
216
216
|
removeTree(runTmp);
|
|
@@ -218,6 +218,19 @@ export function runAgent(opts) {
|
|
|
218
218
|
catch {
|
|
219
219
|
/* left for the OS temp sweep */
|
|
220
220
|
}
|
|
221
|
+
// The two append-log streams opened before the build-and-spawn stage (the raw and
|
|
222
|
+
// backend-native transcripts) are never handed to a stream-settlement path on this throw
|
|
223
|
+
// — there is no child and no finish(), so nothing else would ever end them — end them
|
|
224
|
+
// here best-effort (the same cleanup the outer setup-failure catch makes) or every
|
|
225
|
+
// rejected run leaks two open transcript descriptors that accumulate in the
|
|
226
|
+
// long-lived orchestrator process.
|
|
227
|
+
try {
|
|
228
|
+
rawLog?.stream.end();
|
|
229
|
+
nativeLog?.stream.end();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
/* best-effort */
|
|
233
|
+
}
|
|
221
234
|
reject(err instanceof Error ? err : new Error(`runAgent: ${String(err)}`));
|
|
222
235
|
}
|
|
223
236
|
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/** The settlement half of a backend agent run (the split of src/agent/run
|
|
1
|
+
/** The settlement half of a backend agent run (the split of src/agent/agent-run.ts along the
|
|
2
2
|
* same seam the runner-run → runner-outcome split used): everything that happens after the
|
|
3
3
|
* run's live feed — the spawn-`error` result, the `exit` orphan sweep, and the `close`-time
|
|
4
|
-
* flush/reconcile/classify that builds the `PiRunResult`. run
|
|
5
|
-
* (setup, watchdogs, the feed), delegates its build-and-spawn stage to src/agent/
|
|
6
|
-
* its streaming pipeline to src/agent/
|
|
4
|
+
* flush/reconcile/classify that builds the `PiRunResult`. agent-run.ts keeps the lifecycle
|
|
5
|
+
* (setup, watchdogs, the feed), delegates its build-and-spawn stage to src/agent/agent-spawn.ts,
|
|
6
|
+
* its streaming pipeline to src/agent/agent-stream.ts and its `error`/`exit`/`close` listeners
|
|
7
7
|
* here, so the streaming half stays readable.
|
|
8
8
|
*
|
|
9
|
-
* Each handler takes the run's live state (`RunSettleState`, defined in run
|
|
9
|
+
* Each handler takes the run's live state (`RunSettleState`, defined in agent-run.ts and
|
|
10
10
|
* imported here only as a type) and reads what it closes over at the moment its event fires —
|
|
11
11
|
* the live flags it needs are getters on that state. There is no runtime import cycle:
|
|
12
|
-
* run
|
|
12
|
+
* agent-run imports these functions at runtime, and this module imports only agent-run's type.
|
|
13
13
|
*/
|
|
14
14
|
import { backendBinConfigKey, backendBinSourceLabel } from "../pi/pi-bin.js";
|
|
15
15
|
import { terminateChild } from "../process/process.js";
|
|
@@ -165,44 +165,68 @@ function settleErrorMessage(state, blindRead, failed, stderr, code) {
|
|
|
165
165
|
return (state.parser.errorMessage ??
|
|
166
166
|
(failed ? stderr.trim().slice(-500) || `the agent exited ${code}` : undefined));
|
|
167
167
|
}
|
|
168
|
-
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
|
|
172
|
-
const { backend, runSpec, invocation, translator, onLine } = state;
|
|
168
|
+
/** Flush the tail the decoders and translator held back at stream end: the decoder's last
|
|
169
|
+
* bytes and unterminated final line, then a batching backend's unclosed final assistant
|
|
170
|
+
* message (which a killed run never closes with a later event). */
|
|
171
|
+
function flushRunTail(state) {
|
|
173
172
|
// Flush any bytes the decoder held back at stream end and any unterminated final line so the
|
|
174
173
|
// tail is not lost, then let the translator flush its own pending canonical line(s) — a
|
|
175
174
|
// batching backend's unclosed last assistant message, which a killed run never closes with a
|
|
176
175
|
// later event.
|
|
177
176
|
state.feedCanonical(state.decoder.end(), true);
|
|
178
|
-
for (const canonical of translator.finalize?.() ?? [])
|
|
179
|
-
state.parser.feed(canonical + "\n", onLine);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
for (const canonical of state.translator.finalize?.() ?? [])
|
|
178
|
+
state.parser.feed(canonical + "\n", state.onLine);
|
|
179
|
+
}
|
|
180
|
+
/** Reconcile the backend's post-run work (opencode's authoritative session export), only when
|
|
181
|
+
* the run is not already settled (a failed spawn settles in the 'error' handler before
|
|
182
|
+
* 'close') and was not killed — a killed run's session is half-done and no export should spend
|
|
183
|
+
* its 10 s on it. Its lines are written and parsed like streamed ones, so they reach the raw
|
|
184
|
+
* log and the parser's authoritative fold (run_cost/run_usage). */
|
|
185
|
+
function reconcileBackendAfterRun(state, killed) {
|
|
183
186
|
// The backend's post-run reconciliation (opencode's authoritative session export), only when
|
|
184
187
|
// the run is not already settled (a failed spawn settles in the 'error' handler before
|
|
185
188
|
// 'close') and was not killed — a killed run's session is half-done and no export should
|
|
186
189
|
// spend its 10 s on it. Its lines are written and parsed like streamed ones, so they reach
|
|
187
190
|
// the raw log and the parser's authoritative fold (run_cost/run_usage).
|
|
188
191
|
if (!state.settled() && !killed) {
|
|
189
|
-
for (const canonical of backend.afterRun?.(runSpec, invocation, translator) ?? []) {
|
|
190
|
-
state.parser.feed(canonical + "\n", onLine);
|
|
192
|
+
for (const canonical of state.backend.afterRun?.(state.runSpec, state.invocation, state.translator) ?? []) {
|
|
193
|
+
state.parser.feed(canonical + "\n", state.onLine);
|
|
191
194
|
}
|
|
192
195
|
}
|
|
193
|
-
|
|
196
|
+
}
|
|
197
|
+
/** Whether the exit reads as failed: killed, a parser error stop, or a non-zero code with no
|
|
198
|
+
* final text. */
|
|
199
|
+
function closeFailed(state, code, killed) {
|
|
200
|
+
return (killed ||
|
|
194
201
|
state.parser.stopReason === "error" ||
|
|
195
|
-
(code !== 0 && !state.parser.finalText.trim());
|
|
202
|
+
(code !== 0 && !state.parser.finalText.trim()));
|
|
203
|
+
}
|
|
204
|
+
/** Whether the exit is a permanent config error — a non-killed exit with no transient pi crash
|
|
205
|
+
* and no backend kind, whose parser flag or backend classification say config error. */
|
|
206
|
+
function closeConfigError(state, cls, killed) {
|
|
207
|
+
return (!killed &&
|
|
208
|
+
cls.transientPiCrash !== true &&
|
|
209
|
+
cls.backendKind === undefined &&
|
|
210
|
+
(state.parser.configError || cls.configError === true));
|
|
211
|
+
}
|
|
212
|
+
/** The run's `close` event: the process died and all its streams closed. Flush what the
|
|
213
|
+
* decoders held back, reconcile the backend's post-run work, classify the exit cause, and
|
|
214
|
+
* settle the run's result. */
|
|
215
|
+
export function settleClose(state, code) {
|
|
216
|
+
const { backend } = state;
|
|
217
|
+
flushRunTail(state);
|
|
218
|
+
// A blind image read on an authoring run deliberately killed it, so its exit is a kill like
|
|
219
|
+
// the watchdogs' — the half-done work is discarded.
|
|
220
|
+
const killed = state.wasKilled();
|
|
221
|
+
reconcileBackendAfterRun(state, killed);
|
|
222
|
+
const failed = closeFailed(state, code, killed);
|
|
196
223
|
// The backend classifies the exit's stderr (pi: the transient JSON-parse crash, a
|
|
197
224
|
// provider-wide backend failure, a permanent config error). The runner merges that with the
|
|
198
225
|
// parser's own event-derived flags — one exit has one cause.
|
|
199
226
|
const stderr = state.stderr();
|
|
200
227
|
const cls = backend.classifyExit({ code, stderr, killed });
|
|
201
228
|
const stderrBackend = cls.backendKind;
|
|
202
|
-
const configError =
|
|
203
|
-
cls.transientPiCrash !== true &&
|
|
204
|
-
stderrBackend === undefined &&
|
|
205
|
-
(state.parser.configError || cls.configError === true);
|
|
229
|
+
const configError = closeConfigError(state, cls, killed);
|
|
206
230
|
const blindRead = state.blindRead();
|
|
207
231
|
const errorMessage = settleErrorMessage(state, blindRead, failed, stderr, code);
|
|
208
232
|
state.finish(resultFromParser(state, {
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* parser, the run marker, backend-bin resolution, the notes shim, the run spec, the backend's
|
|
4
4
|
* optional `prepare`, the session-ledger record, the native-to-canonical translator, the two
|
|
5
5
|
* incremental decoders, and the child spawn. It returns the `SpawnedRun` bundle the coordinator
|
|
6
|
-
* (src/agent/run
|
|
7
|
-
* createAgentStream (src/agent/
|
|
8
|
-
* Part 1/2 of the runAgent split: the streaming pipeline (part 2/2) lives in
|
|
9
|
-
* This module imports only run
|
|
6
|
+
* (src/agent/agent-run.ts) binds its watchdogs and settlement wiring to, and passes to
|
|
7
|
+
* createAgentStream (src/agent/agent-stream.ts).
|
|
8
|
+
* Part 1/2 of the runAgent split: the streaming pipeline (part 2/2) lives in agent-stream.ts.
|
|
9
|
+
* This module imports only agent-run's type surface, so
|
|
10
10
|
* there is no runtime import cycle. */
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
import fs from "node:fs";
|
|
@@ -100,6 +100,15 @@ function buildAndSpawn(backend, opts, pre) {
|
|
|
100
100
|
blindRead: () => blind.read,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
103
|
+
/** Warn on a run's own root that a backend's optional `prepare` step failed, then let the run
|
|
104
|
+
* go on without it — the shared tail of the synchronous-throw and async-rejection prepare guards
|
|
105
|
+
* below, so both warn the same way. `warnRoot` is undefined when the session dir has no team
|
|
106
|
+
* root; the warning is then skipped because there is no tumwater event log to write to. */
|
|
107
|
+
function warnPrepareFailed(warnRoot, error) {
|
|
108
|
+
if (warnRoot !== undefined) {
|
|
109
|
+
warnEventBestEffort(warnRoot, TUMWATER_SOURCE, `agent prepare failed, run continues: ${errorMessage(error)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
103
112
|
/** Build and spawn one non-interactive agent run, from the marker write through the child spawn,
|
|
104
113
|
* and return the bundle the run's stream pipeline, watchdogs and settlement wiring consume. The
|
|
105
114
|
* caller's setup (opened logs, the run's private TMPDIR) is passed in; a synchronous throw here
|
|
@@ -151,8 +160,12 @@ export function spawnPiRun(backend, logs, runTmp, opts) {
|
|
|
151
160
|
},
|
|
152
161
|
});
|
|
153
162
|
// The run's cross-group attribution mark, minted before the spawn so the environment can carry
|
|
154
|
-
// it and the exit sweep can name it.
|
|
155
|
-
|
|
163
|
+
// it and the exit sweep can name it. When the session sits under a team root, the mark is
|
|
164
|
+
// stamped with that root's token so a dead-marked orphan can be attributed to this checkout
|
|
165
|
+
// and never reaped as another team's (BUGS.md, "Reap the doctor's orphans, part 1/3"); a
|
|
166
|
+
// session with no team root (a lab script, a smoke run, a prompt-lab replay) stays rootless.
|
|
167
|
+
const markerRoot = nearestTumwaterRoot(opts.sessionDir);
|
|
168
|
+
const runMarker = makeRunMarker(process.pid, markerRoot);
|
|
156
169
|
const resolved = backend.resolveBin(opts.config);
|
|
157
170
|
// Install the notes shim under the team root's `.tumwater/bin` so every backend's run finds
|
|
158
171
|
// `tumwater-notes` first on PATH (notes-shim.ts). A failure to write it logs one warning and
|
|
@@ -202,9 +215,7 @@ export function spawnPiRun(backend, logs, runTmp, opts) {
|
|
|
202
215
|
prepared = backend.prepare?.(runSpec);
|
|
203
216
|
}
|
|
204
217
|
catch (error) {
|
|
205
|
-
|
|
206
|
-
warnEventBestEffort(warnRoot, TUMWATER_SOURCE, `agent prepare failed, run continues: ${errorMessage(error)}`);
|
|
207
|
-
}
|
|
218
|
+
warnPrepareFailed(warnRoot, error);
|
|
208
219
|
}
|
|
209
220
|
if (prepared === undefined) {
|
|
210
221
|
// No `prepare` step: build and spawn synchronously and hand the bundle straight back, so
|
|
@@ -217,9 +228,7 @@ export function spawnPiRun(backend, logs, runTmp, opts) {
|
|
|
217
228
|
await prepared;
|
|
218
229
|
}
|
|
219
230
|
catch (error) {
|
|
220
|
-
|
|
221
|
-
warnEventBestEffort(warnRoot, TUMWATER_SOURCE, `agent prepare failed, run continues: ${errorMessage(error)}`);
|
|
222
|
-
}
|
|
231
|
+
warnPrepareFailed(warnRoot, error);
|
|
223
232
|
}
|
|
224
233
|
return buildAndSpawn(backend, opts, { runSpec, parser, runMarker, resolved, blind, runTmp });
|
|
225
234
|
})();
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* canonical stdout line splitting with its over-long-line discard, the stderr window and its
|
|
4
4
|
* fallback-clone warning matcher, the stream-minted session's second ledger record, and the
|
|
5
5
|
* settle/flush that resolves the run's promise. It is the part of the runAgent split (part
|
|
6
|
-
* 2/2) that the coordinator (src/agent/run
|
|
7
|
-
* interface: run
|
|
6
|
+
* 2/2) that the coordinator (src/agent/agent-run.ts) delegates to behind the `AgentStream`
|
|
7
|
+
* interface: agent-run wires the child's `data` events to `feedStdout`/`feedStderr`, binds the
|
|
8
8
|
* settlement state's stream getters, and keeps the watchdogs, abort wiring and the
|
|
9
|
-
* `error`/`exit`/`close` listeners itself. This module imports only run
|
|
10
|
-
* and
|
|
9
|
+
* `error`/`exit`/`close` listeners itself. This module imports only agent-run's, agent-spawn's
|
|
10
|
+
* and agent-settle's type surfaces, so there is no runtime import cycle. */
|
|
11
11
|
import { removeTree } from "../files/files.js";
|
|
12
12
|
import { recordSession, sessionRecordFor } from "./session-ledger.js";
|
|
13
13
|
/** How many trailing stderr characters are carried into the next chunk's warning match, so a
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* read (plans/opencode-backend.md "Groundwork: the claude-only branches become backend
|
|
4
4
|
* traits"). Pure — imports nothing from the runtime registry (agent-backend.ts) — so those
|
|
5
5
|
* layers can read a backend's traits without pulling the registry in. That is the same reason
|
|
6
|
-
* `BACKEND_DEFAULT_MODELS` lives in src/config/model-selector.ts.
|
|
6
|
+
* `BACKEND_DEFAULT_MODELS` lives in src/config/config-model-selector.ts.
|
|
7
7
|
*
|
|
8
|
-
* An id with no entry reads as pi, because src/agent/run
|
|
8
|
+
* An id with no entry reads as pi, because src/agent/agent-run.ts routes an unregistered id to
|
|
9
9
|
* the pi backend.
|
|
10
10
|
*/
|
|
11
11
|
import { HARNESS_CLAUDE_FLAGS } from "./claude/claude-args.js";
|
|
@@ -17,7 +17,7 @@ import { spawnSync } from "node:child_process";
|
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import { resolvedBackendConfig } from "../../config/config-views.js";
|
|
19
19
|
import { parseJsonObject } from "../../files/json-object.js";
|
|
20
|
-
import { observeAccount, readAccount, subscriptionWindowRejected,
|
|
20
|
+
import { observeAccount, readAccount, subscriptionWindowRejected, windowEntries, windowLabel, windowReserved, windowUsedUp, writeAccount, } from "../subscription-account.js";
|
|
21
21
|
import { claudeBackend, claudeConfigDir } from "./claude-backend.js";
|
|
22
22
|
import { claudeEnv, claudeThirdPartyProvider } from "./claude-env.js";
|
|
23
23
|
/** Read the persisted claude account state, or null when it is absent or unreadable. A file
|
|
@@ -57,13 +57,6 @@ export { subscriptionWindowRejected, windowReserved };
|
|
|
57
57
|
export function claudeWindowLabel(type) {
|
|
58
58
|
return windowLabel(type);
|
|
59
59
|
}
|
|
60
|
-
/** The fraction of a subscription usage window consumed as a whole-number percent —
|
|
61
|
-
* subscription-account.ts's utilizationPct, under its historical claude name. */
|
|
62
|
-
export function claudeUtilizationPct(utilization) {
|
|
63
|
-
return utilizationPct(utilization);
|
|
64
|
-
}
|
|
65
|
-
/** The account's windows as a list, most constrained first — subscription-account.ts's
|
|
66
|
-
* windowEntries, under its historical claude name. */
|
|
67
60
|
export function claudeWindowEntries(account) {
|
|
68
61
|
return windowEntries(account);
|
|
69
62
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The claude adapter for the agent seam (plans/agent-backends.md "The seam", "Sessions",
|
|
2
2
|
* "The claude invocation"). It shapes one run's argv/stdin/env, translates claude's
|
|
3
3
|
* `stream-json` into the canonical stream, and classifies the exit's stderr — everything
|
|
4
|
-
* backend-specific. src/agent/
|
|
4
|
+
* backend-specific. src/agent/agent-spawn.ts spawns the run and records this run in the session
|
|
5
5
|
* ledger; claude mints its own session id (a fresh UUID, or the resumed record's id), which
|
|
6
6
|
* `invocation` returns so the ledger can carry it.
|
|
7
7
|
*
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* missing transcript, a different cwd, or another kind's newer session all review fresh rather
|
|
12
12
|
* than resuming the wrong conversation.
|
|
13
13
|
*
|
|
14
|
-
* Registration lives in src/agent/run
|
|
14
|
+
* Registration lives in src/agent/agent-run.ts beside the pi adapter, so a test can register a
|
|
15
15
|
* fake under its own id; a test selects claude by setting `config.agent = "claude"` on a run. */
|
|
16
16
|
import fs from "node:fs";
|
|
17
17
|
import os from "node:os";
|
|
@@ -19,7 +19,7 @@ import path from "node:path";
|
|
|
19
19
|
import { randomUUID } from "node:crypto";
|
|
20
20
|
import { resolvedBackendConfig } from "../../config/config-views.js";
|
|
21
21
|
import { MODEL_TIERS } from "../../config/config-schema.js";
|
|
22
|
-
import { routeModel } from "../../config/model-selector.js";
|
|
22
|
+
import { routeModel } from "../../config/config-model-selector.js";
|
|
23
23
|
import { backendRunEnv } from "../../process/run-marker.js";
|
|
24
24
|
import { resolveBinForBackend } from "../../pi/pi-bin.js";
|
|
25
25
|
import { expandHomeTilde } from "../../files/files.js";
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* the foreground. `BASH_MAX_TIMEOUT_MS` is derived from `quietTimeoutSeconds` so claude's own
|
|
18
18
|
* Bash timeout fires before tumwater's quiet kill and the model sees a tool error rather than a
|
|
19
19
|
* dead run. */
|
|
20
|
+
import { scrubEnv } from "../env-scrub.js";
|
|
20
21
|
/** The cloud-provider switches that make a claude run bill as API rather than a claude.ai
|
|
21
22
|
* subscription: Bedrock, Vertex, and Azure Foundry. They carry operator intent, so the run
|
|
22
23
|
* environment keeps them (the allowlist below); claude-account's seedBilling and this module's
|
|
@@ -54,35 +55,30 @@ const MIN_BASH_MAX_TIMEOUT_MS = 1_000;
|
|
|
54
55
|
/** Build the claude process environment from `base`. Never mutates `base`. */
|
|
55
56
|
export function claudeEnv(base, opts) {
|
|
56
57
|
const hadClaudeCode = base.CLAUDECODE !== undefined;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// own config and transcripts under it, so a funes hook on ~/.claude/projects never indexes
|
|
84
|
-
// team runs. Unset leaves any inherited CLAUDE_CONFIG_DIR (or claude's default) untouched.
|
|
85
|
-
if (opts.configDir)
|
|
86
|
-
env.CLAUDE_CONFIG_DIR = opts.configDir;
|
|
87
|
-
return env;
|
|
58
|
+
return scrubEnv(base, {
|
|
59
|
+
keep: (key) => (key !== "CLAUDECODE" && key !== "CLAUDE_PID" && key !== "CLAUDE_EFFORT") &&
|
|
60
|
+
!(key.startsWith("CLAUDE_CODE_") && !CLAUDE_CODE_ALLOWLIST.has(key)) &&
|
|
61
|
+
!key.startsWith("CLAUDE_AGENT_SDK_") &&
|
|
62
|
+
// The host session's proxy, not the operator's: only when CLAUDECODE says we are inside one.
|
|
63
|
+
!(key === "ANTHROPIC_BASE_URL" && hadClaudeCode),
|
|
64
|
+
then: (env) => {
|
|
65
|
+
env.CLAUDE_CODE_DISABLE_AUTO_MEMORY = "1";
|
|
66
|
+
env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
|
|
67
|
+
env.BASH_DEFAULT_TIMEOUT_MS = String(BASH_DEFAULT_TIMEOUT_MS);
|
|
68
|
+
if (opts.quietTimeoutSeconds > 0) {
|
|
69
|
+
// 60 s under the quiet kill, so claude's tool error arrives before the run is
|
|
70
|
+
// declared hung; floored so a short quiet window never derives a non-positive cap.
|
|
71
|
+
const derived = opts.quietTimeoutSeconds * 1000 - 60_000;
|
|
72
|
+
env.BASH_MAX_TIMEOUT_MS = String(Math.max(derived, MIN_BASH_MAX_TIMEOUT_MS));
|
|
73
|
+
}
|
|
74
|
+
env.DISABLE_AUTOUPDATER = "1";
|
|
75
|
+
env.DISABLE_TELEMETRY = "1";
|
|
76
|
+
env.DISABLE_ERROR_REPORTING = "1";
|
|
77
|
+
// A team config dir (plans/notes-command.md "Keeping funes out of team runs"): force claude's
|
|
78
|
+
// own config and transcripts under it, so a funes hook on ~/.claude/projects never indexes
|
|
79
|
+
// team runs. Unset leaves any inherited CLAUDE_CONFIG_DIR (or claude's default) untouched.
|
|
80
|
+
if (opts.configDir)
|
|
81
|
+
env.CLAUDE_CONFIG_DIR = opts.configDir;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
88
84
|
}
|