@tangle-network/agent-runtime 0.85.0 → 0.87.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.
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  parseLoopRunnerArgv,
4
4
  runLoopRunnerCli
5
- } from "./chunk-QSYPMMWS.js";
5
+ } from "./chunk-FLVVVBWV.js";
6
6
  import "./chunk-SGKPNBXE.js";
7
- import "./chunk-DLAEEF26.js";
8
- import "./chunk-XN5TIJGP.js";
7
+ import "./chunk-VKVNDNG4.js";
8
+ import "./chunk-6XCW3M7W.js";
9
9
  import "./chunk-UD4BHQMI.js";
10
10
  import "./chunk-BZF3KQ6G.js";
11
- import "./chunk-YPA5MLVE.js";
12
- import "./chunk-4J6RBI3K.js";
11
+ import "./chunk-LCXXIL3U.js";
12
+ import "./chunk-QUXZBZCS.js";
13
13
  import "./chunk-7LO5GMAO.js";
14
14
  import "./chunk-YEJR7IXO.js";
15
15
  import "./chunk-DPEUKJRO.js";
package/dist/loops.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ChatClient, RunRecord, HarnessType, AgentProfile, AnalystFinding, AnalystRunInputs, ToolSpan, StreamingDetector, DetectorSignal, buildTrajectory } from '@tangle-network/agent-eval';
2
2
  export { AnalystFinding, DefaultVerdict, computeFindingId, makeFinding } from '@tangle-network/agent-eval';
