@odla-ai/harness 0.1.2 → 0.2.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.
Files changed (34) hide show
  1. package/dist/{chunk-QTUEF2HZ.js → chunk-3QP4VDQS.js} +1 -1
  2. package/dist/{chunk-QTUEF2HZ.js.map → chunk-3QP4VDQS.js.map} +1 -1
  3. package/dist/{chunk-GMVZ4LZH.js → chunk-5FFR7U4L.js} +1173 -374
  4. package/dist/chunk-5FFR7U4L.js.map +1 -0
  5. package/dist/{chunk-GE6CCN7W.js → chunk-C5VQI2IF.js} +2 -2
  6. package/dist/{chunk-PHXQH4YM.js → chunk-GKDKIU4P.js} +4 -3
  7. package/dist/{chunk-ATKV6VTU.js → chunk-KD7IN3NJ.js} +4 -4
  8. package/dist/cli.cjs.map +1 -1
  9. package/dist/cli.js +4 -4
  10. package/dist/code-runtime-cli.cjs +1233 -680
  11. package/dist/code-runtime-cli.cjs.map +1 -1
  12. package/dist/code-runtime-cli.js +5 -6
  13. package/dist/code-runtime-cli.js.map +1 -1
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +2 -2
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.js +2 -2
  18. package/dist/node.cjs +1530 -443
  19. package/dist/node.cjs.map +1 -1
  20. package/dist/node.d.cts +591 -10
  21. package/dist/node.d.ts +591 -10
  22. package/dist/node.js +304 -6
  23. package/dist/node.js.map +1 -1
  24. package/dist/testing.cjs.map +1 -1
  25. package/dist/testing.d.cts +1 -1
  26. package/dist/testing.d.ts +1 -1
  27. package/dist/testing.js +1 -1
  28. package/dist/{types-D12vK3K9.d.cts → types-0_H9TKkO.d.cts} +1 -1
  29. package/dist/{types-D12vK3K9.d.ts → types-0_H9TKkO.d.ts} +1 -1
  30. package/package.json +6 -12
  31. package/dist/chunk-GMVZ4LZH.js.map +0 -1
  32. /package/dist/{chunk-GE6CCN7W.js.map → chunk-C5VQI2IF.js.map} +0 -0
  33. /package/dist/{chunk-PHXQH4YM.js.map → chunk-GKDKIU4P.js.map} +0 -0
  34. /package/dist/{chunk-ATKV6VTU.js.map → chunk-KD7IN3NJ.js.map} +0 -0
package/dist/node.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { r as HarnessTaskSpec, g as HarnessAgentOutput, f as HarnessAgentInput, H as HarnessControlPlane, u as HarnessToolBroker, a as HarnessLease, d as HarnessInferenceRequest, e as HarnessInferenceResponse, i as CodeSessionEventData, w as HarnessToolRequest, v as HarnessToolName } from './types-D12vK3K9.js';
1
+ import { r as HarnessTaskSpec, g as HarnessAgentOutput, f as HarnessAgentInput, H as HarnessControlPlane, u as HarnessToolBroker, a as HarnessLease, d as HarnessInferenceRequest, e as HarnessInferenceResponse, i as CodeSessionEventData, v as HarnessToolName, w as HarnessToolRequest } from './types-0_H9TKkO.js';
2
2
  import { CodePortableCheckpoint, CodeSourceFile, CodeVerificationReceipt, CodeCheckpointState } from '@odla-ai/camel/code';
3
+ import { Skill, Inference, AgentRunBudget, AgentRun, CompactionPolicy } from '@odla-ai/ai';
3
4
  import { PolicyOutcome } from '@odla-ai/camel/policy';
4
- import '@odla-ai/ai';
5
+ import { PartitionVerdict } from '@odla-ai/graph';
5
6
 
6
7
  /** Supported command-line container engines for isolated harness attempts. */
7
8
  type ContainerEngine = "container" | "podman" | "docker";
