@robmclarty/volley 0.5.2 → 0.5.4
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 +22 -0
- package/README.md +34 -1
- package/dist/cli.js +35 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.js +35 -8
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.5.4 — 2026-09-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **A diagram of the loop, drawn from the code.** The README's new "The flow" section shows the real composition: the branch that skips verification once the builder alone crosses the cost cap, the critic's retry-then-tool-less fallback for local models, and the guard that stops the loop. fascicle's `describe.diagram` renders it from `src/flow.ts`, and `pnpm diagram` prints it from a checkout.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- **The trajectory's flow structure includes the builder and the critic.** They now appear as children of `build` and `critique` in the tree a run records at its start, so the fascicle viewer draws both model calls, and the critic's retry and fallback, before the run reaches them. Live output, verdicts, and exit codes are unchanged.
|
|
10
|
+
|
|
11
|
+
### Internal
|
|
12
|
+
- Built on fascicle 0.12.13. A test holds the `src/flow.ts` header diagram and the README's copy to `describe.diagram`, so neither can drift from the code.
|
|
13
|
+
|
|
14
|
+
## v0.5.3 — 2026-09-24
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Local phases report a true decode rate.** The `throughput` block for Ollama and LM Studio builder and critic phases was labeled `basis: 'decode'` but still counted prompt prefill, because fascicle started the clock on the stream's opening framing rather than the first token. Rates now exclude prefill and read higher than the ones v0.5.2 archived; `claude_cli` rates are unchanged.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **A local stream that dies before its first token is retried by the engine.** fascicle now treats that failure as retryable on the ai_sdk transport, as it already did on native, so some local-critic stream deaths are absorbed before volley's critic retry and tool-less fallback see them.
|
|
21
|
+
|
|
22
|
+
### Internal
|
|
23
|
+
- Built on fascicle 0.12.10.
|
|
24
|
+
|
|
3
25
|
## v0.5.2 — 2026-09-24
|
|
4
26
|
|
|
5
27
|
### Added
|
package/README.md
CHANGED
|
@@ -88,6 +88,38 @@ workspace filesystem and the critic's feedback. Swap the critic to change what
|
|
|
88
88
|
the loop optimizes for: `--critic reviewer` (default), `optimizer`,
|
|
89
89
|
`researcher`, or a path to your own system prompt.
|
|
90
90
|
|
|
91
|
+
### The flow
|
|
92
|
+
|
|
93
|
+
Under the hood the loop is one [fascicle](https://www.npmjs.com/package/fascicle)
|
|
94
|
+
composition in [`src/flow.ts`](./src/flow.ts), and fascicle's `describe.diagram`
|
|
95
|
+
draws it straight from the code. It shows what the sketch above leaves out: the
|
|
96
|
+
branch that skips verification once the builder alone crosses the cost cap, the
|
|
97
|
+
critic's retry-then-tool-less fallback for local models whose streams die, and
|
|
98
|
+
the guard that decides when the loop stops.
|
|
99
|
+
|
|
100
|
+
<!-- flow diagram: pnpm diagram -->
|
|
101
|
+
```text
|
|
102
|
+
volley loop: one round per iteration, up to max_iterations
|
|
103
|
+
├─ iteration sequence
|
|
104
|
+
│ ├─ build open the iteration, run the builder, measure the change
|
|
105
|
+
│ │ └─ builder model call: one agentic session in the build root
|
|
106
|
+
│ ├─ verify branch: did the build alone cross the cost cap?
|
|
107
|
+
│ │ ├─ then skip_verify record the check as skipped and call no critic
|
|
108
|
+
│ │ └─ else sequence
|
|
109
|
+
│ │ ├─ check the deterministic gate: checkride, a command, or none
|
|
110
|
+
│ │ └─ critique the read-only critic's verdict and feedback
|
|
111
|
+
│ │ └─ critic fallback: judge without tools if a local critic's stream keeps dying
|
|
112
|
+
│ │ ├─ retry one more try after a stream death, local providers only
|
|
113
|
+
│ │ │ └─ critic_tools model call with read-only workspace tools
|
|
114
|
+
│ │ └─ pipe mark the verdict degraded
|
|
115
|
+
│ │ └─ critic_toolless model call judging from the check output and a file list
|
|
116
|
+
│ └─ record archive the iteration and rewrite the run summary
|
|
117
|
+
└─ guard gate stop on approval over a green check, the cost cap, or a gate edit
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`pnpm diagram` prints the same tree from a checkout. A test holds this copy and
|
|
121
|
+
the one in the `src/flow.ts` header to what the code builds, so neither drifts.
|
|
122
|
+
|
|
91
123
|
## Install
|
|
92
124
|
|
|
93
125
|
Requires Node ≥ 24, pnpm, and the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
@@ -290,7 +322,7 @@ Providers and their setup:
|
|
|
290
322
|
Install the peer for your provider in the project running volley (e.g.
|
|
291
323
|
`pnpm add ai-sdk-ollama@^4`); fascicle loads it lazily only when the local
|
|
292
324
|
critic actually runs. The major matters: it must satisfy fascicle's declared
|
|
293
|
-
peer range (`^4` for fascicle 0.12.
|
|
325
|
+
peer range (`^4` for fascicle 0.12.13, which tracks the AI SDK v7 line; a bare
|
|
294
326
|
`pnpm add ai-sdk-ollama` installs v4, which is exactly what this major wants).
|
|
295
327
|
The Ollama base URL is the **server root** — `ai-sdk-ollama` adds the `/api`
|
|
296
328
|
prefix itself (volley strips a trailing `/api` from `VOLLEY_OLLAMA_URL` for
|
|
@@ -485,6 +517,7 @@ pnpm test # vitest
|
|
|
485
517
|
pnpm typecheck # tsc --noEmit
|
|
486
518
|
pnpm check # checkride — volley dogfoods its own definition of done
|
|
487
519
|
pnpm build # tsup → dist/
|
|
520
|
+
pnpm diagram # draw the flow from src/flow.ts with describe.diagram
|
|
488
521
|
```
|
|
489
522
|
|
|
490
523
|
Live smoke tests against the real `claude` CLI and real checkride are opt-in:
|
package/dist/cli.js
CHANGED
|
@@ -1804,6 +1804,7 @@ function make_builder_step(deps) {
|
|
|
1804
1804
|
return model_call({
|
|
1805
1805
|
engine: deps.engine,
|
|
1806
1806
|
id: "builder",
|
|
1807
|
+
description: "model call: one agentic session in the build root",
|
|
1807
1808
|
provider: config.builder_provider,
|
|
1808
1809
|
model: config.builder_model,
|
|
1809
1810
|
system: resolve_builder_system(config),
|
|
@@ -2045,11 +2046,16 @@ function make_critic_tools_step(deps) {
|
|
|
2045
2046
|
return model_call2({
|
|
2046
2047
|
...critic_call_config(deps),
|
|
2047
2048
|
id: "critic_tools",
|
|
2049
|
+
description: "model call with read-only workspace tools",
|
|
2048
2050
|
...critic_tool_options(deps.config)
|
|
2049
2051
|
});
|
|
2050
2052
|
}
|
|
2051
2053
|
function make_critic_toolless_step(deps) {
|
|
2052
|
-
return model_call2({
|
|
2054
|
+
return model_call2({
|
|
2055
|
+
...critic_call_config(deps),
|
|
2056
|
+
id: "critic_toolless",
|
|
2057
|
+
description: "model call judging from the check output and a file list"
|
|
2058
|
+
});
|
|
2053
2059
|
}
|
|
2054
2060
|
function critic_retryable(config) {
|
|
2055
2061
|
return (err) => is_retryable_critic_error(config, err);
|
|
@@ -2546,14 +2552,18 @@ function build_critic(deps) {
|
|
|
2546
2552
|
const retryable = critic_retryable(deps.config);
|
|
2547
2553
|
return fallback(
|
|
2548
2554
|
retry(make_critic_tools_step(deps), {
|
|
2555
|
+
description: "one more try after a stream death, local providers only",
|
|
2549
2556
|
max_attempts: MAX_CRITIC_RETRIES + 1,
|
|
2550
2557
|
backoff_ms: 0,
|
|
2551
2558
|
when: retryable,
|
|
2552
2559
|
project: with_retries
|
|
2553
2560
|
}),
|
|
2554
|
-
pipe(make_critic_toolless_step(deps), as_degraded
|
|
2561
|
+
pipe(make_critic_toolless_step(deps), as_degraded, {
|
|
2562
|
+
description: "mark the verdict degraded"
|
|
2563
|
+
}),
|
|
2555
2564
|
{
|
|
2556
2565
|
name: "critic",
|
|
2566
|
+
description: "judge without tools if a local critic's stream keeps dying",
|
|
2557
2567
|
when: retryable,
|
|
2558
2568
|
handoff: (prompt) => toolless_critic_prompt(deps.config, prompt),
|
|
2559
2569
|
project: with_fallback_cause
|
|
@@ -2569,13 +2579,27 @@ function build_flow(deps, resume_from) {
|
|
|
2569
2579
|
bash_executor: deps.bash_executor
|
|
2570
2580
|
});
|
|
2571
2581
|
const critic = build_critic({ engine: deps.engine, config, on_chunk: renderer.critic_chunk });
|
|
2572
|
-
const build = step("build", (s, ctx) => build_phase(deps, builder, s, ctx)
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
const
|
|
2582
|
+
const build = step("build", (s, ctx) => build_phase(deps, builder, s, ctx), {
|
|
2583
|
+
description: "open the iteration, run the builder, measure the change",
|
|
2584
|
+
arm: builder
|
|
2585
|
+
});
|
|
2586
|
+
const skip_verify = step("skip_verify", (s) => skip_verify_phase(deps, s), {
|
|
2587
|
+
description: "record the check as skipped and call no critic"
|
|
2588
|
+
});
|
|
2589
|
+
const check = step("check", (s, ctx) => check_phase(deps, s, ctx), {
|
|
2590
|
+
description: "the deterministic gate: checkride, a command, or none"
|
|
2591
|
+
});
|
|
2592
|
+
const critique = step(
|
|
2593
|
+
"critique",
|
|
2594
|
+
(s, ctx) => critique_phase(deps, critic, s, ctx),
|
|
2595
|
+
{ description: "the read-only critic's verdict and feedback", arm: critic }
|
|
2596
|
+
);
|
|
2597
|
+
const record = step("record", (s) => record_phase(deps, s), {
|
|
2598
|
+
description: "archive the iteration and rewrite the run summary"
|
|
2599
|
+
});
|
|
2577
2600
|
const verify = branch({
|
|
2578
2601
|
name: "verify",
|
|
2602
|
+
description: "did the build alone cross the cost cap?",
|
|
2579
2603
|
when: (s) => cost_cap_hit(config, s),
|
|
2580
2604
|
// fascicle names this arm `then`. It holds a Step object, never a function,
|
|
2581
2605
|
// so nothing can mistake the config for a thenable.
|
|
@@ -2588,9 +2612,12 @@ function build_flow(deps, resume_from) {
|
|
|
2588
2612
|
});
|
|
2589
2613
|
return loop({
|
|
2590
2614
|
name: "volley",
|
|
2615
|
+
description: "one round per iteration, up to max_iterations",
|
|
2591
2616
|
init: (input) => input.resume_from ?? initial_state(),
|
|
2592
2617
|
body: iteration,
|
|
2593
|
-
guard: step("gate", (s) => gate(config, s)
|
|
2618
|
+
guard: step("gate", (s) => gate(config, s), {
|
|
2619
|
+
description: "stop on approval over a green check, the cost cap, or a gate edit"
|
|
2620
|
+
}),
|
|
2594
2621
|
finish: (s, { converged }) => ({ value: s, converged }),
|
|
2595
2622
|
max_rounds: remaining_iterations(config, resume_from)
|
|
2596
2623
|
});
|