3
- import { SandboxEvent, SandboxInstance, CreateSandboxOptions } from '@tangle-network/sandbox';
3
+ import { SandboxEvent, SandboxInstance, CreateSandboxOptions, PromptOptions, TaskOptions } from '@tangle-network/sandbox';
4
4
  export { AgentProfile, CreateSandboxOptions, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox';
5
5
  import { d as ResultBlobStore, S as SpawnJournal, N as NodeId, j as SpawnEvent, E as ExecutorFactory, c as Agent, B as Budget, i as Scope, g as Settled, f as SupervisedResult, h as Spend, U as UsageEvent, b as ExecutorRegistry, k as Supervisor } from './types-Driepl87.js';
6
6
  export { A as AgentSpec, a as Executor, l as ExecutorContext, m as ExecutorResult, R as Runtime, n as SupervisorOpts, T as TreeView, W as WidenGate } from './types-Driepl87.js';
@@ -544,8 +544,10 @@ interface LeaderboardBenchScore {
544
544
  detail?: string;
545
545
  }
546
546
  /** Structurally `BenchmarkAdapter` (bench registry shape): `name`,
547
- * `preflight()`, `loadTasks()`, deterministic `judge()`, `goldArtifact()`. */
548
- interface LeaderboardBenchmarkAdapter {
547
+ * `preflight()`, `loadTasks()`, deterministic `judge()`, `goldArtifact()`.
548
+ * Generic over the artifact channel; the `string` default IS the registry
549
+ * shape, so a default-artifact adapter registers unchanged. */
550
+ interface LeaderboardBenchmarkAdapter<TArtifact = string> {
549
551
  readonly name: string;
550
552
  preflight(): Promise<void>;
551
553
  loadTasks(opts?: {
@@ -553,10 +555,29 @@ interface LeaderboardBenchmarkAdapter {
553
555
  split?: string;
554
556
  ids?: string[];
555
557
  }): Promise<LeaderboardBenchTask[]>;
556
- judge(task: LeaderboardBenchTask, artifact: string): Promise<LeaderboardBenchScore>;
558
+ judge(task: LeaderboardBenchTask, artifact: TArtifact): Promise<LeaderboardBenchScore>;
557
559
  goldArtifact(task: LeaderboardBenchTask): Promise<string | undefined>;
558
560
  }
559
- interface LeaderboardSpec<TCase> {
561
+ /** Per-shot outcome context passed as `onCellEvents`'s third argument — how a
562
+ * thrown shot (which never reaches `parseOutput`) stays visible through the
563
+ * facade instead of surfacing only as an empty zero-token cell. */
564
+ interface LeaderboardIterationInfo {
565
+ /** 0-based shot index within the cell. */
566
+ index: number;
567
+ /** The shot's thrown error message, when the shot failed before scoring. */
568
+ error?: string;
569
+ /** The shot's validator verdict, when the shot reached scoring. */
570
+ verdict?: {
571
+ score?: number;
572
+ };
573
+ }
574
+ /**
575
+ * The declarative leaderboard spec. `TArtifact` is the artifact channel the
576
+ * dispatch produces and the judges score — `string` (the default) is the plain
577
+ * agent-response-text path; a structured artifact type flows natively once the
578
+ * spec supplies `parseOutput` (or a LEVEL-2 `dispatch`) producing it.
579
+ */
580
+ interface LeaderboardSpec<TCase, TArtifact = string> {
560
581
  /** Leaderboard name — the scenario `kind`, default profile name, and report title. */
561
582
  name: string;
562
583
  /** The case corpus. Every case needs a stable string id (see `caseId`). */
@@ -567,10 +588,10 @@ interface LeaderboardSpec<TCase> {
567
588
  /** The per-case task prompt. May be async (e.g. built by shelling out to a
568
589
  * reference implementation); resolved ONCE per case before dispatch. */
569
590
  prompt: (c: TCase) => string | Promise<string>;
570
- /** The domain grader: agent output text → score. Used BOTH as the per-shot
571
- * validator (a shot with `composite > 0` stops the naive retry loop) and,
572
- * wrapped as a campaign judge, as the recorded leaderboard score. */
573
- score: (output: string, c: TCase) => number | LeaderboardScore;
591
+ /** The domain grader: agent output artifact → score. Used BOTH as the
592
+ * per-shot validator (a shot with `composite > 0` stops the naive retry
593
+ * loop) and, wrapped as a campaign judge, as the recorded leaderboard score. */
594
+ score: (output: TArtifact, c: TCase) => number | LeaderboardScore;
574
595
  /** Harness × model axes for `expandProfileAxes`. Defaults: the canonical
575
596
  * `CODING_HARNESSES` × the base profile's `model.default`. `--harnesses` /
576
597
  * `--models` override per run. */
@@ -601,22 +622,36 @@ interface LeaderboardSpec<TCase> {
601
622
  setup?: (ctx: LeaderboardRunContext) => Promise<void> | void;
602
623
  /** Runs once after the matrix, even on failure (reap boxes, close handles). */
603
624
  teardown?: (ctx: LeaderboardRunContext) => Promise<void> | void;
604
- /** Per-cell event tap: the raw sandbox events of each parsed iteration,
605
- * with the case — the seam for domain metric capture (search counts,
606
- * citations) without a substrate change. */
607
- onCellEvents?: (events: readonly SandboxEvent[], c: TCase) => void;
608
- /** Output decode override: raw events the scored output text. Default:
609
- * the sandbox SDK's `collectAgentResponseText` (final answer text; empty
610
- * string when the stream carried none which then scores 0). */
611
- parseOutput?: (events: readonly SandboxEvent[], c: TCase) => string;
625
+ /** Per-cell event tap: the raw sandbox events of EVERY shot, with the case —
626
+ * the seam for domain metric capture (search counts, citations) without a
627
+ * substrate change. Fires once per shot after the cell's loop settles, in
628
+ * shot order, including thrown shots (whose events may be partial or empty);
629
+ * the third argument carries the shot's index + error/verdict outcome. */
630
+ onCellEvents?: (events: readonly SandboxEvent[], c: TCase, iteration?: LeaderboardIterationInfo) => void;
631
+ /** Output decode override: raw events the scored artifact. Default: the
632
+ * sandbox SDK's `collectAgentResponseText` (final answer text; empty string
633
+ * when the stream carried none — which then scores 0). The default only
634
+ * produces `string`, so a spec with a structured `TArtifact` MUST supply
635
+ * this (or a LEVEL-2 `dispatch`). */
636
+ parseOutput?: (events: readonly SandboxEvent[], c: TCase) => TArtifact;
637
+ /**
638
+ * Resolve the model the backend ACTUALLY served off a shot's raw events.
639
+ * Required for HARNESS_NATIVE_MODEL-snapped cells (a vendor-locked harness ×
640
+ * an out-of-family model expands to the `default` sentinel): the RunRecord
641
+ * must pin a real snapshot-bearing model id, which only the dispatch —
642
+ * reading the backend's usage/terminal events — can know. When this returns
643
+ * a value the default dispatch reports it via `ctx.cost.observeModel`;
644
+ * in-family cells (concrete declared model) never need it.
645
+ */
646
+ resolveModel?: (events: readonly SandboxEvent[]) => string | undefined;
612
647
  /** Result export. Default: write `matrix-result.json` under the run dir and
613
648
  * print (+ write) the ranked leaderboard markdown under the export dir. */
614
- export?: (result: RunProfileMatrixResult<string, LeaderboardScenario<TCase>>, ctx: LeaderboardRunContext) => Promise<void> | void;
649
+ export?: (result: RunProfileMatrixResult<TArtifact, LeaderboardScenario<TCase>>, ctx: LeaderboardRunContext) => Promise<void> | void;
615
650
  /** LEVEL 2 — full dispatch replacement (in-process products bring their own).
616
651
  * The default is `loopDispatch` + `naiveDriver` over the resolved backend. */
617
- dispatch?: ProfileDispatchFn<LeaderboardScenario<TCase>, string>;
652
+ dispatch?: ProfileDispatchFn<LeaderboardScenario<TCase>, TArtifact>;
618
653
  /** LEVEL 2 — full judge replacement. Default: `score` wrapped as one judge. */
619
- judges?: JudgeConfig<string, LeaderboardScenario<TCase>>[];
654
+ judges?: JudgeConfig<TArtifact, LeaderboardScenario<TCase>>[];
620
655
  /** Naive-retry shot cap per cell (`--shots`). Default 1. */
621
656
  shots?: number;
622
657
  /** Replicates per cell (`--reps`). Default 1. */
@@ -624,9 +659,9 @@ interface LeaderboardSpec<TCase> {
624
659
  /** Passthrough overrides spread onto the final `runProfileMatrix` call
625
660
  * (e.g. `maxConcurrency`, `costCeiling`, `integrity`, `storage`) — spread
626
661
  * LAST, so anything the facade wired can be overridden. */
627
- matrix?: Partial<RunProfileMatrixOptions<LeaderboardScenario<TCase>, string>>;
662
+ matrix?: Partial<RunProfileMatrixOptions<LeaderboardScenario<TCase>, TArtifact>>;
628
663
  }
629
- interface DefinedLeaderboard<TCase> {
664
+ interface DefinedLeaderboard<TCase, TArtifact = string> {
630
665
  /**
631
666
  * Parse flags, run the matrix, export, and return the raw result.
632
667
  *
@@ -640,11 +675,11 @@ interface DefinedLeaderboard<TCase> {
640
675
  * would silently reuse a prior FAILED zero-token cell and skip dispatch —
641
676
  * only an explicit `--run-dir` opts into that resume behavior.
642
677
  */
643
- run(argv?: string[]): Promise<RunProfileMatrixResult<string, LeaderboardScenario<TCase>>>;
678
+ run(argv?: string[]): Promise<RunProfileMatrixResult<TArtifact, LeaderboardScenario<TCase>>>;
644
679
  /** The same domain surface in the structural `BenchmarkAdapter` shape. */
645
- toBenchmarkAdapter(): LeaderboardBenchmarkAdapter;
680
+ toBenchmarkAdapter(): LeaderboardBenchmarkAdapter<TArtifact>;
646
681
  }
647
- declare function defineLeaderboard<TCase>(spec: LeaderboardSpec<TCase>): DefinedLeaderboard<TCase>;
682
+ declare function defineLeaderboard<TCase, TArtifact = string>(spec: LeaderboardSpec<TCase, TArtifact>): DefinedLeaderboard<TCase, TArtifact>;
648
683
 
649
684
  /**
650
685
  * The third-person observer — the connective tissue that closes the loop.
@@ -767,10 +802,11 @@ interface HarvestReport {
767
802
  /** Batch the firewalled `observe()` analyst over completed runs and accrete the trace-derived facts into the durable corpus — the production-traces→corpus write side of the flywheel. */
768
803
  declare function harvestCorpus(opts: HarvestCorpusOptions): Promise<HarvestReport>;
769
804
 
770
- /** Context handed to each `onPrompt` call. */
805
+ /** Context handed to each `onPrompt` / `onTask` call. */
771
806
  interface InProcessPromptCtx {
772
- /** 0-based round index — increments per `streamPrompt` on the SAME box (so a
773
- * refine driver's round N can differ from round N-1). Fresh boxes start at 0. */
807
+ /** 0-based round index — increments per `streamPrompt`/`streamTask` on the
808
+ * SAME box (so a refine driver's round N can differ from round N-1). Fresh
809
+ * boxes start at 0. */
774
810
  round: number;
775
811
  /** Absolute path of this box's workspace, when a `workdir` was configured.
776
812
  * Write the deliverable / fixtures here; `fs.read`/`fs.write`/`exec` operate
@@ -778,6 +814,13 @@ interface InProcessPromptCtx {
778
814
  workdir?: string;
779
815
  /** Cooperative cancellation channel for this turn. */
780
816
  signal: AbortSignal;
817
+ /** Which box verb produced this call: `prompt` = `streamPrompt`,
818
+ * `task` = `streamTask`. */
819
+ mode: 'prompt' | 'task';
820
+ /** The verbatim per-call options the caller passed to the box verb (minus
821
+ * `signal`, surfaced above) — lets an offline test assert an options
822
+ * passthrough (`model`, `sessionId`, `maxTurns`, …) actually arrived. */
823
+ options?: Record<string, unknown>;
781
824
  }
782
825
  /**
783
826
  * The user callback: given a prompt and its round, produce the box's event
@@ -790,6 +833,14 @@ type InProcessOnPrompt = (prompt: string, ctx: InProcessPromptCtx) => SandboxEve
790
833
  interface InProcessSandboxClientOptions {
791
834
  /** The per-turn behavior — see {@link InProcessOnPrompt}. */
792
835
  onPrompt: InProcessOnPrompt;
836
+ /**
837
+ * Task-mode behavior, driven by `box.streamTask` (the verb `streamAgentTurn`'s
838
+ * `box-task` backend calls). When omitted, `streamTask` drives `onPrompt` —
839
+ * the pseudo-box has ONE behavior callback and both verbs exercise it
840
+ * (`ctx.mode` tells them apart). Provide `onTask` when a test must
841
+ * discriminate the verbs or script different task-mode behavior.
842
+ */
843
+ onTask?: InProcessOnPrompt;
793
844
  /**
794
845
  * Opt in to a REAL filesystem-backed box. When set, each `create()` mints a
795
846
  * fresh temp directory (prefixed `<workdir>-`) and the box exposes
@@ -2052,6 +2103,51 @@ declare function sumSandboxUsage(events: readonly SandboxEvent[], agentRunName?:
2052
2103
  output: number;
2053
2104
  costUsd: number;
2054
2105
  };
2106
+ /**
2107
+ * Cross-event state for {@link mapSandboxToolEvent}. Sandbox backends emit a
2108
+ * tool invocation as MANY `message.part.updated` frames on the same call id
2109
+ * (pending → running → completed), so faithful projection needs per-call
2110
+ * status memory: one `tool_call` on first sighting, at most one `tool_result`
2111
+ * on the terminal transition, nothing on intermediate re-frames. Create one
2112
+ * state per turn via {@link createSandboxToolPartState}.
2113
+ *
2114
+ * @experimental
2115
+ */
2116
+ interface SandboxToolPartState {
2117
+ /** Last seen status per tool call id. A terminal status is sticky — later
2118
+ * frames on a settled call project to nothing. */
2119
+ statusByCall: Map<string, string>;
2120
+ /** Sequence for synthesized call ids when an event carries none. */
2121
+ seq: number;
2122
+ }
2123
+ /** @experimental */
2124
+ declare function createSandboxToolPartState(): SandboxToolPartState;
2125
+ /**
2126
+ * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of
2127
+ * `RuntimeStreamEvent` — the tool-part projection `mapSandboxEvent`
2128
+ * deliberately does NOT perform. Opt-in and additive: `mapSandboxEvent`'s
2129
+ * default vocabulary (text/reasoning deltas + `llm_call`) is unchanged;
2130
+ * consumers that need the tool surface (chat UIs rendering tool activity)
2131
+ * compose this projector alongside it — `streamAgentTurn` does exactly that
2132
+ * under its `preserveToolParts` option.
2133
+ *
2134
+ * Handled shapes (observed on the opencode / claude-code sandbox backends):
2135
+ * - `message.part.updated` with `part.type === 'tool'` — stateful: a
2136
+ * `tool_call` on the call id's first frame (args from `state.input` or
2137
+ * `state.metadata.input`), a `tool_result` when the status transitions to
2138
+ * `completed` (result from `state.output` / `metadata.output`) or to a
2139
+ * terminal failure (result is `{ error, status, output? }` — the error
2140
+ * surfaced in-band, never dropped).
2141
+ * - bare `tool*` event types (`tool.call`, `tool_result`, …) — stateless:
2142
+ * `*result*` types project to `tool_result`, the rest to `tool_call`.
2143
+ *
2144
+ * Returns `[]` for every non-tool event.
2145
+ *
2146
+ * @experimental
2147
+ */
2148
+ declare function mapSandboxToolEvent(event: SandboxEvent, state: SandboxToolPartState): (RuntimeStreamEvent & {
2149
+ type: 'tool_call' | 'tool_result';
2150
+ })[];
2055
2151
  /**
2056
2152
  * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,
2057
2153
  * for runtimes that bridge a sandbox `streamPrompt` into the
@@ -2065,6 +2161,9 @@ declare function sumSandboxUsage(events: readonly SandboxEvent[], agentRunName?:
2065
2161
  * - `message.part.updated` reasoning/thinking part → `reasoning_delta`
2066
2162
  * - cost-bearing events → `llm_call` (shared with the ledger extractor)
2067
2163
  *
2164
+ * Tool parts are deliberately NOT mapped here (unchanged default) — compose
2165
+ * {@link mapSandboxToolEvent} alongside when a consumer needs them.
2166
+ *
2068
2167
  * The opencode backend emits incremental text as
2069
2168
  * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;
2070
2169
  * `delta` is the increment, `part.text` the running accumulation.
@@ -2747,7 +2846,8 @@ declare function runStrategyEvolution(cfg: StrategyEvolutionConfig): Promise<Evo
2747
2846
 
2748
2847
  /**
2749
2848
  * `streamAgentTurn` — the ONE run-a-turn event-stream contract over every
2750
- * execution substrate: a sandbox box (`SandboxInstance.streamPrompt`), a
2849
+ * execution substrate: a sandbox box (`SandboxInstance.streamPrompt`, or
2850
+ * `streamTask` via the `box-task` kind for autonomous-task semantics), a
2751
2851
  * one-shot `Executor` (cli-bridge / router / BYO, via `ExecutorFactory`), and
2752
2852
  * an in-process `AgentExecutionBackend` (the `resolveAgentBackend` output).
2753
2853
  *
@@ -2762,6 +2862,10 @@ declare function runStrategyEvolution(cfg: StrategyEvolutionConfig): Promise<Evo
2762
2862
  * adapter over code that already exists and is already hardened:
2763
2863
  * - `box` — `mapSandboxEvent` + `extractLlmCallEvent` (sandbox-events.ts)
2764
2864
  * project the sandbox event stream; nothing is re-mapped here.
2865
+ * - `box-task` — the same projection over `box.streamTask` (the sandbox
2866
+ * SDK's autonomous-task verb: the agent works to completion,
2867
+ * multi-turn, session state maintained) with per-task
2868
+ * `TaskOptions` passthrough.
2765
2869
  * - `executor` — `inlineSandboxClient` (the ONE executor→box adapter) turns
2766
2870
  * the factory into a box, then the box path drives it. The
2767
2871
  * executor's settle/teardown lifecycle stays in that adapter.
@@ -2781,6 +2885,15 @@ declare function runStrategyEvolution(cfg: StrategyEvolutionConfig): Promise<Evo
2781
2885
  * `final.status: 'failed'` — so cancellation stays distinguishable from a
2782
2886
  * blown deadline.
2783
2887
  *
2888
+ * Mid-stream lifecycle work needs NO extra API: the generator is pull-based,
2889
+ * so the producer is suspended between yields and resumes only when the caller
2890
+ * pulls again. A consumer can therefore run arbitrary async work between
2891
+ * events — sync state on each `tool_result`, decide a no-op retry after
2892
+ * draining, run a pre-`done` flush when it receives `final` and BEFORE it
2893
+ * forwards its own terminal event downstream. The interleaving is guaranteed
2894
+ * (and locked by test): nothing is produced past the event the caller is
2895
+ * holding.
2896
+ *
2784
2897
  * @experimental
2785
2898
  */
2786
2899
 
@@ -2794,6 +2907,34 @@ type AgentTurnBackend = {
2794
2907
  /** A live sandbox box: the turn is one `box.streamPrompt(prompt)` call. */
2795
2908
  kind: 'box';
2796
2909
  box: SandboxInstance;
2910
+ /**
2911
+ * Per-turn `PromptOptions` forwarded verbatim to `streamPrompt`
2912
+ * (`sessionId`, `turnId`, `model`, `backend` profile, `timeoutMs`, …).
2913
+ * The turn's derived abort signal (caller `signal` + `timeoutMs`
2914
+ * deadline) is always installed as `signal` — pass cancellation through
2915
+ * `StreamAgentTurnOptions`, not here.
2916
+ */
2917
+ options?: Omit<PromptOptions, 'signal'>;
2918
+ /** Model label stamped on cost-only `llm_call` events. Default `'agent'`. */
2919
+ agentRunName?: string;
2920
+ } | {
2921
+ /**
2922
+ * A live sandbox box in TASK mode: the turn is one
2923
+ * `box.streamTask(prompt)` call — the sandbox SDK's autonomous-task
2924
+ * verb. Unlike `streamPrompt` (one chat turn), the agent works until
2925
+ * the task completes or errors, session state is maintained for
2926
+ * continuity, and `options.maxTurns` bounds the agent's internal turns.
2927
+ * Event projection, usage folding, and the terminal `final` contract
2928
+ * are identical to the `box` kind.
2929
+ */
2930
+ kind: 'box-task';
2931
+ box: SandboxInstance;
2932
+ /**
2933
+ * Per-task `TaskOptions` forwarded verbatim to `streamTask`
2934
+ * (`maxTurns` plus every `PromptOptions` field). The turn's derived
2935
+ * abort signal is always installed as `signal`.
2936
+ */
2937
+ options?: Omit<TaskOptions, 'signal'>;
2797
2938
  /** Model label stamped on cost-only `llm_call` events. Default `'agent'`. */
2798
2939
  agentRunName?: string;
2799
2940
  } | {
@@ -2825,6 +2966,25 @@ interface StreamAgentTurnOptions {
2825
2966
  * (a blown deadline is a turn failure, not a caller cancellation).
2826
2967
  */
2827
2968
  timeoutMs?: number;
2969
+ /**
2970
+ * Opt-in tool-part projection for box-kind backends (`box`, `box-task`,
2971
+ * `executor`): sandbox tool parts additionally surface in-stream as
2972
+ * `tool_call` / `tool_result` events (`mapSandboxToolEvent`), so a consumer
2973
+ * rendering tool activity needs no bespoke sandbox-event parser. Default
2974
+ * off — the stream vocabulary existing consumers see is unchanged. No-op
2975
+ * for the `chat` kind (its backend emits `RuntimeStreamEvent`s directly,
2976
+ * tool events included when the backend produces them).
2977
+ */
2978
+ preserveToolParts?: boolean;
2979
+ /**
2980
+ * Raw-event tap for box-kind backends: called (and awaited) with every
2981
+ * unmapped `SandboxEvent` BEFORE it is projected, so a consumer can read
2982
+ * parts the chat-UX projection drops (part ids, step markers, custom
2983
+ * backend events) without forking the mapper. Purely observational — it
2984
+ * cannot alter the mapped stream. Never called for the `chat` kind, which
2985
+ * has no sandbox events.
2986
+ */
2987
+ onRawEvent?: (event: SandboxEvent) => void | Promise<void>;
2828
2988
  }
2829
2989
  /**
2830
2990
  * Metered usage of one turn, summed over every cost-bearing event the backend
@@ -3908,4 +4068,4 @@ declare function runInWorkspace<T>(ws: Workspace, body: (cwd: string) => Promise
3908
4068
  commitOnInvalid?: boolean;
3909
4069
  }): Promise<WorkspaceRun<T>>;
3910
4070
 
3911
- export { Agent, AgentRunSpec, type AgentTurnBackend, type AgentTurnUsage, type AgenticOptions, type AgenticRunResult, type AgenticSurface, type AgenticTask, type AgenticTool, AnalystRegistry, type AnytimeReport, type AnytimeStrategySummary, type AnytimeTaskCurve, type ApplyContinuation, type ArtifactHandle, AssertTraceDerivedFindings, type AuditIntentInput, type AuditIntentOptions, type AuthorStrategyOptions, type AuthoredProfile, type AuthoredStrategy, type BenchmarkCell, type BenchmarkConfig, type BenchmarkLift, type BenchmarkReport, type BenchmarkStrategySummary, type BenchmarkTaskRow, Budget, type BudgetPool, type BudgetReadout, type ChampionPick, type ChampionPolicy, type CheckpointCapableBox, type CollectedAgentTurn, CombinatorShape, type CompletionAnalyst, type CompletionEvidence, type CompletionPolicy, type CompletionVerdict, CoordinationEvent, type CoordinationMcpHandle, Corpus, CorpusFilter, CorpusRecord, type CreateScopeAnalystOptions, type CriuCapableClient, DefinePersonaInput, type DefinedLeaderboard, type DelegateOptions, type Deliverable, DeliverableSpec, type DriveHarness, Driver, type DriverAgentOptions, type DumbDriverOptions, type Environment, EqualKArm, EqualKOnCostOptions, EqualKVerdict, type EvolutionArchiveNode, type EvolutionAuthor, type EvolutionBandInfo, type EvolutionCandidate, type EvolutionGeneration, type EvolutionReport, ExecCtx, ExecutorConfig, ExecutorFactory, ExecutorRegistry, FanoutOptions, FanoutWinnerSelector, FileCorpus, type ForkCapableBox, type GitWorkspaceOptions, type HarvestCorpusOptions, type HarvestFailure, type HarvestReport, InMemoryCorpus, InMemoryResultBlobStore, type InMemoryRunContext, type InMemoryRunContextOptions, InMemorySpawnJournal, type InProcessOnPrompt, type InProcessPromptCtx, type InProcessSandboxClientOptions, type Inbox, type InboxMessage, type IntentAudit, type Interval, Iteration, type Leaderboard, type LeaderboardBenchScore, type LeaderboardBenchTask, type LeaderboardBenchmarkAdapter, type LeaderboardFlagSpec, type LeaderboardOptions, type LeaderboardRow, type LeaderboardRunContext, type LeaderboardScenario, type LeaderboardScore, type LeaderboardSpec, type LoopCampaignDispatchOptions, type LoopDispatchOptions, LoopLineageOptions, type LoopOptionsForDispatch, LoopResult, LoopShape, LoopTokenUsage, LoopUntilSpec, LoopWinner, MakeWorkerAgent, type McpEndpoint, type McpEnvironmentOptions, MountRecorder, type NaiveDriverOptions, type Observation, type ObserveInput, type ObserveOptions, type OpenSandboxRunOptions, Outcome, OutputAdapter, type PairwiseOptions, type PairwiseVerdict, PanelSpec, Persona, PipelineStage, type ProfileRichness, type ProfileRichnessThresholds, type PromotionGateOptions, type PromotionVerdict, type RegistryAnalyzeProjection, RenderCorpusToInstructionsOptions, type ReservationTicket, type ResolveSandboxClientOptions, ResultBlobStore, RouterConfig, type RunAgenticOptions, RunPersonifiedOptions, type SandboxCapabilities, SandboxClient, type SandboxLineage, type SandboxLineageHandle, type SandboxRun, SandboxRunAbortError, Scope, ScopeAnalyst, ScopeAnalyzeInput, ScopeWidenGate, type SessionCapableBox, type SessionTraceBox, Settled, ShapeRegistry, type Shell, type ShotPersona, type ShotSpec, Spend, SteerContext, type SteeringDecision, type Strategy, type StrategyCtx, type StrategyEvolutionConfig, type StrategyResult, type StreamAgentTurnOptions, type SuperviseOptions, type SuperviseSurfaceOptions, type SuperviseSurfaceResult, SupervisedResult, Supervisor, type SupervisorAgentDeps, type SupervisorProfile, type SurfaceScore, type SurfaceWorkerConfig, type SurfaceWorkerOut, ToolLoopChat, ToolLoopCompactionOptions, type TraceSource, type TrajectoryAnalysis, TrajectoryReport, TrajectoryReportOptions, type TurnResult, UsageEvent, type UsageSink, Validator, type VerifierEnvironmentOptions, VerifySpec, type WatchTraceOptions, type WaterfallCollector, type WaterfallReport, type WaterfallSpan, WidenSpec, WinnerStrategy, type Workspace, type WorkspaceCommit, type WorkspaceRun, acquireSandbox, adaptiveRefine, analyzeTrace, anytimeReport, asAuthoredProfile, assertModelAllowed, assertStrategyContract, assertTraceDerivedFindings, assessAuthoredProfile, auditIntent, authorStrategy, authoredWorker, breadthStrategy, buildSteerContext, builtinShapes, collectAgentTurn, completionAuthorizes, contentAddress, createBudgetPool, createInMemoryRunContext, createInbox, createMcpEnvironment, createPushTraceSource, createSandboxLineage, createScope, createScopeAnalyst, createShapeRegistry, createSupervisor, createVerifierEnvironment, createWaterfallCollector, decodeToolPart, defaultAnalystInstruction, defaultAuditorInstruction, defaultDelegateBudget, defaultProfileRichnessThresholds, defaultSelectWinner, defaultToolDetectors, defineLeaderboard, definePersona, defineStrategy, delegate, depthStrategy, deterministicCompletion, discriminatingMeans, driverAgent, dumbDriver, equalKOnCost, extractLlmCallEvent, failuresAnalyst, fanout, finalizeBestDelivered, flatWidenGate, gitWorkspace, harvestCorpus, inProcessSandboxClient, inlineSandboxClient, jjWorkspace, leaderboard, localShell, loopCampaignDispatch, loopDispatch, loopUntil, mapSandboxEvent, naiveDriver, observe, openSandboxRun, pairwiseSignificance, panel, pickChampion, pipeline, printBenchmarkReport, probeSandboxCapabilities, profileRichnessFinding, promotionGate, refine, registerShape, registryScopeAnalyst, renderAnytimeTable, renderCorpusToInstructions, renderLeaderboardHtml, renderLeaderboardMarkdown, renderLeaderboardSvg, renderPairwiseMarkdown, renderReport, reportLoopUsage, resolveSandboxClient, runAgentic, runBenchmark, runInWorkspace, runLoop, runPersonified, runStrategyEvolution, sample, sampleThenRefine, sandboxSessionTraceSource, selectChampion, selectValidWinner, sentinelCompletion, serveCoordinationMcp, settledToIteration, spendFromUsageEvents, stopSentinel, strategyAuthorContract, streamAgentTurn, sumSandboxUsage, supervise, superviseSurface, supervisorAgent, supervisorInstructions, trajectoryReport, verify, watchTrace, widen, workerFromBackend };
4071
+ export { Agent, AgentRunSpec, type AgentTurnBackend, type AgentTurnUsage, type AgenticOptions, type AgenticRunResult, type AgenticSurface, type AgenticTask, type AgenticTool, AnalystRegistry, type AnytimeReport, type AnytimeStrategySummary, type AnytimeTaskCurve, type ApplyContinuation, type ArtifactHandle, AssertTraceDerivedFindings, type AuditIntentInput, type AuditIntentOptions, type AuthorStrategyOptions, type AuthoredProfile, type AuthoredStrategy, type BenchmarkCell, type BenchmarkConfig, type BenchmarkLift, type BenchmarkReport, type BenchmarkStrategySummary, type BenchmarkTaskRow, Budget, type BudgetPool, type BudgetReadout, type ChampionPick, type ChampionPolicy, type CheckpointCapableBox, type CollectedAgentTurn, CombinatorShape, type CompletionAnalyst, type CompletionEvidence, type CompletionPolicy, type CompletionVerdict, CoordinationEvent, type CoordinationMcpHandle, Corpus, CorpusFilter, CorpusRecord, type CreateScopeAnalystOptions, type CriuCapableClient, DefinePersonaInput, type DefinedLeaderboard, type DelegateOptions, type Deliverable, DeliverableSpec, type DriveHarness, Driver, type DriverAgentOptions, type DumbDriverOptions, type Environment, EqualKArm, EqualKOnCostOptions, EqualKVerdict, type EvolutionArchiveNode, type EvolutionAuthor, type EvolutionBandInfo, type EvolutionCandidate, type EvolutionGeneration, type EvolutionReport, ExecCtx, ExecutorConfig, ExecutorFactory, ExecutorRegistry, FanoutOptions, FanoutWinnerSelector, FileCorpus, type ForkCapableBox, type GitWorkspaceOptions, type HarvestCorpusOptions, type HarvestFailure, type HarvestReport, InMemoryCorpus, InMemoryResultBlobStore, type InMemoryRunContext, type InMemoryRunContextOptions, InMemorySpawnJournal, type InProcessOnPrompt, type InProcessPromptCtx, type InProcessSandboxClientOptions, type Inbox, type InboxMessage, type IntentAudit, type Interval, Iteration, type Leaderboard, type LeaderboardBenchScore, type LeaderboardBenchTask, type LeaderboardBenchmarkAdapter, type LeaderboardFlagSpec, type LeaderboardIterationInfo, type LeaderboardOptions, type LeaderboardRow, type LeaderboardRunContext, type LeaderboardScenario, type LeaderboardScore, type LeaderboardSpec, type LoopCampaignDispatchOptions, type LoopDispatchOptions, LoopLineageOptions, type LoopOptionsForDispatch, LoopResult, LoopShape, LoopTokenUsage, LoopUntilSpec, LoopWinner, MakeWorkerAgent, type McpEndpoint, type McpEnvironmentOptions, MountRecorder, type NaiveDriverOptions, type Observation, type ObserveInput, type ObserveOptions, type OpenSandboxRunOptions, Outcome, OutputAdapter, type PairwiseOptions, type PairwiseVerdict, PanelSpec, Persona, PipelineStage, type ProfileRichness, type ProfileRichnessThresholds, type PromotionGateOptions, type PromotionVerdict, type RegistryAnalyzeProjection, RenderCorpusToInstructionsOptions, type ReservationTicket, type ResolveSandboxClientOptions, ResultBlobStore, RouterConfig, type RunAgenticOptions, RunPersonifiedOptions, type SandboxCapabilities, SandboxClient, type SandboxLineage, type SandboxLineageHandle, type SandboxRun, SandboxRunAbortError, type SandboxToolPartState, Scope, ScopeAnalyst, ScopeAnalyzeInput, ScopeWidenGate, type SessionCapableBox, type SessionTraceBox, Settled, ShapeRegistry, type Shell, type ShotPersona, type ShotSpec, Spend, SteerContext, type SteeringDecision, type Strategy, type StrategyCtx, type StrategyEvolutionConfig, type StrategyResult, type StreamAgentTurnOptions, type SuperviseOptions, type SuperviseSurfaceOptions, type SuperviseSurfaceResult, SupervisedResult, Supervisor, type SupervisorAgentDeps, type SupervisorProfile, type SurfaceScore, type SurfaceWorkerConfig, type SurfaceWorkerOut, ToolLoopChat, ToolLoopCompactionOptions, type TraceSource, type TrajectoryAnalysis, TrajectoryReport, TrajectoryReportOptions, type TurnResult, UsageEvent, type UsageSink, Validator, type VerifierEnvironmentOptions, VerifySpec, type WatchTraceOptions, type WaterfallCollector, type WaterfallReport, type WaterfallSpan, WidenSpec, WinnerStrategy, type Workspace, type WorkspaceCommit, type WorkspaceRun, acquireSandbox, adaptiveRefine, analyzeTrace, anytimeReport, asAuthoredProfile, assertModelAllowed, assertStrategyContract, assertTraceDerivedFindings, assessAuthoredProfile, auditIntent, authorStrategy, authoredWorker, breadthStrategy, buildSteerContext, builtinShapes, collectAgentTurn, completionAuthorizes, contentAddress, createBudgetPool, createInMemoryRunContext, createInbox, createMcpEnvironment, createPushTraceSource, createSandboxLineage, createSandboxToolPartState, createScope, createScopeAnalyst, createShapeRegistry, createSupervisor, createVerifierEnvironment, createWaterfallCollector, decodeToolPart, defaultAnalystInstruction, defaultAuditorInstruction, defaultDelegateBudget, defaultProfileRichnessThresholds, defaultSelectWinner, defaultToolDetectors, defineLeaderboard, definePersona, defineStrategy, delegate, depthStrategy, deterministicCompletion, discriminatingMeans, driverAgent, dumbDriver, equalKOnCost, extractLlmCallEvent, failuresAnalyst, fanout, finalizeBestDelivered, flatWidenGate, gitWorkspace, harvestCorpus, inProcessSandboxClient, inlineSandboxClient, jjWorkspace, leaderboard, localShell, loopCampaignDispatch, loopDispatch, loopUntil, mapSandboxEvent, mapSandboxToolEvent, naiveDriver, observe, openSandboxRun, pairwiseSignificance, panel, pickChampion, pipeline, printBenchmarkReport, probeSandboxCapabilities, profileRichnessFinding, promotionGate, refine, registerShape, registryScopeAnalyst, renderAnytimeTable, renderCorpusToInstructions, renderLeaderboardHtml, renderLeaderboardMarkdown, renderLeaderboardSvg, renderPairwiseMarkdown, renderReport, reportLoopUsage, resolveSandboxClient, runAgentic, runBenchmark, runInWorkspace, runLoop, runPersonified, runStrategyEvolution, sample, sampleThenRefine, sandboxSessionTraceSource, selectChampion, selectValidWinner, sentinelCompletion, serveCoordinationMcp, settledToIteration, spendFromUsageEvents, stopSentinel, strategyAuthorContract, streamAgentTurn, sumSandboxUsage, supervise, superviseSurface, supervisorAgent, supervisorInstructions, trajectoryReport, verify, watchTrace, widen, workerFromBackend };
package/dist/loops.js CHANGED
@@ -89,7 +89,7 @@ import {
89
89
  watchTrace,
90
90
  widen,
91
91
  worktreeFanout
92
- } from "./chunk-DLAEEF26.js";
92
+ } from "./chunk-VKVNDNG4.js";
93
93
  import {
94
94
  InMemoryResultBlobStore,
95
95
  InMemorySpawnJournal,
@@ -131,7 +131,7 @@ import {
131
131
  supervisorAgent,
132
132
  supervisorInstructions,
133
133
  workerFromBackend
134
- } from "./chunk-XN5TIJGP.js";
134
+ } from "./chunk-6XCW3M7W.js";
135
135
  import "./chunk-UD4BHQMI.js";
136
136
  import {
137
137
  createAgentEnvironmentProviderRegistry,
@@ -141,10 +141,12 @@ import {
141
141
  sandboxClientAsProvider
142
142
  } from "./chunk-BZF3KQ6G.js";
143
143
  import {
144
+ createSandboxToolPartState,
144
145
  extractLlmCallEvent,
145
146
  mapSandboxEvent,
147
+ mapSandboxToolEvent,
146
148
  sumSandboxUsage
147
- } from "./chunk-4J6RBI3K.js";
149
+ } from "./chunk-QUXZBZCS.js";
148
150
  import "./chunk-7LO5GMAO.js";
149
151
  import "./chunk-YEJR7IXO.js";
150
152
  import "./chunk-DPEUKJRO.js";
@@ -185,6 +187,7 @@ export {
185
187
  createMcpEnvironment,
186
188
  createPushTraceSource,
187
189
  createSandboxLineage,
190
+ createSandboxToolPartState,
188
191
  createScope,
189
192
  createScopeAnalyst,
190
193
  createShapeRegistry,
@@ -227,6 +230,7 @@ export {
227
230
  loopUntil,
228
231
  makeFinding,
229
232
  mapSandboxEvent,
233
+ mapSandboxToolEvent,
230
234
  naiveDriver,
231
235
  observe,
232
236
  openSandboxRun,
package/dist/mcp/bin.js CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  DelegationTaskQueue,
10
10
  FileDelegationStore,
11
11
  createMcpServer
12
- } from "../chunk-XN5TIJGP.js";
12
+ } from "../chunk-6XCW3M7W.js";
13
13
  import "../chunk-UD4BHQMI.js";
14
14
  import "../chunk-BZF3KQ6G.js";
15
- import "../chunk-4J6RBI3K.js";
15
+ import "../chunk-QUXZBZCS.js";
16
16
  import "../chunk-7LO5GMAO.js";
17
17
  import "../chunk-YEJR7IXO.js";
18
18
  import "../chunk-DPEUKJRO.js";
package/dist/mcp/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  mcpToolsForRuntimeMcp,
8
8
  mcpToolsForRuntimeMcpSubset
9
- } from "../chunk-G7HQI6DB.js";
9
+ } from "../chunk-MHYF2LIP.js";
10
10
  import {
11
11
  createKbGate
12
12
  } from "../chunk-SGKPNBXE.js";
@@ -14,7 +14,7 @@ import {
14
14
  assertTraceDerivedFindings,
15
15
  runCoderChecks,
16
16
  selectValidWinner
17
- } from "../chunk-DLAEEF26.js";
17
+ } from "../chunk-VKVNDNG4.js";
18
18
  import {
19
19
  DELEGATE_DESCRIPTION,
20
20
  DELEGATE_FEEDBACK_DESCRIPTION,
@@ -64,7 +64,7 @@ import {
64
64
  validateDelegateUiAuditArgs,
65
65
  validateDelegationHistoryArgs,
66
66
  validateDelegationStatusArgs
67
- } from "../chunk-XN5TIJGP.js";
67
+ } from "../chunk-6XCW3M7W.js";
68
68
  import "../chunk-UD4BHQMI.js";
69
69
  import {
70
70
  deleteBoxSafe,
@@ -72,7 +72,7 @@ import {
72
72
  throwAbort,
73
73
  throwIfAborted
74
74
  } from "../chunk-BZF3KQ6G.js";
75
- import "../chunk-4J6RBI3K.js";
75
+ import "../chunk-QUXZBZCS.js";
76
76
  import {
77
77
  runLocalHarness
78
78
  } from "../chunk-7LO5GMAO.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-runtime",
3
- "version": "0.85.0",
3
+ "version": "0.87.0",
4
4
  "description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
5
5
  "homepage": "https://github.com/tangle-network/agent-runtime#readme",
6
6
  "repository": {
@@ -64,8 +64,9 @@ agent-runtime's `AgentSpec { profile, harness }`. To sweep it as an eval axis,
64
64
  don't hand-declare a harness list — expand one base profile across
65
65
  `CODING_HARNESSES` with `expandProfileAxes` (agent-eval), run with
66
66
  `runProfileMatrix`, and pivot results by the stamped `AgentProfileCell`
67
- (`groupRunsByAgentProfileCell`); incompatible `(harness, model)` pairs drop via
68
- `harnessSupportsModel`.
67
+ (`groupRunsByAgentProfileCell`); `harnessSupportsModel` filters per harness,
68
+ and a vendor-locked harness that supports none of the requested models SNAPS
69
+ to its native default (`HARNESS_NATIVE_MODEL`) — never silently dropped.
69
70
 
70
71
  | Altitude — I want to… | Use | Source |
71
72
  |---|---|---|
@@ -80,6 +81,10 @@ don't hand-declare a harness list — expand one base profile across
80
81
  | **Add a stateful tool-using domain** | implement `AgenticSurface` (5 hooks) — `/loops` | canonical-api §3.3 |
81
82
  | **Drive a team of agents over a graded `AgenticSurface` task** (workers settle on its check, driver self-improves from the failing tests) | `superviseSurface(profile, task, { surface, worker })` — `/loops` | canonical-api §2 |
82
83
  | **Benchmark: compare strategies + significance + Pareto on a domain** | `runBenchmark({ environment, tasks, worker, strategies })` — `/loops` | canonical-api §3.3 |
84
+ | **Author a PRODUCT eval leaderboard** (cases + prompt + grader → ranked board, standard flags, fresh run-dir, export, `toBenchmarkAdapter()`) | `defineLeaderboard({ name, cases, prompt, score, axis?, backends?, flags?, setup?/teardown?, onCellEvents?, resolveModel?, export?, dispatch?, judges?, matrix? })` — `/loops` — NOT a hand-assembled flag-parsing + `runProfileMatrix` frame; `runProfileMatrix` is the escape floor, the level-2 `dispatch` override is how in-process products plug in | `src/runtime/define-leaderboard.ts` (verify vs source) |
85
+ | **Resolve a harness-in-box backend** (box / local cli-bridge / router leaf, one `SandboxClient` shape) | `resolveSandboxClient({ backend: 'sandbox' \| 'bridge' \| 'router' })` — `/loops` — NOT a per-product backend factory or a hand-faked box (`inlineSandboxClient` / the bridge executor already exist) | `src/runtime/resolve-sandbox-client.ts` |
86
+ | **Resolve an in-process chat backend** (the one `--backend` branch for `runChatThroughRuntime` / `runAgentTaskStream`) | `resolveAgentBackend({ kind: 'router' \| 'tcloud' \| 'cli-bridge' \| 'sandbox' })` — root `.` | `src/resolve-agent-backend.ts` |
87
+ | **Run ONE agent turn as one normalized event stream** (box, executor, or chat backend; guaranteed terminal result+usage) | `streamAgentTurn(backend, prompt, { signal, timeoutMs })` + `collectAgentTurn(stream)` — `/loops` | canonical-api §2 |
83
88
  | **Benchmark report: multi-profile × multi-axis leaderboard** (ranked board + score matrix + SVG/HTML charts, any `RunRecord[]`) | `leaderboard(records)` + `renderLeaderboardMarkdown` / `renderLeaderboardSvg` / `renderLeaderboardHtml` — `/loops` | canonical-api §2 |
84
89
  | **Meter one `openSandboxRun` cell's token/cost usage** | `sumSandboxUsage(events)` — `/loops` | canonical-api §2 |
85
90
  | **Sweep harness × model as an eval axis** (turn one base profile into the full harness × model set) | `expandProfileAxes({ base, harnesses, models })` over `CODING_HARNESSES` → `runProfileMatrix(...)`, pivot with `groupRunsByAgentProfileCell` — `agent-eval` root — NOT a hand-declared `HARNESSES` list | agent-eval root (verify vs source) |
@@ -125,10 +130,24 @@ holds the load-bearing invariant the parallel breaks:
125
130
  (seeded, identical run-to-run; never report a point lift without `low/high/pairs`).
126
131
  - a per-product `HARNESSES` / `HarnessBackend` list + a metadata-harness reader
127
132
  **≈** `CODING_HARNESSES` + `expandProfileAxes` (the one canonical harness list;
128
- incompatible `(harness, model)` pairs drop via `harnessSupportsModel`) and the
133
+ vendor-locked harnesses SNAP to their native model via `HARNESS_NATIVE_MODEL`,
134
+ never dropped) and the
129
135
  `AgentProfileCell` stamped by `runProfileMatrix`, pivoted via
130
136
  `groupRunsByAgentProfileCell` — never bake the harness into the model id so the
131
137
  same model can run under multiple harnesses.
138
+ - a per-product leaderboard CLI (flag parsing + run-dir management + axis
139
+ expansion + a `runProfileMatrix` call + export/markdown) **≈**
140
+ `defineLeaderboard` (0.84+; it owns that whole frame — FRESH default run-dir,
141
+ standard `--backend`/`--harnesses`/`--models`/`--cases`/`--shots`/`--reps`
142
+ flags, `toBenchmarkAdapter()`; the product writes ~150-250 domain lines).
143
+ - a backend factory / `if (backend === 'router') ... else ...` branch or a
144
+ hand-faked box around a non-box executor **≈** `resolveSandboxClient`
145
+ (harness-in-box: `'sandbox' | 'bridge' | 'router'`) or `resolveAgentBackend`
146
+ (in-process: `'router' | 'tcloud' | 'cli-bridge' | 'sandbox'`) — grep the
147
+ substrate first; `inlineSandboxClient` and the bridge executor exist.
148
+ - a per-provider stream→event mapper for a single agent turn **≈**
149
+ `streamAgentTurn` + `collectAgentTurn` (0.85+; one `RuntimeStreamEvent`
150
+ contract over box / executor / chat, guaranteed terminal result+usage).
132
151
 
133
152
  ## End-to-end recipe
134
153
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/runtime/sandbox-events.ts"],"sourcesContent":["/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n },\n agentRunName,\n )\n }\n return undefined\n}\n\n/**\n * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an\n * `openSandboxRun` cell. Folds `extractLlmCallEvent` over the stream (which reads usage off EVERY backend\n * event shape), so a `runProfileMatrix` dispatch can report it to `ctx.cost`:\n *\n * const turn = await run.start(prompt)\n * const u = sumSandboxUsage(turn.events)\n * if (u.input || u.output) ctx.cost.observeTokens({ input: u.input, output: u.output })\n * if (u.costUsd) ctx.cost.observe(u.costUsd, 'sandbox-cell')\n *\n * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the\n * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).\n */\nexport function sumSandboxUsage(\n events: readonly SandboxEvent[],\n agentRunName = 'agent',\n): { input: number; output: number; costUsd: number } {\n let input = 0\n let output = 0\n let costUsd = 0\n for (const ev of events) {\n const call = extractLlmCallEvent(ev, agentRunName)\n if (!call) continue\n input += call.tokensIn ?? 0\n output += call.tokensOut ?? 0\n costUsd += call.costUsd ?? 0\n }\n return { input, output, costUsd }\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const tokensOut = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const costUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n if (tokensIn === undefined && tokensOut === undefined && costUsd === undefined) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (costUsd !== undefined) event.costUsd = costUsd\n return event\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n"],"mappings":"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","names":[]}