@@ -181,7 +182,6 @@ interface CodeRuntimeCapabilities {
181
182
  source?: CodeLocalSourceDescriptor;
182
183
  images?: {
183
184
  ready: boolean;
184
- pi: string;
185
185
  recipes: Array<{
186
186
  id: string;
187
187
  image: string;
@@ -210,7 +210,7 @@ interface CodeRuntimeSnapshot {
210
210
  commands: CodeRuntimeCommand[];
211
211
  }
212
212
  /** Commands accepted by the outbound Code runtime. */
213
- type CodeRuntimeCommandKind = "start" | "prompt" | "checkpoint_stop" | "resume";
213
+ type CodeRuntimeCommandKind = "start" | "prompt" | "checkpoint_stop" | "resume" | "pursue";
214
214
  /** One sequenced command fenced to an exact host-binding generation. */
215
215
  interface CodeRuntimeCommand {
216
216
  commandId: string;
@@ -313,6 +313,79 @@ interface FrozenCodeLocalSource {
313
313
  sourceDir: string;
314
314
  }
315
315
 
316
+ /** v1's system prompt, verbatim. Do not edit without re-baselining the bench. */
317
+ declare const V1_SYSTEM_PROMPT = "You are Pi, the coding agent inside an odla Code harness.\nUse only the odla_read, odla_apply_git_diff, and odla_run_recipe tools.\nFor mutations, call odla_apply_git_diff with raw git diff text. It must start\nwith \"diff --git a/<path> b/<path>\", include matching \"---\" and \"+++\" file\nheaders and numbered \"@@\" hunks, and never use \"*** Begin Patch\" wrappers.\nThe workspace, model, and tool effects are controlled by the host broker.\nNever claim a build or test passed unless odla_run_recipe returned that result.";
318
+ /** The v2 prompt. v1's said "Use only the odla_read, odla_apply_git_diff, and
319
+ * odla_run_recipe tools", so leaving it in place would have told the model not
320
+ * to touch the tools this milestone exists to test. */
321
+ declare const V2_SYSTEM_PROMPT = "You are the coding agent inside an odla Code harness.\nStart by orienting: odla_list shows the files in the workspace and odla_search\nfinds a literal string across them. Prefer those over guessing a path.\nThen odla_read a bounded range, and odla_apply_git_diff to mutate.\nFor mutations, call odla_apply_git_diff with raw git diff text. It must start\nwith \"diff --git a/<path> b/<path>\", include matching \"---\" and \"+++\" file\nheaders and numbered \"@@\" hunks, and never use \"*** Begin Patch\" wrappers.\nThe workspace, model, and tool effects are controlled by the host broker.\nNever claim a build or test passed unless odla_run_recipe returned that result.";
322
+ /** Which tool surface the agent sees. `v1` reproduces the Pi container's exact
323
+ * three tools so the recorded baseline stays comparable. */
324
+ type CodeSurface = "v1" | "v2" | "v3";
325
+ /** v3 leads with orientation, because that is where the tokens went. */
326
+ declare const V3_SYSTEM_PROMPT = "You are the coding agent inside an odla Code harness.\n\nOrient before you look. odla_overview gives the directory shape of the whole\nrepository in a few hundred lines; odla_where_is finds where a symbol is defined,\ndisambiguated by package; odla_who_imports finds what depends on a file; and\nodla_who_touches finds the code that reads and writes a table or database\nnamespace, which is how a bug report about wrong data becomes a file path.\nPrefer these over listing the tree \u2014 a full listing of a real repository is tens\nof thousands of tokens and you will carry it for the rest of the session.\n\nThen odla_search for a literal string, odla_read for a bounded range, and\nodla_apply_git_diff to change something. A patch must start with\n\"diff --git a/<path> b/<path>\", include matching \"---\" and \"+++\" headers and\nnumbered \"@@\" hunks with at least one line of surrounding context, and must never\nuse \"*** Begin Patch\" wrappers.\n\nThe workspace, model, and tool effects are controlled by the host broker.\nNever claim a build or test passed unless odla_run_recipe returned that result.";
327
+ /**
328
+ * The system prompt for each tool surface, keyed by version.
329
+ *
330
+ * Kept addressable so a benchmark can hold everything else fixed and vary only
331
+ * the prompt — v1 is byte-identical to what the retired Pi agent shipped, which
332
+ * is what makes the comparison against v2 and v3 meaningful.
333
+ */
334
+ declare const SYSTEM_PROMPT_FOR: Record<CodeSurface, string>;
335
+ /** Which tool surface the skill describes, and the recipes it may run. */
336
+ interface CodeSkillOpts {
337
+ broker: HarnessToolBroker;
338
+ lease: HarnessLease;
339
+ workspaceDir: string;
340
+ /** Defaults to v1 so an unqualified run reproduces the baseline. */
341
+ surface?: CodeSurface;
342
+ /** Records every brokered call so the report can attribute errors per tool. */
343
+ onToolCall?(call: {
344
+ tool: HarnessToolName;
345
+ ok: boolean;
346
+ durationMs: number;
347
+ }): void;
348
+ }
349
+ /**
350
+ * The three v1 tools, brokered. Each handler is a thin adapter: it forwards the
351
+ * model's arguments to the broker unchanged and maps the bounded
352
+ * `HarnessToolResponse` onto the agent loop's `ToolOutput`, so a policy denial
353
+ * or a failed patch reaches the model as a tool error rather than an exception.
354
+ */
355
+ declare function codeSkill(opts: CodeSkillOpts): Skill;
356
+
357
+ /** The workspace, tools, and budget for a single attempt inside the runtime. */
358
+ interface CodeAgentAttemptOptions {
359
+ inference: Inference;
360
+ broker: HarnessToolBroker;
361
+ lease: HarnessLease;
362
+ workspaceDir: string;
363
+ prompt: string;
364
+ surface?: CodeSurface;
365
+ maxSteps?: number;
366
+ /** Run-wide ceiling enforced by runAgent against incremental usage. */
367
+ budget?: AgentRunBudget;
368
+ signal?: AbortSignal;
369
+ /** Emitted per brokered call so the engine can report tool activity. */
370
+ onToolCall?(call: {
371
+ tool: HarnessToolName;
372
+ ok: boolean;
373
+ durationMs: number;
374
+ }): void;
375
+ }
376
+ /** Whether one attempt completed, and the text it ended on. */
377
+ interface CodeAgentAttemptResult {
378
+ status: "completed" | "failed";
379
+ /** The agent's closing text, surfaced to the owner as its final message. */
380
+ finalText: string;
381
+ /** Present only when the attempt failed. */
382
+ error?: string;
383
+ /** Why the loop stopped, for diagnostics. */
384
+ stoppedReason?: string;
385
+ }
386
+ /** Run one attempt with runAgent over the brokered surface. */
387
+ declare function runCodeAgentAttempt(options: CodeAgentAttemptOptions): Promise<CodeAgentAttemptResult>;
388
+
316
389
  /** One control-owned regular file expected from a clean build recipe. */
317
390
  interface CodeExpectedArtifact {
318
391
  id: string;
@@ -380,13 +453,11 @@ interface CodeToolBrokerOptions {
380
453
  interface CodePiRuntimeEngineOptions {
381
454
  control: CodeRuntimeAgentControlPlane;
382
455
  engine: ContainerEngine;
383
- image: string;
384
- /** The Code CLI may build its bundled, content-addressed Pi adapter locally. */
385
- imageAuthorization?: "digest" | "cli_embedded";
386
456
  recipes: readonly CodeBuildRecipe[];
387
457
  recipeAuthorization?: "registered_recipe" | "exact_approval";
388
458
  limits?: ContainerLimits;
389
- runAttempt?: (options: ContainerRunOptions) => Promise<ContainerRunResult>;
459
+ /** Injectable agent attempt. Defaults to runAgent over the brokered surface. */
460
+ runAgentAttempt?: (options: CodeAgentAttemptOptions) => Promise<CodeAgentAttemptResult>;
390
461
  recipeExecutor?: CodeRecipeExecutor;
391
462
  /** Owner-visible and terminal-visible bounded runtime failures. */
392
463
  onDiagnostic?: (message: string) => void;
@@ -457,7 +528,7 @@ declare function prepareRuntimeCheckpoint(input: PrepareRuntimeCheckpointInput):
457
528
  /** Active isolated workspace state required to prepare and publish a Code checkpoint candidate. */
458
529
  interface RuntimeCheckpointSession {
459
530
  workspace: StagedWorkspace;
460
- done: Promise<ContainerRunResult | null>;
531
+ done: Promise<CodeAgentAttemptResult | null>;
461
532
  abort: AbortController;
462
533
  baseCommitSha: string;
463
534
  trustedBaseDigest: `sha256:${string}`;
@@ -481,6 +552,18 @@ declare class CodeRuntimeCheckpointManager {
481
552
  acknowledged(command: CodeRuntimeCommand, result: CodeRuntimeCommandResult): Promise<boolean>;
482
553
  }
483
554
 
555
+ interface CodeCommandMetadata {
556
+ role: "coding" | "review";
557
+ title: string;
558
+ prompt: string;
559
+ maxTokensPerInteraction: number;
560
+ planningInputDigest: `sha256:${string}` | null;
561
+ attestationDigest: string;
562
+ repository: string;
563
+ baseCommitSha: string;
564
+ sourceTreeDigest: `sha256:${string}`;
565
+ }
566
+
484
567
  /** Disposable local source tree materialized from an exact, validated Code snapshot. */
485
568
  interface MaterializedCodeSource {
486
569
  sourceDir: string;
@@ -492,10 +575,508 @@ declare function materializeCodeRuntimeSource(snapshot: CodeRuntimeSourceSnapsho
492
575
  * checkout. They remain outside the candidate diff and the tool broker denies
493
576
  * every write beneath the reference root. */
494
577
  declare function attachCodeRuntimeReferences(workspace: StagedWorkspace, references: NonNullable<CodeRuntimeSourceSnapshot["references"]>): Promise<void>;
578
+ /**
579
+ * Obtain the staged workspace one start/resume command should run in.
580
+ *
581
+ * Extracted from the engine because it is a different job: bringing a
582
+ * workspace into being from either a terminal's frozen checkout or the
583
+ * control plane's attested GitHub snapshot, with the checks that each source
584
+ * demands. The engine's remaining concern is what to RUN in it.
585
+ */
586
+ declare function materializeCommandWorkspace(input: {
587
+ command: CodeRuntimeCommand;
588
+ metadata: CodeCommandMetadata;
589
+ resume: boolean;
590
+ control: Pick<CodeRuntimeAgentControlPlane, "source">;
591
+ localSource?: FrozenCodeLocalSource;
592
+ }): Promise<{
593
+ workspace: StagedWorkspace;
594
+ sourceDigest: `sha256:${string}`;
595
+ localTrustedBaseDigest?: `sha256:${string}`;
596
+ requestedLocal: CodeLocalSourceDescriptor | null;
597
+ }>;
495
598
 
496
599
  /** Create the trusted CaMeL boundary for all Pi filesystem and build effects. */
497
600
  declare function createCodeToolBroker(options: CodeToolBrokerOptions): HarnessToolBroker;
498
601
 
602
+ /** One brokered tool call, recorded for per-tool error attribution. */
603
+ interface CodeToolCallRecord {
604
+ tool: HarnessToolName;
605
+ ok: boolean;
606
+ durationMs: number;
607
+ }
608
+ /** One coding run: which tool surface, which model, and what it may spend. */
609
+ interface RunCodeAgentOptions {
610
+ inference: Inference;
611
+ broker: HarnessToolBroker;
612
+ lease: HarnessLease;
613
+ workspaceDir: string;
614
+ /** The owner task. Becomes the single user turn that opens the run. */
615
+ prompt: string;
616
+ model: string;
617
+ /** Model turns before the loop stops. v1 had no equivalent bound. */
618
+ maxSteps?: number;
619
+ /** Run-wide token and tool-call ceilings, enforced by runAgent. */
620
+ budget?: AgentRunBudget;
621
+ /**
622
+ * Shorten the conversation when it gets expensive. Defaults to keeping the
623
+ * task and the three most recent tool exchanges once a turn bills more than
624
+ * 120k input tokens; pass `null` to keep the whole history.
625
+ *
626
+ * A default rather than opt-in because the failure it prevents is silent:
627
+ * without it a long run simply costs more each turn until the model's context
628
+ * ends the run, and nothing in the trace says that is what happened.
629
+ */
630
+ compaction?: CompactionPolicy | null;
631
+ /** Tool surface the agent sees. Defaults to v1, the recorded baseline. */
632
+ surface?: CodeSurface;
633
+ /** Overrides the surface's system prompt. Leave unset to keep runs comparable. */
634
+ system?: string;
635
+ maxTokens?: number;
636
+ signal?: AbortSignal;
637
+ deadline?: number;
638
+ /** Observe each brokered call as it completes, for live engine events. */
639
+ onToolCall?(call: CodeToolCallRecord): void;
640
+ /**
641
+ * Extra skills composed alongside the code tools — PM, chat, anything else
642
+ * the host can authorize.
643
+ *
644
+ * They arrive as an argument rather than being built here on purpose: those
645
+ * skills need a database credential scoped to a tenant, which the host has
646
+ * and the harness must not. It also keeps @odla-ai/pm and @odla-ai/chat out
647
+ * of this package's install graph for a capability not every consumer wants.
648
+ */
649
+ extraSkills?: Skill[];
650
+ }
651
+ /** A finished run — its final text, why it stopped, and what it cost. */
652
+ interface CodeAgentRun {
653
+ run: AgentRun;
654
+ toolCalls: CodeToolCallRecord[];
655
+ }
656
+ /**
657
+ * Drive one Code attempt to completion and return the agent run plus the
658
+ * brokered tool calls it made.
659
+ *
660
+ * `budget` is enforced by runAgent against *incremental* usage per turn, which
661
+ * is the fix for v1's per-interaction accounting: v1 summed `inputTokens +
662
+ * outputTokens` from every response into one counter, so re-sent context was
663
+ * charged again on each turn and a 32k allowance died in two turns.
664
+ */
665
+ declare function runCodeAgent(options: RunCodeAgentOptions): Promise<CodeAgentRun>;
666
+
667
+ interface CodeInteractionBudgetState {
668
+ tokens: number;
669
+ noticeEmitted: boolean;
670
+ }
671
+
672
+ /** How a runtime attempt reaches a model: through the control plane, never direct. */
673
+ interface CodeRuntimeInferenceOptions {
674
+ command: CodeRuntimeCommand;
675
+ metadata: CodeCommandMetadata;
676
+ state: CodeInteractionBudgetState;
677
+ control: CodeRuntimeAgentControlPlane;
678
+ event(value: CodeSessionEventData): Promise<void>;
679
+ }
680
+ /**
681
+ * Build the brokered `Inference` for one attempt.
682
+ *
683
+ * `catalog` is deliberately empty. The runtime does not know what the platform
684
+ * policy resolved the model to, and the trace's cost contract is that unknown
685
+ * pricing is absent rather than zero — a run that silently reported $0 would
686
+ * make the brokered route look free next to a priced one.
687
+ */
688
+ declare function createCodeRuntimeInference(options: CodeRuntimeInferenceOptions): Inference;
689
+
690
+ /** Everything the runner learns from one attempt. */
691
+ interface GoalAttemptOutcome {
692
+ /** Did the goal's proof pass on the resulting tree? */
693
+ gatePassed: boolean;
694
+ /** What the gate said, fed back as the next instruction when it failed. */
695
+ feedback: string;
696
+ /** Tokens this attempt consumed, for the budget. */
697
+ tokens: number;
698
+ /** USD this attempt cost, when known. Unknown is not zero. */
699
+ costUsd?: number;
700
+ /** Set when the attempt could not run at all, as opposed to running and failing. */
701
+ error?: string;
702
+ /** Model turns taken. Used to rank racers; optional elsewhere. */
703
+ steps?: number;
704
+ /** Size of the candidate patch produced. Used to break a tie between racers. */
705
+ patchBytes?: number;
706
+ }
707
+ /** What one attempt is told: which try this is, and what to do. */
708
+ interface GoalAttemptInput {
709
+ /** 1-based attempt number. */
710
+ attempt: number;
711
+ /** What to tell the agent this time. */
712
+ prompt: string;
713
+ signal?: AbortSignal;
714
+ }
715
+ /** Run one attempt and report what the gate made of it. */
716
+ type GoalAttempt = (input: GoalAttemptInput) => Promise<GoalAttemptOutcome>;
717
+ /** Bounds on autonomous pursuit. Every one of them is a stop, not a suggestion. */
718
+ interface GoalBudget {
719
+ /** Hard cap on attempts. Required — an unbounded loop is not a budget. */
720
+ maxAttempts: number;
721
+ maxTokens?: number;
722
+ maxUsd?: number;
723
+ /** Absolute epoch milliseconds. */
724
+ deadline?: number;
725
+ }
726
+ /** What the runner reports as it goes, so a board can follow from evidence. */
727
+ type GoalEvent = {
728
+ type: "attempt_started";
729
+ attempt: number;
730
+ prompt: string;
731
+ } | {
732
+ type: "attempt_failed";
733
+ attempt: number;
734
+ feedback: string;
735
+ error?: string;
736
+ } | {
737
+ type: "goal_met";
738
+ attempts: number;
739
+ tokens: number;
740
+ costUsd?: number;
741
+ } | {
742
+ type: "goal_abandoned";
743
+ reason: GoalStoppedReason;
744
+ attempts: number;
745
+ tokens: number;
746
+ costUsd?: number;
747
+ };
748
+ /**
749
+ * One goal, its proof, and the budget the runner may spend pursuing it.
750
+ *
751
+ * The budget is not advisory. Every ceiling is checked after the attempt that
752
+ * consumed it, so a run always stops on a named reason rather than drifting.
753
+ */
754
+ interface GoalRunSpec {
755
+ /** The objective, in the owner's words. */
756
+ goal: string;
757
+ /** How the runner knows it is met — surfaced to the agent so it aims at the
758
+ * same thing the gate measures. */
759
+ proof?: string;
760
+ budget: GoalBudget;
761
+ signal?: AbortSignal;
762
+ now?: () => number;
763
+ /**
764
+ * Follow the run. Called for every transition so a project board can be
765
+ * updated from what actually happened rather than from the agent's account
766
+ * of it.
767
+ *
768
+ * Failures here are SWALLOWED. The work is authoritative and the board is a
769
+ * projection of it: a PM outage must not abandon a goal that is succeeding,
770
+ * and losing a comment is cheaper than losing the run. `boardErrors` on the
771
+ * result records what did not land, so silence is never mistaken for success.
772
+ */
773
+ onEvent?(event: GoalEvent): Promise<void> | void;
774
+ }
775
+ /** Why a run ended. `proof_passed` is the only success; the rest are budgets. */
776
+ type GoalStoppedReason = "proof_passed" | "max_attempts" | "token_budget" | "cost_budget" | "deadline" | "cancelled" | "attempt_failed";
777
+ /** One attempt as it actually went, including what the gate said about it. */
778
+ interface GoalAttemptRecord {
779
+ attempt: number;
780
+ gatePassed: boolean;
781
+ tokens: number;
782
+ costUsd?: number;
783
+ feedback: string;
784
+ error?: string;
785
+ }
786
+ /**
787
+ * The whole pursuit: whether the proof passed, why it stopped, and every
788
+ * attempt along the way. `boardErrors` records handler failures the runner
789
+ * swallowed, so a silent kanban outage is visible rather than invisible.
790
+ */
791
+ interface GoalRun {
792
+ met: boolean;
793
+ /** Event-handler failures, in order. Empty when the board kept up. */
794
+ boardErrors: string[];
795
+ stoppedReason: GoalStoppedReason;
796
+ attempts: GoalAttemptRecord[];
797
+ tokens: number;
798
+ /** Summed cost, or undefined when no attempt reported one. */
799
+ costUsd?: number;
800
+ durationMs: number;
801
+ }
802
+ /**
803
+ * Pursue one goal.
804
+ *
805
+ * The re-prompt carries the GATE's output, not a restatement of the goal. That
806
+ * is the whole trick: an agent told only "try again" repeats itself, while an
807
+ * agent handed the failing test output has something new to act on. It is also
808
+ * why the runner refuses to continue without feedback — a gate that fails
809
+ * silently would produce an expensive loop that cannot learn.
810
+ */
811
+ declare function runGoal(spec: GoalRunSpec, attempt: GoalAttempt): Promise<GoalRun>;
812
+
813
+ /** One racer's result, tagged with which racer produced it. */
814
+ interface RacedOutcome extends GoalAttemptOutcome {
815
+ racer: number;
816
+ }
817
+ /**
818
+ * One race: N independent attempts at the same goal, cheapest winner taken.
819
+ *
820
+ * The run is charged for every racer, not just the winner. Racing answers
821
+ * variance, not size; at equal budget, depth beat it on every axis measured.
822
+ */
823
+ interface RacedAttemptOptions {
824
+ /** How many attempts run per round. */
825
+ racers: number;
826
+ /** Runs one racer. Each MUST get an isolated workspace — racers edit in
827
+ * parallel, and a shared tree would have them overwrite each other. */
828
+ attempt(input: GoalAttemptInput & {
829
+ racer: number;
830
+ }): Promise<GoalAttemptOutcome>;
831
+ /** Override the ranking. Default: {@link selectWinner}. */
832
+ select?(outcomes: RacedOutcome[]): RacedOutcome;
833
+ /** Observe the whole field, for reporting what racing cost and bought. */
834
+ onRound?(outcomes: RacedOutcome[], winner: RacedOutcome): void;
835
+ }
836
+ /**
837
+ * Rank a field of racers.
838
+ *
839
+ * Passing candidates first — that is the only thing that decides the goal. Then
840
+ * fewest steps, then cheapest, then smallest patch: three tie-breaks that all
841
+ * prefer the attempt which did LESS to get there, on the view that a smaller
842
+ * change reaching the same verified state is the better one to keep.
843
+ *
844
+ * Unknown cost ranks LAST among passing candidates rather than cheapest. An
845
+ * unpriced racer must not win a cost comparison it never entered.
846
+ */
847
+ declare function selectWinner(outcomes: RacedOutcome[]): RacedOutcome;
848
+ /**
849
+ * Build a GoalAttempt that races `racers` attempts and returns the winner.
850
+ *
851
+ * The returned outcome reports the round's TOTAL tokens and cost, not the
852
+ * winner's. The runner charges its budget from what an attempt reports, and
853
+ * charging only the winner would make racing look free — three racers would
854
+ * cost what one did, and every budget in the system would be wrong by a factor
855
+ * of N. Racing trades depth for breadth at honest spend, or it is not a trade.
856
+ */
857
+ declare function racedAttempt(options: RacedAttemptOptions): GoalAttempt;
858
+
859
+ /** One independent slice of a goal, scoped to the files it may touch. */
860
+ interface SubGoal {
861
+ id: string;
862
+ /** What this sub-agent is asked to do. */
863
+ goal: string;
864
+ /** The files it declares it will touch. Its patch is rejected if it strays. */
865
+ files: string[];
866
+ }
867
+ /** A plan that cannot be run in parallel — overlapping files, or a stray edit. */
868
+ declare class DecompositionError extends Error {
869
+ constructor(message: string);
870
+ }
871
+ /**
872
+ * Reject a plan that cannot be worked in parallel, BEFORE any agent starts.
873
+ *
874
+ * Two sub-goals declaring the same file is the whole failure mode: they would
875
+ * produce patches against the same lines and one of them could not be applied.
876
+ * Catching it here costs nothing; catching it at integration costs every token
877
+ * both sub-agents spent.
878
+ */
879
+ declare function assertDisjointPlan(plan: readonly SubGoal[]): void;
880
+ /** Files a patch actually touches, or null when it is not a valid patch. */
881
+ declare function patchPaths(patch: string, maxBytes?: number): string[] | null;
882
+ /**
883
+ * Check a sub-agent stayed inside what it declared.
884
+ *
885
+ * This is what makes the partition a guarantee rather than a hope: the plan is
886
+ * checked up front, and each patch is checked against the plan. A sub-agent that
887
+ * wandered into a neighbour's file is refused even if its patch would have
888
+ * applied cleanly, because the NEXT sub-patch was written against a tree where
889
+ * that edit does not exist.
890
+ */
891
+ declare function straySubGoalFiles(sub: SubGoal, patch: string): string[];
892
+ /** What one sub-goal produced: its patch, its cost, and whether it worked. */
893
+ interface SubGoalResult {
894
+ sub: SubGoal;
895
+ /** The candidate patch, or empty when the sub-agent changed nothing. */
896
+ patch: string;
897
+ tokens: number;
898
+ costUsd?: number;
899
+ error?: string;
900
+ }
901
+ /** Applying one sub-goal's patch to the accumulating tree, and the verdict. */
902
+ interface IntegrationStep {
903
+ subGoalId: string;
904
+ /** Did the accumulated tree still satisfy the proof after applying this one? */
905
+ gatePassed: boolean;
906
+ applied: boolean;
907
+ reason?: "stray_files" | "did_not_apply" | "gate_failed" | "sub_goal_failed" | "no_changes";
908
+ detail?: string;
909
+ }
910
+ /**
911
+ * A decomposed pursuit end to end.
912
+ *
913
+ * Measured at 1.68x the tokens of straight depth for 0.88x the wall clock, so
914
+ * this buys latency, not efficiency — see chooseStrategy for when that trade
915
+ * is worth making.
916
+ */
917
+ interface DecomposedRun {
918
+ met: boolean;
919
+ steps: IntegrationStep[];
920
+ tokens: number;
921
+ costUsd?: number;
922
+ }
923
+ /** The plan, its results, and the two callbacks that apply and judge them. */
924
+ interface IntegrateOptions {
925
+ plan: readonly SubGoal[];
926
+ results: readonly SubGoalResult[];
927
+ /** Apply one patch to the accumulating tree; reject means it did not apply. */
928
+ apply(patch: string): Promise<boolean>;
929
+ /** Run the proof against the accumulating tree. */
930
+ gate(): Promise<boolean>;
931
+ }
932
+ /**
933
+ * Integrate sub-results one at a time, gating after each.
934
+ *
935
+ * Order follows the plan, and the first failure stops integration. Continuing
936
+ * past one would mean gating a tree whose earlier layer is already known bad,
937
+ * so every later verdict would describe a state nobody intends to ship.
938
+ */
939
+ declare function integrateSubGoals(options: IntegrateOptions): Promise<DecomposedRun>;
940
+ /**
941
+ * The check `assertDisjointPlan` cannot make: do the sub-goals collide through
942
+ * what they *reach*, not merely through what they declare?
943
+ *
944
+ * Collision ids are graph ids (`file:src/a.ts`, `table:orders`), because the
945
+ * answer is no longer only about files — a shared table is a real collision and
946
+ * naming it as a bare path would be a lie about what it is.
947
+ *
948
+ * Declared files being disjoint is necessary and not sufficient. Two sub-goals
949
+ * can own different modules and still both depend on a third; a change either
950
+ * one makes to that shared module lands in one patch and is invisible to the
951
+ * other's verification. The import graph is the only thing that can see this,
952
+ * and it is cheap — the graph over 3,400 files builds in ~400ms, against the
953
+ * cost of running two agents to completion and discovering it at integration.
954
+ *
955
+ * Reported rather than thrown, because a shared dependency is sometimes fine:
956
+ * two sub-goals may both READ a types module neither intends to touch. The
957
+ * caller decides whether the overlap is one it can live with.
958
+ */
959
+ declare function planReachCollisions(plan: readonly SubGoal[], workspace: {
960
+ paths: readonly string[];
961
+ read(path: string): Promise<string>;
962
+ }): Promise<PartitionVerdict>;
963
+
964
+ /** What is known about a goal before choosing how to pursue it. */
965
+ interface StrategySignals {
966
+ /** Prior attempts, oldest first. Empty on the first pass. */
967
+ priorAttempts?: Array<{
968
+ gatePassed: boolean;
969
+ feedback: string;
970
+ error?: string;
971
+ }>;
972
+ /** True when wall clock is the binding constraint — a human is waiting, or a
973
+ * deadline is close — and paying more per solve to finish sooner is correct. */
974
+ latencyBound?: boolean;
975
+ /** Estimated tokens needed to hold the whole goal at once. */
976
+ estimatedContextTokens?: number;
977
+ /** What one agent can actually hold. */
978
+ contextLimit?: number;
979
+ /** A checked disjoint partition, when the planner produced one. */
980
+ partitionable?: boolean;
981
+ /** Racers/sub-agents available if fanning out. */
982
+ width?: number;
983
+ }
984
+ /** How to spend a goal's budget: straight depth, N racers, or a split plan. */
985
+ type GoalStrategy = "sequential" | "race" | "decompose";
986
+ /** The chosen strategy, why, and what it is expected to cost against depth. */
987
+ interface StrategyChoice {
988
+ strategy: GoalStrategy;
989
+ /** Why, in terms a human reviewing a run can check. */
990
+ reason: string;
991
+ /** Expected cost multiplier vs sequential, from the measurements above.
992
+ * 1 for sequential; fan-out is charged what it was observed to cost. */
993
+ expectedCostMultiplier: number;
994
+ /** False when the branch rests on something not yet measured. */
995
+ measured: boolean;
996
+ }
997
+ /** Observed premiums. Update these from the bench, not from expectation. */
998
+ declare const MEASURED_PREMIUM: Readonly<{
999
+ /** 3 racers vs pure depth at equal budget: 21,044 / 7,936. */
1000
+ racePerRacer: 0.55;
1001
+ /** Decomposition across 3 sub-agents: 10,897 / 6,474. */
1002
+ decomposePerSubGoal: 0.23;
1003
+ }>;
1004
+ /**
1005
+ * Choose a strategy.
1006
+ *
1007
+ * Order matters and encodes the evidence: depth first, because it won every
1008
+ * measured comparison; fan-out only where depth demonstrably cannot help.
1009
+ */
1010
+ declare function chooseStrategy(signals?: StrategySignals): StrategyChoice;
1011
+ /** Whether the gate's output gives the next attempt something to work with. */
1012
+ declare const feedbackIsActionable: (feedback: string) => boolean;
1013
+
1014
+ /**
1015
+ * Strip an apply_patch envelope from around a git diff.
1016
+ *
1017
+ * Models emit `*** Begin Patch` / `*** End Patch` constantly — twice in five
1018
+ * tool calls during the first real dogfood — even with a system prompt that
1019
+ * forbids it by name, because it is the dominant format elsewhere. What they
1020
+ * wrap in it is usually a perfectly good unified diff, and git apply then dies
1021
+ * on "unexpected line: *** End Patch".
1022
+ *
1023
+ * Refusing that is a surface arguing with its users and charging them a turn to
1024
+ * lose. The envelope is stripped ONLY when a real `diff --git` survives
1025
+ * underneath: this accepts a mis-wrapped diff, and does not attempt to
1026
+ * translate the genuinely different apply_patch dialect, which would mean
1027
+ * inventing hunks nobody wrote.
1028
+ */
1029
+ declare function stripPatchEnvelope(patch: string): string;
1030
+ /** Validate a text-only, same-path unified patch and return every affected path. */
1031
+ declare function validateCodePatch(rawPatch: string, maxBytes: number): string[];
1032
+ /** Restrict Code tool paths to ordinary files outside filtered/secret trees. */
1033
+ declare function validateRelativePath(path: string): void;
1034
+ /** Resolve a validated relative path without permitting a root escape. */
1035
+ declare function resolveCodePath(workspaceDir: string, path: string): string;
1036
+ /**
1037
+ * Why `git apply` rejected a patch, in terms the author can act on.
1038
+ *
1039
+ * A hunk with no context lines is the common case and the least obvious: git
1040
+ * refuses it whenever the file has more lines than the hunk claims, with only
1041
+ * "patch does not apply" to show for it. Models emit context-free hunks
1042
+ * constantly, so saying this plainly turns a silent retry loop into one
1043
+ * corrected call.
1044
+ */
1045
+ declare function describePatchFailure(patch: string, detail: string): string;
1046
+ /** Apply a previously validated patch without invoking a shell or repository hooks. */
1047
+ declare function applyCodePatch(workspaceDir: string, rawPatch: string, paths: readonly string[]): Promise<void>;
1048
+
1049
+ /**
1050
+ * An installed dependency tree lent to a recipe for the length of one run.
1051
+ *
1052
+ * Mounted under a reserved name and removed in a finally, so the agent can run
1053
+ * a real build without the tree ever being addressable by a patch.
1054
+ */
1055
+ interface RecipeDependencies {
1056
+ /** Absolute path to an installed dependency tree on the host. */
1057
+ source: string;
1058
+ /** Where it appears inside the workspace. Must be a reserved name, so the
1059
+ * agent still cannot address it. */
1060
+ mountAs?: string;
1061
+ }
1062
+ /**
1063
+ * Wrap an executor so `dependencies.source` is present during each recipe run.
1064
+ *
1065
+ * It is linked, not copied. A link costs nothing per run, and dependencies are
1066
+ * identical across every attempt, racer and verification — copying them would
1067
+ * multiply the largest thing in the tree by the number of stages.
1068
+ *
1069
+ * The link is removed in a `finally`, so a recipe that times out or throws
1070
+ * cannot leave it behind for `workspace.patch()` to diff or
1071
+ * `digestStagedWorkspace` to hash.
1072
+ */
1073
+ declare function withRecipeDependencies(executor: CodeRecipeExecutor, dependencies: RecipeDependencies): CodeRecipeExecutor;
1074
+ /** Resolve the dependency tree for a repository root, when it has one. */
1075
+ declare function installedDependencies(repoRoot: string): Promise<RecipeDependencies | null>;
1076
+
1077
+ /** Every ordinary, policy-legal source path under `root`, sorted. */
1078
+ declare function registeredFiles(root: string, limit?: number): Promise<string[]>;
1079
+
499
1080
  /** Build a trusted, fixed command for an isolated and networkless recipe container. */
500
1081
  declare function buildRecipeContainerArgs(engine: ContainerEngine, workspaceDir: string, recipe: CodeBuildRecipe, name?: string): string[];
501
1082
  /** Create a recipe executor that never invokes a shell and never mounts credentials. */
@@ -541,4 +1122,4 @@ interface CodeVerificationEvidence {
541
1122
  /** Rebuild a candidate from an exact trusted base and emit a prose-free clean-verifier receipt. */
542
1123
  declare function verifyCodeCandidate(input: VerifyCodeCandidateInput): Promise<CodeVerificationEvidence>;
543
1124
 
544
- export { CODE_RUNTIME_PROTOCOL_VERSION, type CodeBuildRecipe, type CodeExpectedArtifact, type CodeLocalSourceDescriptor, CodePiRuntimeEngine, type CodePiRuntimeEngineOptions, type CodeRecipeExecutor, type CodeRecipeResult, type CodeRuntimeAgentControlPlane, type CodeRuntimeBinding, type CodeRuntimeCandidateResponse, type CodeRuntimeCapabilities, CodeRuntimeCheckpointManager, type CodeRuntimeClientOptions, type CodeRuntimeCommand, type CodeRuntimeCommandEngine, type CodeRuntimeCommandKind, type CodeRuntimeCommandResult, CodeRuntimeControlError, type CodeRuntimeControlPlane, type CodeRuntimeLoopOptions, CodeRuntimeReconciler, type CodeRuntimeReviewRequest, type CodeRuntimeReviewResponse, type CodeRuntimeSnapshot, type CodeRuntimeSourceSnapshot, type CodeToolBrokerOptions, type CodeToolDecision, type CodeVerificationEvidence, type CodeVerificationLog, type CodeVerificationPolicy, type ContainerEngine, type ContainerEngineSelectionOptions, type ContainerEngineVerificationOptions, type ContainerLimits, type ContainerRunOptions, type ContainerRunResult, type CreateCodeWorkspaceCheckpointInput, type HarnessRunnerOptions, type MaterializedCodeSource, type MaterializedGitTree, type PrepareRuntimeCheckpointInput, type PreparedRuntimeCheckpoint, type RestoreCodeWorkspaceCheckpointInput, type RestoredCodeWorkspaceCheckpoint, type RuntimeCheckpointSession, type StageWorkspaceOptions, type StagedWorkspace, type VerifyCodeCandidateInput, assertCodeBuildRecipe, assertPinnedImage, attachCodeRuntimeReferences, buildContainerRunArgs, buildRecipeContainerArgs, createCodeRuntimeControlClient, createCodeToolBroker, createCodeWorkspaceCheckpoint, createContainerRecipeExecutor, digestStagedWorkspace, isCheckpointEffectCompleted, materializeCodeRuntimeSource, materializeGitTree, prepareRuntimeCheckpoint, restoreCodeWorkspaceCheckpoint, runCodeRuntimeHeartbeatLoop, runContainerAttempt, runHarnessRunner, runLeasedAttempt, safeWorkspaceLabel, selectContainerEngine, stageWorkspace, stageWorkspacePair, verifyCodeCandidate, verifyContainerEngineBoundary };
1125
+ export { CODE_RUNTIME_PROTOCOL_VERSION, type CodeAgentAttemptOptions, type CodeAgentAttemptResult, type CodeAgentRun, type CodeBuildRecipe, type CodeExpectedArtifact, type CodeLocalSourceDescriptor, CodePiRuntimeEngine, type CodePiRuntimeEngineOptions, type CodeRecipeExecutor, type CodeRecipeResult, type CodeRuntimeAgentControlPlane, type CodeRuntimeBinding, type CodeRuntimeCandidateResponse, type CodeRuntimeCapabilities, CodeRuntimeCheckpointManager, type CodeRuntimeClientOptions, type CodeRuntimeCommand, type CodeRuntimeCommandEngine, type CodeRuntimeCommandKind, type CodeRuntimeCommandResult, CodeRuntimeControlError, type CodeRuntimeControlPlane, type CodeRuntimeInferenceOptions, type CodeRuntimeLoopOptions, CodeRuntimeReconciler, type CodeRuntimeReviewRequest, type CodeRuntimeReviewResponse, type CodeRuntimeSnapshot, type CodeRuntimeSourceSnapshot, type CodeSkillOpts, type CodeSurface, type CodeToolBrokerOptions, type CodeToolCallRecord, type CodeToolDecision, type CodeVerificationEvidence, type CodeVerificationLog, type CodeVerificationPolicy, type ContainerEngine, type ContainerEngineSelectionOptions, type ContainerEngineVerificationOptions, type ContainerLimits, type ContainerRunOptions, type ContainerRunResult, type CreateCodeWorkspaceCheckpointInput, type DecomposedRun, DecompositionError, type GoalAttempt, type GoalAttemptInput, type GoalAttemptOutcome, type GoalAttemptRecord, type GoalBudget, type GoalEvent, type GoalRun, type GoalRunSpec, type GoalStoppedReason, type GoalStrategy, type HarnessRunnerOptions, type IntegrateOptions, type IntegrationStep, MEASURED_PREMIUM, type MaterializedCodeSource, type MaterializedGitTree, type PrepareRuntimeCheckpointInput, type PreparedRuntimeCheckpoint, type RacedAttemptOptions, type RacedOutcome, type RecipeDependencies, type RestoreCodeWorkspaceCheckpointInput, type RestoredCodeWorkspaceCheckpoint, type RunCodeAgentOptions, type RuntimeCheckpointSession, SYSTEM_PROMPT_FOR, type StageWorkspaceOptions, type StagedWorkspace, type StrategyChoice, type StrategySignals, type SubGoal, type SubGoalResult, V1_SYSTEM_PROMPT, V2_SYSTEM_PROMPT, V3_SYSTEM_PROMPT, type VerifyCodeCandidateInput, applyCodePatch, assertCodeBuildRecipe, assertDisjointPlan, assertPinnedImage, attachCodeRuntimeReferences, buildContainerRunArgs, buildRecipeContainerArgs, chooseStrategy, codeSkill, createCodeRuntimeControlClient, createCodeRuntimeInference, createCodeToolBroker, createCodeWorkspaceCheckpoint, createContainerRecipeExecutor, describePatchFailure, digestStagedWorkspace, feedbackIsActionable, installedDependencies, integrateSubGoals, isCheckpointEffectCompleted, materializeCodeRuntimeSource, materializeCommandWorkspace, materializeGitTree, patchPaths, planReachCollisions, prepareRuntimeCheckpoint, racedAttempt, registeredFiles, resolveCodePath, restoreCodeWorkspaceCheckpoint, runCodeAgent, runCodeAgentAttempt, runCodeRuntimeHeartbeatLoop, runContainerAttempt, runGoal, runHarnessRunner, runLeasedAttempt, safeWorkspaceLabel, selectContainerEngine, selectWinner, stageWorkspace, stageWorkspacePair, straySubGoalFiles, stripPatchEnvelope, validateCodePatch, validateRelativePath, verifyCodeCandidate, verifyContainerEngineBoundary, withRecipeDependencies };