@robmclarty/volley 0.8.0 → 0.9.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/dist/index.d.ts CHANGED
@@ -32,7 +32,13 @@ type RunStatus = 'running' | 'success' | 'budget_exhausted' | 'cost_cap_reached'
32
32
  /** The builder edited the gate that judges it, under `--fail-on-gate-edit`:
33
33
  * the run halts on the spot rather than iterating, because a check the builder
34
34
  * can rewrite is not evidence the work is done. */
35
- 'gate_edit_blocked' | 'interrupted' | 'error';
35
+ 'gate_edit_blocked' |
36
+ /** A critique's other outcome: the critic requested changes, or the check
37
+ * failed, on the one change it reviewed. A run never ends this way. */
38
+ 'rejected' | 'interrupted' | 'error';
39
+ /** What a run does: the builder/critic loop (`run`), or one check and one
40
+ * critique of a change already made, with no builder at all (`critique`). */
41
+ type RunMode = 'run' | 'critique';
36
42
  /** User-facing configuration, before resolution (config file / flags). */
37
43
  type VolleyConfig = {
38
44
  prompt: string;
@@ -43,6 +49,7 @@ type VolleyConfig = {
43
49
  builder_provider?: BuilderProvider;
44
50
  builder_url?: string;
45
51
  builder_max_steps?: number;
52
+ builder_max_tool_calls?: number;
46
53
  allow_unsandboxed_builder?: boolean;
47
54
  critic_model?: string;
48
55
  critic_provider?: CriticProvider;
@@ -69,6 +76,13 @@ type ResolvedConfig = {
69
76
  version: 2;
70
77
  run_id: string;
71
78
  started_at: string;
79
+ /** A run, or a critique (`volley critique`). A critique's builder fields
80
+ * hold the defaults, untouched by flags and the environment, since no
81
+ * builder is ever constructed, and its loop settings are one round with
82
+ * nothing isolated or committed. */
83
+ mode: RunMode;
84
+ /** The task. A critique needs none, since a critic judges against the
85
+ * criteria alone, so it's empty unless one was given to record. */
72
86
  prompt: string;
73
87
  criteria: string;
74
88
  check: CheckMode;
@@ -84,6 +98,11 @@ type ResolvedConfig = {
84
98
  * lmstudio, openrouter) takes one. */
85
99
  builder_url: string | null;
86
100
  builder_max_steps: number;
101
+ /** How many tool calls a tool-loop builder may make in one turn; fascicle
102
+ * runs the first that many, in order, and drops the rest. 1 by default,
103
+ * since some model and runtime pairs return parallel calls badly; raise it
104
+ * for a pair that handles them. Ignored by the `claude_cli` builder. */
105
+ builder_max_tool_calls: number;
87
106
  /** Operator opt-out permitting a non-`claude_cli` builder to run without a
88
107
  * sandbox — it gets a real host bash. Refused by default. */
89
108
  allow_unsandboxed_builder: boolean;
@@ -127,9 +146,12 @@ type ResolvedConfig = {
127
146
  * `VOLLEY_SANDBOX_IMAGE` override it. Meaningful only for a local builder —
128
147
  * the `claude_cli` path never uses Docker. */
129
148
  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. */
149
+ /** The root that holds each run's state at `<state_dir>/<run_id>/`, as an
150
+ * absolute path: `--state-dir`, else the default root,
151
+ * `$XDG_STATE_HOME/volley` or `~/.local/state/volley`. `resolve_config`
152
+ * always sets one. Null is the layout older runs used, `<workspace>/.volley/`,
153
+ * rotated aside by the next fresh run: a run an older volley started
154
+ * resumes in it, and a hand-built config can still ask for it. */
133
155
  state_dir: string | null;
134
156
  workspace: string;
135
157
  verbose: boolean;
@@ -270,7 +292,7 @@ type LoopState = {
270
292
  halt: HaltReason;
271
293
  cost_warned: boolean;
272
294
  };
273
- /** Projection of final loop state; `.volley/summary.json` mirrors this. */
295
+ /** Projection of final loop state; the run's `summary.json` mirrors this. */
274
296
  type RunResult = {
275
297
  run_id: string;
276
298
  status: RunStatus;
@@ -288,8 +310,15 @@ type RunResult = {
288
310
  * other copy of its effects, so teardown commits them onto `volley/<run id>`
289
311
  * and keeps the branch instead of force-deleting it. Null whenever nothing
290
312
  * survived — every non-worktree run, every integrated run, every discarded
291
- * one. `--json` consumers read it here; `.volley/summary.json` mirrors it. */
313
+ * one. `--json` consumers read it here; the run's `summary.json` mirrors it. */
292
314
  salvaged_branch: string | null;
315
+ /** A run, or a critique, which runs no builder. */
316
+ mode: RunMode;
317
+ /** Where this run keeps its config, summary, trajectory, feedback, verdict,
318
+ * and iterations: `<state_dir>/<run_id>/` under a state root, else the
319
+ * workspace's `.volley/`. Inside the sandbox container it is the container's
320
+ * path, which a harness maps back to the directory it mounted. */
321
+ run_state_dir: string;
293
322
  };
294
323
  //#endregion
295
324
  //#region src/config.d.ts
@@ -307,6 +336,12 @@ type ResolveOptions = {
307
336
  * while under `volley acp` each of its commands `ask`s a person first, which
308
337
  * takes the container's place. */
309
338
  uncontained_builder?: 'refuse' | 'ask';
339
+ /** Where the run's state goes, in place of what the config says: `volley
340
+ * resume` passes the root it found the run under, or null for a run an older
341
+ * volley kept in the workspace's `.volley/`, which resumes there. */
342
+ state_dir?: string | null;
343
+ /** A run (the default), or a critique, which has no builder or loop. */
344
+ mode?: RunMode;
310
345
  };
311
346
  /** Merge, expand, and validate a raw `VolleyConfig` into a `ResolvedConfig`.
312
347
  * `check_resolved` comes from check detection against the workspace, so the
@@ -365,13 +400,19 @@ type BuilderDeps = {
365
400
  };
366
401
  //#endregion
367
402
  //#region src/critic/run.d.ts
403
+ /** A critic that holds tools isn't constrain-decoded, so nothing stops it
404
+ * answering null for the feedback or the unmet list, as GLM did on approving.
405
+ * Both fields allow null and read it as empty, so that answer is a verdict
406
+ * rather than a schema failure that ends the run. fascicle sends the schema's
407
+ * input side, so the model sees each field as its type or null, still
408
+ * required; `.describe()` comes last so the description stays on the field. */
368
409
  export declare const verdict_schema: z.ZodObject<{
369
410
  verdict: z.ZodEnum<{
370
411
  approved: "approved";
371
412
  changes_requested: "changes_requested";
372
413
  }>;
373
- feedback: z.ZodString;
374
- unmet_criteria: z.ZodArray<z.ZodString>;
414
+ feedback: z.ZodPipe<z.ZodNullable<z.ZodString>, z.ZodTransform<string, string | null>>;
415
+ unmet_criteria: z.ZodPipe<z.ZodNullable<z.ZodArray<z.ZodString>>, z.ZodTransform<string[], string[] | null>>;
375
416
  }, z.core.$strip>;
376
417
  type VerdictOutput = z.infer<typeof verdict_schema>;
377
418
  type CriticDeps = {
@@ -399,7 +440,7 @@ type RendererOptions = {
399
440
  color: boolean;
400
441
  max_cost_usd: number | null;
401
442
  /** Where the run's trajectory is written, named when verbose output is cut
402
- * short (default: `.volley/trajectory.jsonl`). */
443
+ * short (default: "the run's trajectory.jsonl"). */
403
444
  trajectory_path?: string;
404
445
  write?: (text: string) => void;
405
446
  };
@@ -467,6 +508,25 @@ type OrchestratorDeps = {
467
508
  install_signal_handlers?: boolean;
468
509
  };
469
510
  export declare function run_volley(config: ResolvedConfig, deps: OrchestratorDeps, resume_from?: LoopState | null): Promise<RunResult>;
511
+ /** What a critique runs with: what a run does, less the builder's seat, plus
512
+ * the revision its change is measured from. */
513
+ type CritiqueDeps = Omit<OrchestratorDeps, 'seats'> & {
514
+ /** The revision the change under review is measured from (`--baseline`):
515
+ * everything that differs from it, committed or not, untracked files
516
+ * included. HEAD when absent, so the uncommitted edits are the change. */
517
+ baseline?: string;
518
+ /** The critic's tools in place of volley's reads, as for a run's critic. */
519
+ critic?: Omit<CriticSeat, 'engine'>;
520
+ };
521
+ /**
522
+ * `volley critique`: the check and the critic, once, over a change already
523
+ * made. No builder is constructed, and the engine serves the critic's seat
524
+ * alone, so a critic can be measured on fixed, labeled changes without paying
525
+ * for builder turns. It writes the state a one-round run does, with the mode
526
+ * recorded, and ends `success` on approval over a green check, else
527
+ * `rejected`.
528
+ */
529
+ export declare function run_critique(config: ResolvedConfig, deps: CritiqueDeps): Promise<RunResult>;
470
530
  //#endregion
471
531
  //#region src/loop_state.d.ts
472
532
  export declare function initial_state(): LoopState;
@@ -485,6 +545,13 @@ export declare function gate(config: ResolvedConfig, state: LoopState): {
485
545
  export declare function status_of(value: LoopState, converged: boolean): RunStatus;
486
546
  //#endregion
487
547
  //#region src/sandbox.d.ts
548
+ /** Where volley inside the container keeps each run's state when no
549
+ * `--state-dir` names a root: the default root under the pinned `HOME`, since
550
+ * the image sets no `XDG_STATE_HOME`. `sandbox_run_args`'s `run_state` binds one
551
+ * run's host directory at that run's directory under it, so the state outlives
552
+ * the `--rm` container. Exported so a harness can map a contained run's
553
+ * `run_state_dir` back to the directory it mounted. */
554
+ export declare const SANDBOX_STATE_ROOT: string;
488
555
  /** Canonical identity of the allowlist bridge the examples create (`docker
489
556
  * network create --subnet <SANDBOX_NETWORK_SUBNET> <SANDBOX_NETWORK_NAME>`) and
490
557
  * the host `DOCKER-USER` rules scope to. Exported so the blessed examples
@@ -530,7 +597,7 @@ export declare function network_run_args(network: SandboxNetwork, network_name:
530
597
  /**
531
598
  * The hardened `docker run` argv that launches volley inside its container.
532
599
  * Pure and exported so the flag set is unit-testable without a
533
- * daemon and the blessed examples / `--dry-run` preflight can render it.
600
+ * daemon and the blessed examples and a parent harness can render it.
534
601
  *
535
602
  * A one-shot, foreground `--rm` container — the whole run *is* this
536
603
  * container (an earlier design ran a detached `-d … sleep infinity` daemon volley
@@ -542,6 +609,25 @@ export declare function network_run_args(network: SandboxNetwork, network_name:
542
609
  * the image entrypoint (the opt-in real-docker isolation test runs a raw `sh`
543
610
  * this way).
544
611
  *
612
+ * `run_state` binds the run's own state directory on the host,
613
+ * `<root>/<run_id>/`, at `<SANDBOX_STATE_ROOT>/<run_id>/`, where volley inside
614
+ * keeps that run's state when no `--state-dir` names a root, so it outlives the
615
+ * container at the path a run on the host would use. Only the one directory
616
+ * is mounted, never the root: the builder inside runs arbitrary commands, so
617
+ * it can rewrite its own run's state but can't read another run's trajectory
618
+ * or plant a check in another run's config. Its own run's state is still the
619
+ * builder's to rewrite, config.json included, so a host-side resume refuses
620
+ * that config, which volley signs only outside the sandbox (`./trust.ts`), and
621
+ * a directory holding a symlink: resume a contained run inside the container,
622
+ * with the same mount. Create the
623
+ * directory on the host first, empty (Docker on Linux makes a missing bind
624
+ * source owned by root, which the container's user can't write), and pass the
625
+ * same id to volley as `--run-id`. Keep it outside `build_root`, so the check
626
+ * never reads it, and pass no `XDG_STATE_HOME` in `env`, which would move the
627
+ * root off the mount. Omit it and no state is mounted: a contained run keeping
628
+ * its state under the default root then stops before spending (exit 5),
629
+ * since the read-only root filesystem leaves it nowhere to write.
630
+ *
545
631
  * `store_volume` mounts the named volume as the pnpm store; omit it and no store
546
632
  * is mounted. A store is shared by every run that mounts it, so a caller that
547
633
  * installs dependencies in a separate container first can keep the builder's
@@ -551,13 +637,17 @@ export declare function network_run_args(network: SandboxNetwork, network_name:
551
637
  * Never emits `--privileged` and refuses `--user 0`: volley must not hand
552
638
  * the sandbox root or full capabilities. Everything writable under the
553
639
  * `--read-only` rootfs is an explicit tmpfs or volume; the bind-mounted worktree
554
- * is the one host-visible write path.
640
+ * and, when one is named, the state root are the host-visible write paths.
555
641
  */
556
642
  export declare function sandbox_run_args(options: {
557
643
  image: string;
558
644
  build_root: string;
559
645
  uid: number | null;
560
646
  gid: number | null;
647
+ run_state?: {
648
+ root: string;
649
+ run_id: string;
650
+ } | null;
561
651
  store_volume?: string | null;
562
652
  network: SandboxNetwork;
563
653
  network_name: string;
@@ -568,6 +658,13 @@ export declare function sandbox_run_args(options: {
568
658
  command?: ReadonlyArray<string>;
569
659
  }): string[];
570
660
  //#endregion
661
+ //#region src/workspace.d.ts
662
+ /** The root each run's state sits under when `--state-dir` names none:
663
+ * `$XDG_STATE_HOME/volley`, else `<home>/.local/state/volley`. A relative
664
+ * `XDG_STATE_HOME` is ignored, as the XDG spec says. `home` is the host's by
665
+ * default; the sandbox passes its container's to spell the same root inside. */
666
+ export declare function default_state_root(env: Readonly<Record<string, string | undefined>>, home?: string): string;
667
+ //#endregion
571
668
  //#region src/exit_codes.d.ts
572
669
  export declare const EXIT_SUCCESS = 0;
573
670
  export declare const EXIT_BUDGET_EXHAUSTED = 2;
@@ -580,5 +677,5 @@ export declare const EXIT_INTERRUPTED = 130;
580
677
  export declare function exit_code_for_status(status: RunStatus): number;
581
678
  export declare function exit_code_for_error(err: unknown): number;
582
679
  //#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 };
680
+ export type { BuilderPermissionMode, CheckArtifact, CheckMode, CheckResult, CheckRunnerKind, CostSource, CriticPreset, CriticProvider, CritiqueDeps, HaltReason, LoopState, PhaseRecord, RenderMode, Renderer, RendererOptions, ResolvedConfig, RunMode, RunResult, RunStatus, SandboxNetwork, Verdict, VerdictOutput, VolleyConfig };
584
681
  //# sourceMappingURL=index.d.ts.map