@robmclarty/volley 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +335 -32
- package/dist/cli.js +4944 -3320
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +442 -304
- package/dist/index.js +3578 -2658
- package/dist/index.js.map +1 -1
- package/package.json +24 -15
- package/src/builder/presets/harness_append_acp.md +86 -0
- package/src/critic/presets/harness_append.md +2 -1
- package/src/critic/presets/harness_append_acp.md +27 -0
- package/src/critic/presets/harness_append_local.md +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { z } from
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Shared value and type contracts for volley.
|
|
6
|
-
*
|
|
7
|
-
* Type aliases are PascalCase; value-level identifiers are snake_case.
|
|
8
|
-
* Fascicle's usage/cost types are used verbatim so summaries carry the
|
|
9
|
-
* substrate's shapes, not a re-derivation.
|
|
10
|
-
*/
|
|
11
|
-
|
|
1
|
+
import { Engine, FinishReason, StreamChunk, Throughput, Tool, ToolApprovalHandler, TrajectoryLogger, UsageTotals } from "fascicle";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/types.d.ts
|
|
12
4
|
type Verdict = 'approved' | 'changes_requested';
|
|
13
5
|
type CriticPreset = 'reviewer' | 'optimizer' | 'researcher';
|
|
14
6
|
/** Transport for the critic role. The critic is read-only + schema-constrained,
|
|
15
|
-
* so it has always been able to run on a local model.
|
|
16
|
-
|
|
7
|
+
* so it has always been able to run on a local model. `openrouter` reaches a
|
|
8
|
+
* hosted model over one API key and takes the local critic's tools and
|
|
9
|
+
* degradation ladder. */
|
|
10
|
+
type CriticProvider = 'claude_cli' | 'ollama' | 'lmstudio' | 'openrouter';
|
|
17
11
|
/** Transport for the builder role. `claude_cli` is a full agentic Claude Code
|
|
18
|
-
* session;
|
|
19
|
-
* (write/exec tools supplied by the harness)
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
type BuilderProvider = 'claude_cli' | 'ollama' | 'lmstudio';
|
|
12
|
+
* session; every other provider runs the builder as a volley-driven tool loop
|
|
13
|
+
* (write/exec tools supplied by the harness), whether its model is local
|
|
14
|
+
* (`ollama`, `lmstudio`) or hosted (`openrouter`). */
|
|
15
|
+
type BuilderProvider = 'claude_cli' | 'ollama' | 'lmstudio' | 'openrouter';
|
|
23
16
|
type BuilderPermissionMode = 'acceptEdits' | 'bypassPermissions';
|
|
24
17
|
/** `auto`, `none`, or any shell command (the `string & {}` keeps the literal
|
|
25
18
|
* hints in editor completions without collapsing the union). */
|
|
@@ -30,116 +23,139 @@ type CostSource = 'provider_reported' | 'engine_derived' | 'unknown';
|
|
|
30
23
|
* died. Recorded alongside a critic retry so a degraded run says
|
|
31
24
|
* *what* failed, not just that it retried. */
|
|
32
25
|
type CauseKind = 'provider_5xx' | 'network' | 'unknown';
|
|
33
|
-
|
|
26
|
+
/** Why a local critic's verdict came from the tool-less fallback:
|
|
27
|
+
* `stream_error`, the tool-bearing call kept dying on the provider's stream
|
|
28
|
+
* after the bounded retry; `step_exhaustion`, it spent its step budget (or
|
|
29
|
+
* stopped on a tool call) without producing a verdict. */
|
|
30
|
+
type DegradeCause = 'stream_error' | 'step_exhaustion';
|
|
31
|
+
type RunStatus = 'running' | 'success' | 'budget_exhausted' | 'cost_cap_reached' |
|
|
34
32
|
/** The builder edited the gate that judges it, under `--fail-on-gate-edit`:
|
|
35
33
|
* the run halts on the spot rather than iterating, because a check the builder
|
|
36
34
|
* can rewrite is not evidence the work is done. */
|
|
37
|
-
|
|
35
|
+
'gate_edit_blocked' | 'interrupted' | 'error';
|
|
38
36
|
/** User-facing configuration, before resolution (config file / flags). */
|
|
39
37
|
type VolleyConfig = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
38
|
+
prompt: string;
|
|
39
|
+
workspace: string;
|
|
40
|
+
criteria: string;
|
|
41
|
+
check?: CheckMode;
|
|
42
|
+
builder_model?: string;
|
|
43
|
+
builder_provider?: BuilderProvider;
|
|
44
|
+
builder_url?: string;
|
|
45
|
+
builder_max_steps?: number;
|
|
46
|
+
allow_unsandboxed_builder?: boolean;
|
|
47
|
+
critic_model?: string;
|
|
48
|
+
critic_provider?: CriticProvider;
|
|
49
|
+
critic_url?: string;
|
|
50
|
+
builder_permission_mode?: BuilderPermissionMode;
|
|
51
|
+
critic?: CriticPreset | (string & {});
|
|
52
|
+
max_iterations?: number;
|
|
53
|
+
max_cost_usd?: number | null;
|
|
54
|
+
git_checkpoints?: boolean;
|
|
55
|
+
worktree?: boolean;
|
|
56
|
+
discard_worktree?: boolean;
|
|
57
|
+
gate_paths?: string[];
|
|
58
|
+
fail_on_gate_edit?: boolean;
|
|
59
|
+
sandbox_image?: string;
|
|
60
|
+
state_dir?: string;
|
|
61
|
+
verbose?: boolean;
|
|
62
|
+
quiet?: boolean;
|
|
63
|
+
json?: boolean;
|
|
64
|
+
show_thinking?: boolean;
|
|
65
|
+
dry_run?: boolean;
|
|
65
66
|
};
|
|
66
|
-
/** Fully resolved run configuration;
|
|
67
|
+
/** Fully resolved run configuration; the run's `config.json` mirrors this. */
|
|
67
68
|
type ResolvedConfig = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
69
|
+
version: 2;
|
|
70
|
+
run_id: string;
|
|
71
|
+
started_at: string;
|
|
72
|
+
prompt: string;
|
|
73
|
+
criteria: string;
|
|
74
|
+
check: CheckMode;
|
|
75
|
+
check_resolved: CheckRunnerKind;
|
|
76
|
+
builder_model: string;
|
|
77
|
+
builder_provider: BuilderProvider;
|
|
78
|
+
/** The builder seat's own model server base URL (`--builder-url`), as given.
|
|
79
|
+
* It is normalized, and crossed to the host LLM endpoint when contained, only
|
|
80
|
+
* where it is used, because the crossing depends on where the run executes.
|
|
81
|
+
* Null falls back to the provider's env var (`VOLLEY_OLLAMA_URL` /
|
|
82
|
+
* `VOLLEY_LMSTUDIO_URL` / `VOLLEY_OPENROUTER_URL`), then its default, never to
|
|
83
|
+
* the critic's URL. Only a provider volley reaches at a base URL (ollama,
|
|
84
|
+
* lmstudio, openrouter) takes one. */
|
|
85
|
+
builder_url: string | null;
|
|
86
|
+
builder_max_steps: number;
|
|
87
|
+
/** Operator opt-out permitting a non-`claude_cli` builder to run without a
|
|
88
|
+
* sandbox — it gets a real host bash. Refused by default. */
|
|
89
|
+
allow_unsandboxed_builder: boolean;
|
|
90
|
+
critic_model: string;
|
|
91
|
+
critic_provider: CriticProvider;
|
|
92
|
+
/** The critic seat's own model server base URL (`--critic-url`), as given;
|
|
93
|
+
* resolved like `builder_url`, and null never falls back to the builder's. */
|
|
94
|
+
critic_url: string | null;
|
|
95
|
+
builder_permission_mode: BuilderPermissionMode;
|
|
96
|
+
critic_preset: CriticPreset | 'custom';
|
|
97
|
+
critic_prompt_path: string | null;
|
|
98
|
+
max_iterations: number;
|
|
99
|
+
max_cost_usd: number | null;
|
|
100
|
+
git_checkpoints: boolean;
|
|
101
|
+
/** Isolate the builder run's effects in a per-run git worktree: the
|
|
102
|
+
* builder/critic file tools' containment root moves to the worktree, while
|
|
103
|
+
* the run's state stays where `state_dir` puts it (the control plane). Off by
|
|
104
|
+
* default; requires the workspace to be a git repository. */
|
|
105
|
+
worktree: boolean;
|
|
106
|
+
/** Throw a `--worktree` run's effects away at teardown even when it converged:
|
|
107
|
+
* the run branch is force-deleted rather than kept, because only the
|
|
108
|
+
* verdicts are wanted. This is what `volley matrix` sweeps with — one config,
|
|
109
|
+
* many builder×critic seats, no per-seat commits or branches left anywhere.
|
|
110
|
+
* Refused without `--worktree`; `--git` still wins, so a `--worktree --git`
|
|
111
|
+
* run integrates exactly as it always has. */
|
|
112
|
+
discard_worktree: boolean;
|
|
113
|
+
/** Glob patterns naming the *gate*: the tests, fixtures, and check
|
|
114
|
+
* configuration that decide whether the builder's work passes. An edit to one
|
|
115
|
+
* is reported to the critic and recorded in the summary — and refused outright
|
|
116
|
+
* under `fail_on_gate_edit`. Defaults to `DEFAULT_GATE_PATTERNS`; a config's
|
|
117
|
+
* own list replaces it, because what counts as the gate is the task's call
|
|
118
|
+
* (a test-writing task edits tests by design). */
|
|
119
|
+
gate_paths: string[];
|
|
120
|
+
/** Halt the run when the builder edits a `gate_paths` match, instead of
|
|
121
|
+
* reporting it and continuing. Off by default: a gate edit is legitimate often
|
|
122
|
+
* enough that refusing it by default would break honest tasks, and the mark on
|
|
123
|
+
* the summary already makes it impossible to miss. */
|
|
124
|
+
fail_on_gate_edit: boolean;
|
|
125
|
+
/** Container image the local-builder Docker sandbox runs. Defaults to
|
|
126
|
+
* volley's own `Dockerfile`-built image; `--sandbox-image <tag>` /
|
|
127
|
+
* `VOLLEY_SANDBOX_IMAGE` override it. Meaningful only for a local builder —
|
|
128
|
+
* the `claude_cli` path never uses Docker. */
|
|
129
|
+
sandbox_image: string;
|
|
130
|
+
/** The root that holds each run's state at `<state_dir>/<run_id>/`
|
|
131
|
+
* (`--state-dir`), resolved to an absolute path. Null keeps it at
|
|
132
|
+
* `<workspace>/.volley/`, rotated aside by the next fresh run. */
|
|
133
|
+
state_dir: string | null;
|
|
134
|
+
workspace: string;
|
|
135
|
+
verbose: boolean;
|
|
136
|
+
quiet: boolean;
|
|
137
|
+
json: boolean;
|
|
138
|
+
show_thinking: boolean;
|
|
123
139
|
};
|
|
124
140
|
/** One failing check slot's raw artifact, for the critic prompt. */
|
|
125
141
|
type CheckArtifact = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
slot: string;
|
|
143
|
+
path: string | null;
|
|
144
|
+
content: string;
|
|
145
|
+
truncated: boolean;
|
|
130
146
|
};
|
|
131
147
|
type CheckResult = {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
ran: boolean;
|
|
149
|
+
runner: CheckRunnerKind;
|
|
150
|
+
ok: boolean;
|
|
151
|
+
exit_code: number | null;
|
|
152
|
+
duration_ms: number;
|
|
153
|
+
failing_slots: string[];
|
|
154
|
+
detail: CheckArtifact[];
|
|
155
|
+
/** Raw checkride summary JSON (when the runner is checkride). */
|
|
156
|
+
summary?: unknown;
|
|
157
|
+
/** Captured combined output (when the runner is a shell command). */
|
|
158
|
+
log?: string;
|
|
143
159
|
};
|
|
144
160
|
/** How one path changed between the run's baseline and the builder's output.
|
|
145
161
|
* `renamed` covers git's rename/copy detection (the destination path is the one
|
|
@@ -150,175 +166,310 @@ type ChangeStatus = 'added' | 'modified' | 'deleted' | 'renamed' | 'unknown';
|
|
|
150
166
|
* passes. A gate edit is not misconduct by itself (some tasks are *about* the
|
|
151
167
|
* tests); it is the fact that makes a green check prove less. */
|
|
152
168
|
type ChangedFile = {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
path: string;
|
|
170
|
+
status: ChangeStatus;
|
|
171
|
+
gate: boolean;
|
|
156
172
|
};
|
|
157
173
|
/** What the builder changed since the run's baseline, as the harness sees it
|
|
158
174
|
* (`src/changes.ts`). Null in `LoopState` when the build root is not a git
|
|
159
175
|
* repository — change detection is evidence, never a precondition. */
|
|
160
176
|
type ChangeSet = {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
177
|
+
/** The commit the changes are measured against — the build root's HEAD before
|
|
178
|
+
* the first iteration. Null in a repository with no commits yet, where only
|
|
179
|
+
* untracked files can be reported. */
|
|
180
|
+
baseline: string | null;
|
|
181
|
+
/** The changed paths, sorted, capped at the collection limit. */
|
|
182
|
+
files: ChangedFile[];
|
|
183
|
+
/** Every gate-matching path — computed over the *full* list before the cap,
|
|
184
|
+
* so truncation can never hide a gate edit. */
|
|
185
|
+
gate_edits: string[];
|
|
186
|
+
/** How many paths changed in total, before the cap. */
|
|
187
|
+
total: number;
|
|
188
|
+
truncated: boolean;
|
|
173
189
|
};
|
|
174
190
|
/** Per-phase record for iteration summaries. */
|
|
175
191
|
type PhaseRecord = {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
192
|
+
provider: string;
|
|
193
|
+
model: string;
|
|
194
|
+
session_id: string | null;
|
|
195
|
+
/** Wall-clock of the phase's model work, the same measure for every provider
|
|
196
|
+
* so an all-local run and an all-Claude run compare directly. The builder's is
|
|
197
|
+
* fascicle's `GenerateResult.timing`: the whole call, every turn and the tools
|
|
198
|
+
* between them. The critic's spans its whole degradation ladder, failed
|
|
199
|
+
* attempts included, since no one result's timing covers the attempts before
|
|
200
|
+
* it. Neither counts the Ollama prewarm. */
|
|
201
|
+
duration_ms: number;
|
|
202
|
+
usage: UsageTotals;
|
|
203
|
+
cost_usd: number | null;
|
|
204
|
+
cost_source: CostSource;
|
|
205
|
+
/** How the phase's generate call ended. For a local builder,
|
|
206
|
+
* `'max_steps'` is a budget cutoff surfaced as a warning, not an error. */
|
|
207
|
+
finish_reason: FinishReason;
|
|
208
|
+
/** Tool calls fascicle executed in this phase, and how many were recovered
|
|
209
|
+
* from assistant text rather than returned structurally — the raw
|
|
210
|
+
* counts behind the salvage-rate health metric. */
|
|
211
|
+
tool_calls: number;
|
|
212
|
+
salvaged_tool_calls: number;
|
|
213
|
+
/** Provider-error retries that preceded this phase's successful call
|
|
214
|
+
* — the local-critic degradation ladder's first rung: a stochastic
|
|
215
|
+
* Ollama stream death is retried once before falling back. Set on the critic
|
|
216
|
+
* record (0 when the first call succeeded); absent on the builder, which is
|
|
217
|
+
* not retried this build. Additive — old summary consumers ignore it. */
|
|
218
|
+
retries?: number;
|
|
219
|
+
/** The `cause_kind` of the retried provider error, recorded with `retries`.
|
|
220
|
+
* Absent when no retry happened. */
|
|
221
|
+
retry_cause_kind?: CauseKind;
|
|
222
|
+
/** This local-critic verdict was rendered by the tool-less fallback rung:
|
|
223
|
+
* the tool-bearing critique kept dying on the provider's stream
|
|
224
|
+
* even after the bounded retry, or ran out of steps before its verdict, so the
|
|
225
|
+
* critic judged with no read access — grounded only by the criteria, the raw
|
|
226
|
+
* check artifacts, and a workspace file inventory. Set (true) on a degraded
|
|
227
|
+
* critic record; absent on a full critique and on the builder. Additive, and a
|
|
228
|
+
* degraded verdict is *always* marked so it never passes silently as a full
|
|
229
|
+
* one. */
|
|
230
|
+
critic_degraded?: boolean;
|
|
231
|
+
/** Why the verdict degraded, set with `critic_degraded`. Additive. */
|
|
232
|
+
degrade_cause?: DegradeCause;
|
|
233
|
+
/** Output tokens per second over the model's own turns (tool execution and
|
|
234
|
+
* retry backoff excluded), from fascicle's per-step timing. `basis` is
|
|
235
|
+
* `decode` when every turn streamed, `blended` when any round trip counted
|
|
236
|
+
* prefill and network too — always the case for `claude_cli`, whose rate
|
|
237
|
+
* fascicle derives from the CLI's reported API time. Absent when no turn
|
|
238
|
+
* carried timing. Additive.
|
|
239
|
+
*
|
|
240
|
+
* On Ollama, `decode` holds only while a turn's first chunk arrives as its
|
|
241
|
+
* first token is generated. Ollama sends a tool call whole, so a turn whose
|
|
242
|
+
* only output is a tool call dispatches nothing until generation ends, and
|
|
243
|
+
* its tokens are divided by the last few milliseconds: a non-thinking seat's
|
|
244
|
+
* rate reads far too high until robmclarty/fascicle#12 times those turns
|
|
245
|
+
* another way. A thinking seat needs ai-sdk-ollama 4.3.0 or later, which
|
|
246
|
+
* streams the reasoning even though volley never sets `think`. */
|
|
247
|
+
throughput?: Throughput;
|
|
221
248
|
};
|
|
222
249
|
type HaltReason = 'cost_cap' | 'gate_edit' | null;
|
|
223
250
|
/** Carry-state threaded through the fascicle loop. */
|
|
224
251
|
type LoopState = {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
252
|
+
iteration: number;
|
|
253
|
+
iteration_started_at: string;
|
|
254
|
+
feedback: string | null;
|
|
255
|
+
verdict: Verdict | null;
|
|
256
|
+
unmet_criteria: string[];
|
|
257
|
+
check: CheckResult | null;
|
|
258
|
+
/** What the builder changed this iteration, measured against the run's
|
|
259
|
+
* baseline (`src/changes.ts`). Null when the build root is not a git
|
|
260
|
+
* repository, and before the first `build` step of a run. */
|
|
261
|
+
changes: ChangeSet | null;
|
|
262
|
+
builder: PhaseRecord | null;
|
|
263
|
+
critic: PhaseRecord | null;
|
|
264
|
+
total_usage: UsageTotals;
|
|
265
|
+
total_cost_usd: number;
|
|
266
|
+
builder_cost_usd: number;
|
|
267
|
+
critic_cost_usd: number;
|
|
268
|
+
check_duration_ms: number;
|
|
269
|
+
iteration_cost_usd: number;
|
|
270
|
+
halt: HaltReason;
|
|
271
|
+
cost_warned: boolean;
|
|
245
272
|
};
|
|
246
273
|
/** Projection of final loop state; `.volley/summary.json` mirrors this. */
|
|
247
274
|
type RunResult = {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
275
|
+
run_id: string;
|
|
276
|
+
status: RunStatus;
|
|
277
|
+
started_at: string;
|
|
278
|
+
completed_at: string | null;
|
|
279
|
+
iterations_completed: number;
|
|
280
|
+
total_usage: UsageTotals;
|
|
281
|
+
total_cost_usd: number;
|
|
282
|
+
builder_cost_usd: number;
|
|
283
|
+
critic_cost_usd: number;
|
|
284
|
+
check_duration_ms: number;
|
|
285
|
+
final_verdict: Verdict | null;
|
|
286
|
+
/** The run branch teardown left behind because this run's work was never
|
|
287
|
+
* integrated: under `--worktree` without `--git` a converged run has no
|
|
288
|
+
* other copy of its effects, so teardown commits them onto `volley/<run id>`
|
|
289
|
+
* and keeps the branch instead of force-deleting it. Null whenever nothing
|
|
290
|
+
* survived — every non-worktree run, every integrated run, every discarded
|
|
291
|
+
* one. `--json` consumers read it here; `.volley/summary.json` mirrors it. */
|
|
292
|
+
salvaged_branch: string | null;
|
|
266
293
|
};
|
|
267
|
-
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/config.d.ts
|
|
268
296
|
/** `@path/to/file` reads the file; any other string passes through. */
|
|
269
|
-
declare function expand_at_file(value: string, cwd: string): string;
|
|
297
|
+
export declare function expand_at_file(value: string, cwd: string): string;
|
|
270
298
|
/** Load a `volley.config.ts` / `.js` file's default export. */
|
|
271
|
-
declare function load_config_file(path: string): Promise<VolleyConfig>;
|
|
299
|
+
export declare function load_config_file(path: string): Promise<VolleyConfig>;
|
|
272
300
|
type ResolveOptions = {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
301
|
+
cwd?: string;
|
|
302
|
+
run_id?: string;
|
|
303
|
+
started_at?: string;
|
|
304
|
+
env?: Record<string, string | undefined>;
|
|
305
|
+
/** What happens to a tool-loop builder with nothing between its commands
|
|
306
|
+
* and the host (`unguarded_builder`): the CLI `refuse`s it, the default,
|
|
307
|
+
* while under `volley acp` each of its commands `ask`s a person first, which
|
|
308
|
+
* takes the container's place. */
|
|
309
|
+
uncontained_builder?: 'refuse' | 'ask';
|
|
277
310
|
};
|
|
278
311
|
/** Merge, expand, and validate a raw `VolleyConfig` into a `ResolvedConfig`.
|
|
279
312
|
* `check_resolved` comes from check detection against the workspace, so the
|
|
280
313
|
* SDK and the CLI resolve `auto` and a command check the same way. */
|
|
281
|
-
declare function resolve_config(raw: VolleyConfig, options?: ResolveOptions): ResolvedConfig;
|
|
282
|
-
|
|
314
|
+
export declare function resolve_config(raw: VolleyConfig, options?: ResolveOptions): ResolvedConfig;
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/builder/tools.d.ts
|
|
317
|
+
/** The raw outcome of running one bash command, before the model-facing
|
|
318
|
+
* truncation + timeout marker the `bash` tool applies. `status` is the exit code
|
|
319
|
+
* (null when the process was killed by a signal — e.g. the timeout SIGKILL);
|
|
320
|
+
* `timed_out` marks the timeout path. */
|
|
321
|
+
type BashOutcome = {
|
|
322
|
+
status: number | null;
|
|
323
|
+
stdout: string;
|
|
324
|
+
stderr: string;
|
|
325
|
+
timed_out: boolean;
|
|
326
|
+
};
|
|
327
|
+
/** How the `bash` tool runs a command: the local `host_bash_executor`
|
|
328
|
+
* (`spawnSync`) — which under whole-process containment runs *inside*
|
|
329
|
+
* volley's own hardened container, no `docker exec` hop. The executor runs one
|
|
330
|
+
* command to completion and returns its raw outcome; the `bash` tool owns the
|
|
331
|
+
* truncation + timeout marker. Kept as an injectable seam so tests can supply a
|
|
332
|
+
* fake and a future shape could re-point it (the `docker exec` executor was
|
|
333
|
+
* retired). */
|
|
334
|
+
type BashExecutor = (command: string, options: {
|
|
335
|
+
timeout_ms: number;
|
|
336
|
+
max_capture_bytes: number;
|
|
337
|
+
}) => BashOutcome;
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/workspace_tools.d.ts
|
|
340
|
+
/** Which tool set a seat holds, so its prompt names the tools it has: volley's
|
|
341
|
+
* own (`'volley'`), or fascicle's workspace tools under `volley acp`
|
|
342
|
+
* (`'acp'`). */
|
|
343
|
+
type ToolSet = 'volley' | 'acp';
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/builder.d.ts
|
|
346
|
+
type BuilderDeps = {
|
|
347
|
+
engine: Engine;
|
|
348
|
+
config: ResolvedConfig;
|
|
349
|
+
on_chunk: (chunk: StreamChunk) => void;
|
|
350
|
+
/** The `bash` executor for a sandboxed local builder (`docker exec` against
|
|
351
|
+
* the run's container), or null to use the host `spawnSync` default (the
|
|
352
|
+
* unsandboxed escape hatch, or `claude_cli` which supplies no volley tools). */
|
|
353
|
+
bash_executor?: BashExecutor | null;
|
|
354
|
+
/** Tools to hand the builder in place of volley's own set; absent, it gets
|
|
355
|
+
* that set. A `claude_cli` builder brings its own tools and never takes
|
|
356
|
+
* these. */
|
|
357
|
+
tools?: ReadonlyArray<Tool>;
|
|
358
|
+
/** Which set `tools` is, so the prompt names the tools the builder holds;
|
|
359
|
+
* absent, volley's own. */
|
|
360
|
+
tool_set?: ToolSet;
|
|
361
|
+
/** Answers each call to a tool that asks first (`needs_approval`). Like
|
|
362
|
+
* `tools`, it reaches every builder but `claude_cli`. Absent, such a call
|
|
363
|
+
* fails closed. */
|
|
364
|
+
on_tool_approval?: ToolApprovalHandler;
|
|
365
|
+
};
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/critic/run.d.ts
|
|
368
|
+
export declare const verdict_schema: z.ZodObject<{
|
|
369
|
+
verdict: z.ZodEnum<{
|
|
370
|
+
approved: "approved";
|
|
371
|
+
changes_requested: "changes_requested";
|
|
372
|
+
}>;
|
|
373
|
+
feedback: z.ZodString;
|
|
374
|
+
unmet_criteria: z.ZodArray<z.ZodString>;
|
|
375
|
+
}, z.core.$strip>;
|
|
376
|
+
type VerdictOutput = z.infer<typeof verdict_schema>;
|
|
377
|
+
type CriticDeps = {
|
|
378
|
+
engine: Engine;
|
|
379
|
+
config: ResolvedConfig;
|
|
380
|
+
on_chunk: (chunk: StreamChunk) => void;
|
|
381
|
+
/** Tools to hand the critic's tool-bearing call in place of volley's reads;
|
|
382
|
+
* absent, it gets those. A `claude_cli` critic reads through the CLI's own
|
|
383
|
+
* tools and never takes these. The critic takes no approval handler, since
|
|
384
|
+
* nothing it reads with should ever ask. */
|
|
385
|
+
tools?: ReadonlyArray<Tool>;
|
|
386
|
+
/** Which set `tools` is, so the harness append names the reads the critic
|
|
387
|
+
* holds; absent, volley's own. */
|
|
388
|
+
tool_set?: ToolSet;
|
|
389
|
+
};
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/render/format.d.ts
|
|
283
392
|
type Role = 'builder' | 'critic' | 'check';
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
* Live output renderer: consumes `StreamChunk`s from each
|
|
287
|
-
* `generate` call plus volley's own phase/cost lines, and writes human
|
|
288
|
-
* progress to stderr. Streaming is a display concern only — the trajectory
|
|
289
|
-
* jsonl is the durable record.
|
|
290
|
-
*/
|
|
291
|
-
|
|
393
|
+
//#endregion
|
|
394
|
+
//#region src/render/renderer.d.ts
|
|
292
395
|
type RenderMode = 'default' | 'verbose' | 'quiet' | 'json';
|
|
293
396
|
type RendererOptions = {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
397
|
+
mode: RenderMode;
|
|
398
|
+
show_thinking: boolean;
|
|
399
|
+
color: boolean;
|
|
400
|
+
max_cost_usd: number | null;
|
|
401
|
+
/** Where the run's trajectory is written, named when verbose output is cut
|
|
402
|
+
* short (default: `.volley/trajectory.jsonl`). */
|
|
403
|
+
trajectory_path?: string;
|
|
404
|
+
write?: (text: string) => void;
|
|
299
405
|
};
|
|
300
406
|
type Renderer = {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
407
|
+
builder_chunk: (chunk: StreamChunk) => void;
|
|
408
|
+
critic_chunk: (chunk: StreamChunk) => void;
|
|
409
|
+
phase_start: (iteration: number, phase: Role) => void;
|
|
410
|
+
phase_end: (iteration: number, phase: Role, ok: boolean, detail?: string) => void;
|
|
411
|
+
cost_line: (iteration: number, phase: Role, phase_cost: number | null, run_total: number) => void;
|
|
412
|
+
info: (message: string) => void;
|
|
413
|
+
warn: (message: string) => void;
|
|
414
|
+
error: (message: string) => void;
|
|
415
|
+
final_summary: (result: RunResult) => void;
|
|
310
416
|
};
|
|
311
|
-
declare function create_renderer(options: RendererOptions): Renderer;
|
|
312
|
-
|
|
417
|
+
export declare function create_renderer(options: RendererOptions): Renderer;
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/phases.d.ts
|
|
420
|
+
/** How the check runs when the caller sets it, as `volley acp` does. Left
|
|
421
|
+
* out, the check inherits volley's environment and runs unasked, as on the
|
|
422
|
+
* CLI. */
|
|
423
|
+
type CheckGate = {
|
|
424
|
+
/** The whole environment the check runs with, in place of volley's own. */
|
|
425
|
+
env?: Readonly<Record<string, string>>;
|
|
426
|
+
/** Asked on the check's card before each check, the way a model call asks
|
|
427
|
+
* before a gated tool runs. Anything short of an allow keeps the check from
|
|
428
|
+
* running, and it's recorded as failed. */
|
|
429
|
+
on_approval?: ToolApprovalHandler;
|
|
430
|
+
};
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/flow.d.ts
|
|
433
|
+
/** The builder seat: the engine its calls go through, and, when the caller
|
|
434
|
+
* injects them, the tools that replace volley's own set, the name of that set
|
|
435
|
+
* (which its prompt names), and the handler that answers their approval
|
|
436
|
+
* requests. */
|
|
437
|
+
type BuilderSeat = Pick<BuilderDeps, 'engine' | 'tools' | 'tool_set' | 'on_tool_approval'>;
|
|
438
|
+
/** The critic seat: its engine and, when injected, the tools that replace its
|
|
439
|
+
* reads and the name of their set. It takes no approval handler, since its
|
|
440
|
+
* reads never ask. */
|
|
441
|
+
type CriticSeat = Pick<CriticDeps, 'engine' | 'tools' | 'tool_set'>;
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/orchestrator.d.ts
|
|
313
444
|
type OrchestratorDeps = {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
445
|
+
renderer: Renderer;
|
|
446
|
+
/** An engine to run both seats on in place of the ones built from the
|
|
447
|
+
* config. It serves both seats as-is, so the seat URLs then steer only
|
|
448
|
+
* volley's own requests (prewarm and the context probe), not model calls. */
|
|
449
|
+
engine?: Engine;
|
|
450
|
+
/** What to hand each seat in place of volley's own wiring: the builder's
|
|
451
|
+
* tools and the handler that answers their approval requests, and the
|
|
452
|
+
* critic's tools. A seat or a field left out keeps today's wiring, and a
|
|
453
|
+
* `claude_cli` seat, which brings its own tools, takes none of it. */
|
|
454
|
+
seats?: {
|
|
455
|
+
builder?: Omit<BuilderSeat, 'engine'>;
|
|
456
|
+
critic?: Omit<CriticSeat, 'engine'>;
|
|
457
|
+
};
|
|
458
|
+
/** A second sink for the run's trajectory, beside the run's
|
|
459
|
+
* `trajectory.jsonl`. With one, the flow runs through `run.stream`, so the
|
|
460
|
+
* sink also receives each model call's streamed chunks as `model_chunk`
|
|
461
|
+
* events, which the file never keeps. */
|
|
462
|
+
trajectory?: TrajectoryLogger;
|
|
463
|
+
/** The environment the check runs with and the approval it asks first, in
|
|
464
|
+
* place of inheriting volley's environment and running unasked. */
|
|
465
|
+
check?: CheckGate;
|
|
466
|
+
abort?: AbortSignal;
|
|
467
|
+
install_signal_handlers?: boolean;
|
|
318
468
|
};
|
|
319
|
-
declare function run_volley(config: ResolvedConfig, deps: OrchestratorDeps, resume_from?: LoopState | null): Promise<RunResult>;
|
|
320
|
-
|
|
321
|
-
|
|
469
|
+
export declare function run_volley(config: ResolvedConfig, deps: OrchestratorDeps, resume_from?: LoopState | null): Promise<RunResult>;
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/loop_state.d.ts
|
|
472
|
+
export declare function initial_state(): LoopState;
|
|
322
473
|
/** All stopping conditions live here: acceptance, cost cap, the gate
|
|
323
474
|
* edit refusal, and — implicitly via `max_rounds` — the iteration budget.
|
|
324
475
|
* Success wins when it and the cap land on the same iteration.
|
|
@@ -327,34 +478,19 @@ declare function initial_state(): LoopState;
|
|
|
327
478
|
* it: the run passed a check the builder had rewritten, so the pass is exactly
|
|
328
479
|
* what is in question. It stops the run instead of iterating, because a model
|
|
329
480
|
* that just edited the gate is not a promising candidate to be asked again. */
|
|
330
|
-
declare function gate(config: ResolvedConfig, state: LoopState): {
|
|
331
|
-
|
|
332
|
-
|
|
481
|
+
export declare function gate(config: ResolvedConfig, state: LoopState): {
|
|
482
|
+
stop: boolean;
|
|
483
|
+
state: LoopState;
|
|
333
484
|
};
|
|
334
|
-
declare function status_of(value: LoopState, converged: boolean): RunStatus;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
* Critic invocation: a read-only session (`claude_cli`, or a local model with
|
|
338
|
-
* volley's read tools) returning a schema-validated structured verdict. The
|
|
339
|
-
* harness — not the critic — writes `.volley/feedback.md` and `.volley/verdict`.
|
|
340
|
-
*/
|
|
341
|
-
|
|
342
|
-
declare const verdict_schema: z.ZodObject<{
|
|
343
|
-
verdict: z.ZodEnum<{
|
|
344
|
-
approved: "approved";
|
|
345
|
-
changes_requested: "changes_requested";
|
|
346
|
-
}>;
|
|
347
|
-
feedback: z.ZodString;
|
|
348
|
-
unmet_criteria: z.ZodArray<z.ZodString>;
|
|
349
|
-
}, z.core.$strip>;
|
|
350
|
-
type VerdictOutput = z.infer<typeof verdict_schema>;
|
|
351
|
-
|
|
485
|
+
export declare function status_of(value: LoopState, converged: boolean): RunStatus;
|
|
486
|
+
//#endregion
|
|
487
|
+
//#region src/sandbox.d.ts
|
|
352
488
|
/** Canonical identity of the allowlist bridge the examples create (`docker
|
|
353
489
|
* network create --subnet <SANDBOX_NETWORK_SUBNET> <SANDBOX_NETWORK_NAME>`) and
|
|
354
490
|
* the host `DOCKER-USER` rules scope to. Exported so the blessed examples
|
|
355
491
|
* share one spelling of the invocation spec. */
|
|
356
|
-
declare const SANDBOX_NETWORK_NAME = "volley-sandbox-net";
|
|
357
|
-
declare const SANDBOX_NETWORK_SUBNET = "172.31.99.0/24";
|
|
492
|
+
export declare const SANDBOX_NETWORK_NAME = "volley-sandbox-net";
|
|
493
|
+
export declare const SANDBOX_NETWORK_SUBNET = "172.31.99.0/24";
|
|
358
494
|
/**
|
|
359
495
|
* The container's egress posture. Under whole-process containment
|
|
360
496
|
* the model client and the `fetch` tool run inside the container, so its
|
|
@@ -390,7 +526,7 @@ type SandboxNetwork = 'none' | 'allowlist';
|
|
|
390
526
|
* collapses the allowlist targets onto the host gateway (`host.docker.internal`,
|
|
391
527
|
* the Linux-portable spelling — harmless where Docker Desktop already provides it).
|
|
392
528
|
*/
|
|
393
|
-
declare function network_run_args(network: SandboxNetwork, network_name: string): string[];
|
|
529
|
+
export declare function network_run_args(network: SandboxNetwork, network_name: string): string[];
|
|
394
530
|
/**
|
|
395
531
|
* The hardened `docker run` argv that launches volley inside its container.
|
|
396
532
|
* Pure and exported so the flag set is unit-testable without a
|
|
@@ -417,30 +553,32 @@ declare function network_run_args(network: SandboxNetwork, network_name: string)
|
|
|
417
553
|
* `--read-only` rootfs is an explicit tmpfs or volume; the bind-mounted worktree
|
|
418
554
|
* is the one host-visible write path.
|
|
419
555
|
*/
|
|
420
|
-
declare function sandbox_run_args(options: {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
556
|
+
export declare function sandbox_run_args(options: {
|
|
557
|
+
image: string;
|
|
558
|
+
build_root: string;
|
|
559
|
+
uid: number | null;
|
|
560
|
+
gid: number | null;
|
|
561
|
+
store_volume?: string | null;
|
|
562
|
+
network: SandboxNetwork;
|
|
563
|
+
network_name: string;
|
|
564
|
+
name?: string | null;
|
|
565
|
+
env?: ReadonlyArray<readonly [string, string]>;
|
|
566
|
+
labels?: ReadonlyArray<readonly [string, string]>;
|
|
567
|
+
entrypoint?: string | null;
|
|
568
|
+
command?: ReadonlyArray<string>;
|
|
433
569
|
}): string[];
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
declare const
|
|
437
|
-
declare const
|
|
438
|
-
declare const
|
|
439
|
-
declare const
|
|
440
|
-
declare const
|
|
441
|
-
declare const
|
|
442
|
-
declare const
|
|
443
|
-
declare
|
|
444
|
-
declare function
|
|
445
|
-
|
|
446
|
-
|
|
570
|
+
//#endregion
|
|
571
|
+
//#region src/exit_codes.d.ts
|
|
572
|
+
export declare const EXIT_SUCCESS = 0;
|
|
573
|
+
export declare const EXIT_BUDGET_EXHAUSTED = 2;
|
|
574
|
+
export declare const EXIT_BUILDER_ERROR = 3;
|
|
575
|
+
export declare const EXIT_CHECK_ERROR = 4;
|
|
576
|
+
export declare const EXIT_CONFIG_ERROR = 5;
|
|
577
|
+
export declare const EXIT_CRITIC_ERROR = 6;
|
|
578
|
+
export declare const EXIT_COST_CAP = 7;
|
|
579
|
+
export declare const EXIT_INTERRUPTED = 130;
|
|
580
|
+
export declare function exit_code_for_status(status: RunStatus): number;
|
|
581
|
+
export declare function exit_code_for_error(err: unknown): number;
|
|
582
|
+
//#endregion
|
|
583
|
+
export type { BuilderPermissionMode, CheckArtifact, CheckMode, CheckResult, CheckRunnerKind, CostSource, CriticPreset, CriticProvider, HaltReason, LoopState, PhaseRecord, RenderMode, Renderer, RendererOptions, ResolvedConfig, RunResult, RunStatus, SandboxNetwork, Verdict, VerdictOutput, VolleyConfig };
|
|
584
|
+
//# sourceMappingURL=index.d.ts.map
|