@oneharness/sdk 0.3.24 → 0.4.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.
@@ -0,0 +1,438 @@
1
+ /**
2
+ * One line of `oneharness run --stream` output.
3
+ *
4
+ * Event lines carry normalized actions as they arrive. Exactly one terminal
5
+ * result line carries the complete report unless the consumer closes the
6
+ * stream early. This is an output contract, so deserialization deliberately
7
+ * tolerates additive fields from newer producers.
8
+ */
9
+ export type RunStreamEnvelope = {
10
+ event: ActionEvent;
11
+ type: "event";
12
+ [k: string]: unknown;
13
+ } | {
14
+ report: RunReport;
15
+ type: "result";
16
+ [k: string]: unknown;
17
+ };
18
+ /**
19
+ * The normalized, closed set of failure reasons oneharness can classify from a
20
+ * harness's output. It is the single source for the `failure_kind` contract
21
+ * value: serialized as the snake_case token a consumer reads in the report
22
+ * (`auth`, `rate_limit`, `model_not_found`, `quota`, `tool_deferred`), so the
23
+ * wire shape is unchanged — modeling it as an enum keeps a misspelled or
24
+ * invalid kind unrepresentable and gives every producer/consumer (classifier,
25
+ * `is_failure`, the fallback fall-through rule, the report, history) one
26
+ * definition to share instead of scattered string literals.
27
+ */
28
+ export type FailureKind = "auth" | "rate_limit" | "model_not_found" | "quota" | "tool_deferred";
29
+ /**
30
+ * How a harness emits its result, which decides how `text` is extracted.
31
+ *
32
+ * Also accepted as a CLI value (`--output-format`, parsed in the `oneharness`
33
+ * binary) and a config-file value (`output_format`, via `Deserialize`). The
34
+ * CLI parsing lives in the binary so this core crate stays free of `clap`.
35
+ */
36
+ export type OutputFormat = "text" | "json" | "stream-json";
37
+ /**
38
+ * The outcome of attempting to run one harness.
39
+ */
40
+ export type Status = "ok" | "nonzero" | "timeout" | "spawn-error" | "skipped" | "planned";
41
+ /**
42
+ * One normalized action a harness took, harness-agnostic so a single consumer
43
+ * assertion works across harnesses. Every field is always serialized (null when
44
+ * absent) so the shape is stable, mirroring the `usage` contract.
45
+ */
46
+ export interface ActionEvent {
47
+ /**
48
+ * Position of this event within the run, so "≤ N tool calls" and "did X
49
+ * before Y" are expressible from a stable ordering (also array order).
50
+ */
51
+ index: number;
52
+ /**
53
+ * Structured, tool-shaped arguments (the command string, the file path),
54
+ * so a consumer asserts on specific args without re-parsing; `null` when the
55
+ * event carries none (e.g. a `tool_result`).
56
+ */
57
+ input: unknown;
58
+ /**
59
+ * The kind of event: `tool_call` (the model invoked a tool) or
60
+ * `tool_result` (the observation returned to the model). Left open for
61
+ * future kinds rather than an enum, so a new shape never breaks the field.
62
+ */
63
+ kind: string;
64
+ /**
65
+ * Normalized tool name where knowable (e.g. `bash`, `Edit`); `null` for a
66
+ * `tool_result`, or when the harness did not name the tool.
67
+ */
68
+ name: string | null;
69
+ /**
70
+ * The result/observation text, when the trace exposes it; `null` otherwise.
71
+ */
72
+ output: string | null;
73
+ [k: string]: unknown;
74
+ }
75
+ /**
76
+ * The top-level `run` report written to stdout.
77
+ */
78
+ export interface RunReport {
79
+ /**
80
+ * Same-prefix batch metadata when this run fanned **one** harness over more
81
+ * than one prompt; `null` on an ordinary run. Its presence is the signal a
82
+ * consumer keys on to read each result's own `prompt`.
83
+ */
84
+ batch: BatchReport | null;
85
+ /**
86
+ * Back-compat convenience: `true` exactly when `permission_mode` is
87
+ * `bypass`. Retained so existing consumers keep working; new consumers
88
+ * should read `permission_mode`.
89
+ */
90
+ bypass_permissions: boolean;
91
+ /**
92
+ * Config files that shaped this run, in layering order (user first,
93
+ * project last); empty under `--no-config` or when none exist.
94
+ */
95
+ config_files: string[];
96
+ dry_run: boolean;
97
+ /**
98
+ * Fallback-mode metadata when this run drove the selected harnesses in
99
+ * priority order, stopping at the first that ran (`--run-mode fallback`);
100
+ * `null` on a parallel run (and under `--print-command`, where nothing
101
+ * executes). Its presence tells a consumer that `results` holds only the
102
+ * harnesses actually *attempted* — the fallen-through ones in order, then
103
+ * the one that ran — not every selected harness.
104
+ */
105
+ fallback: FallbackReport | null;
106
+ /**
107
+ * Whether the resumed session was forked (`--fork`) rather than appended to.
108
+ * `false` unless `--resume` was given with `--fork`.
109
+ */
110
+ fork: boolean;
111
+ /**
112
+ * The history session file this run streamed normalized records to
113
+ * (absolute); `null` when history was not enabled (or under `--print-command`,
114
+ * where nothing runs). The programmatic handle a consumer captures to read the
115
+ * session back later with `oneharness history show`.
116
+ */
117
+ history_file: string | null;
118
+ /**
119
+ * The parsed `--mock-rules` ruleset this run was intercepted with; `null`
120
+ * when no mocking was requested. Present so a consumer can tell a mocked
121
+ * run's report from a clean one without out-of-band state.
122
+ */
123
+ mock_rules: unknown;
124
+ /**
125
+ * The effective top-level model: the first of the fan-out `models` list when
126
+ * one was given, else the single configured/CLI model, else `null`. Each
127
+ * result's own `model` is authoritative on a fan-out run.
128
+ */
129
+ model: string | null;
130
+ /**
131
+ * The model fan-out list this run multiplied over (repeated `--model` /
132
+ * config `models`), or `null` on an ordinary single-model run. Its presence
133
+ * is the signal a consumer keys on to read each result's own `model`: in
134
+ * `parallel` mode `results` holds one entry per (harness, model) pair; in
135
+ * `fallback` mode the pairs were tried in priority order (harness-major,
136
+ * model-minor).
137
+ */
138
+ models: string[] | null;
139
+ oneharness_version: string;
140
+ /**
141
+ * The normalized approval mode requested for this run (see the README
142
+ * support matrix). Each harness maps it to its own mechanism.
143
+ */
144
+ permission_mode: "read-only" | "plan" | "default" | "edit" | "auto" | "bypass";
145
+ /**
146
+ * The prompt sent. On an ordinary run this is *the* prompt every result
147
+ * shares; on a **batch** run (see `batch`) it repeats the first prompt for
148
+ * back-compat, and each result's own `prompt` field is authoritative.
149
+ */
150
+ prompt: string;
151
+ results: RunResult[];
152
+ /**
153
+ * The session id being continued, when `--resume` was passed; else `null`.
154
+ */
155
+ resume: string | null;
156
+ /**
157
+ * The JSON Schema applied to this run (structured output), or `null` when
158
+ * none was requested. Echoed so a consumer sees the exact constraint each
159
+ * result was validated against.
160
+ */
161
+ schema: unknown;
162
+ /**
163
+ * Maximum retries allowed per harness under the validate/retry loop; `null`
164
+ * when no schema was requested.
165
+ */
166
+ schema_max_retries: number | null;
167
+ schema_version: string;
168
+ /**
169
+ * The uniform session handle in play (`--session <name>`), or `null` when
170
+ * none was requested. Lets a consumer thread one stable name across turns
171
+ * instead of extracting each harness's native session id. Distinct from the
172
+ * low-level `resume` field above, which echoes an explicit `--resume` id.
173
+ */
174
+ session: SessionReport | null;
175
+ /**
176
+ * The spy-log path the mock hook appended tool-call records to (absolute);
177
+ * `null` when none was requested.
178
+ */
179
+ spy_file: string | null;
180
+ [k: string]: unknown;
181
+ }
182
+ /**
183
+ * Metadata for a same-prefix batch run (one harness, N prompts sharing a
184
+ * cacheable prefix). Present on [`RunReport::batch`] only in that mode.
185
+ */
186
+ export interface BatchReport {
187
+ /**
188
+ * Whether the fan-out actually **forked** the warm-up's session to reuse its
189
+ * cached prefix (`min-tokens` on a fork-capable harness whose warm-up exposed
190
+ * a session id). `false` for `speed`, for `min-tokens` on a harness that
191
+ * cannot fork, or when the warm-up exposed no session to fork. When `true`,
192
+ * the fan-out results' `command` carries the resume/fork flags and their
193
+ * `usage.cache_read_tokens` reflect the reused prefix.
194
+ */
195
+ forked: boolean;
196
+ /**
197
+ * How many prompts were run (equals `results.len()`).
198
+ */
199
+ prompt_count: number;
200
+ /**
201
+ * How the prompts were scheduled across the parallel runner.
202
+ */
203
+ strategy: "speed" | "min-tokens";
204
+ [k: string]: unknown;
205
+ }
206
+ /**
207
+ * Metadata for a fallback run (harnesses tried in priority order until one
208
+ * runs). Present on [`RunReport::fallback`] only in that mode. The per-harness
209
+ * detail lives in `results`; this block summarizes the outcome so a consumer
210
+ * need not re-derive it from statuses.
211
+ */
212
+ export interface FallbackReport {
213
+ /**
214
+ * The candidates fallen through because they could not run the task at all,
215
+ * in priority order, each with why (`not-installed`, `spawn-error`, `auth`,
216
+ * `quota`, and — on a model fan-out — `model-not-found` / `rate-limit`; see
217
+ * [`crate::domain::fallback::startup_failure_reason`]).
218
+ */
219
+ fell_through: FallThrough[];
220
+ /**
221
+ * The harness that actually ran the task (the run stopped there), or `null`
222
+ * when no candidate could run at all — every one was a startup failure.
223
+ */
224
+ ran: string | null;
225
+ [k: string]: unknown;
226
+ }
227
+ /**
228
+ * One candidate a fallback run fell through, with the reason it could not run.
229
+ */
230
+ export interface FallThrough {
231
+ /**
232
+ * Canonical harness id.
233
+ */
234
+ harness: string;
235
+ /**
236
+ * Short reason token (`not-installed` / `spawn-error` / `auth` / `quota` /
237
+ * `model-not-found` / `rate-limit`).
238
+ */
239
+ reason: string;
240
+ [k: string]: unknown;
241
+ }
242
+ /**
243
+ * One harness's entry in the report.
244
+ */
245
+ export interface RunResult {
246
+ /**
247
+ * Whether that binary was found.
248
+ */
249
+ available: boolean;
250
+ /**
251
+ * The binary name or path oneharness resolved and would invoke.
252
+ */
253
+ bin: string;
254
+ /**
255
+ * The exact argv oneharness built (argv[0] is the binary).
256
+ */
257
+ command: string[];
258
+ /**
259
+ * Wall-clock duration of the run; `null` when not executed.
260
+ */
261
+ duration_ms: number | null;
262
+ /**
263
+ * Human-readable problem + suggested action; `null` on success.
264
+ */
265
+ error: string | null;
266
+ /**
267
+ * Best-effort normalized tool-call / action events the harness took (shell
268
+ * commands, file edits, tool uses), in order — so consumers can assert on
269
+ * *behavior*, not just the final `text`. `null` when the harness's output
270
+ * exposes no machine-readable trace (a plain-text harness, or Claude Code's
271
+ * single-document `json` result), distinct from `[]` — an empty array is not
272
+ * currently emitted; absence is signalled by `null` + a null `events_source`.
273
+ * Never fabricated. See [`crate::domain::events`].
274
+ */
275
+ events: ActionEvent[] | null;
276
+ /**
277
+ * How `events` was recovered (e.g. `json:opencode-parts`,
278
+ * `stream-json:content-blocks`), parallel to `text_source`; `null` when no
279
+ * events were found. Lets a consumer tell "harness doesn't support it" from
280
+ * "no tools were used."
281
+ */
282
+ events_source: string | null;
283
+ /**
284
+ * Process exit code; `null` when not run, timed out, or signalled.
285
+ */
286
+ exit_code: number | null;
287
+ /**
288
+ * Best-effort failure reason; `null` when unclassified. Distinct from
289
+ * `status`, which records oneharness's relationship to the process. Two
290
+ * families: coarse reasons for a non-zero run (`auth`, `rate_limit`,
291
+ * `model_not_found`, `quota`), and `tool_deferred` — a run that exited
292
+ * *cleanly* but only deferred a builtin tool call instead of executing it
293
+ * (Claude Code bridge/managed deployments), so it did no useful work. The
294
+ * deferred case is the only `failure_kind` that can appear on a `status: ok`
295
+ * run, and it also marks the run as failed for exit-code purposes.
296
+ * Serialized as its snake_case token (see [`FailureKind`]).
297
+ */
298
+ failure_kind: FailureKind | null;
299
+ /**
300
+ * Where `failure_kind` was read (`stderr`/`stdout`); `null` when absent.
301
+ */
302
+ failure_kind_source: string | null;
303
+ /**
304
+ * Canonical harness id (e.g. `claude-code`).
305
+ */
306
+ harness: string;
307
+ /**
308
+ * The model this result ran with (the value oneharness put on the harness's
309
+ * model flag), or `null` when no model was requested and the harness used its
310
+ * own default. On a **model fan-out** run (`RunReport::models`), this is what
311
+ * distinguishes results that share a harness — each entry is one (harness,
312
+ * model) pair. The model is also visible in `command`; this field surfaces it
313
+ * without parsing the argv.
314
+ */
315
+ model: string | null;
316
+ output_format: OutputFormat;
317
+ /**
318
+ * The prompt this result ran, set only on a **batch** run (one harness
319
+ * fanned over N prompts), where each result has its own prompt. `null` on an
320
+ * ordinary run, where the single top-level `prompt` applies to every result.
321
+ */
322
+ prompt: string | null;
323
+ /**
324
+ * Structured-output run only: how many times this harness was invoked under
325
+ * the validate/retry loop (1 + retries). `null` when no schema was
326
+ * requested or the harness did not run.
327
+ */
328
+ schema_attempts: number | null;
329
+ /**
330
+ * Structured-output run only: the validation errors from the final attempt,
331
+ * joined for display; `null` when valid or no schema was requested.
332
+ */
333
+ schema_error: string | null;
334
+ /**
335
+ * Structured-output run only: whether `structured` conformed to the schema
336
+ * on the final attempt. `null` when no schema was requested (or the harness
337
+ * did not run); `false` when a schema was requested but the result never
338
+ * conformed (including "no JSON found").
339
+ */
340
+ schema_valid: boolean | null;
341
+ /**
342
+ * Best-effort harness session id for continuation; `null` when none is
343
+ * exposed. Surfaced for a consumer to thread into `--resume`, and consumed
344
+ * by oneharness itself when `--session` is in play (it is captured into the
345
+ * session store to back the uniform handle — see [`RunReport::session`]).
346
+ */
347
+ session_id: string | null;
348
+ status: Status;
349
+ /**
350
+ * Raw captured stderr (empty for skipped/planned).
351
+ */
352
+ stderr: string;
353
+ /**
354
+ * Raw captured stdout (empty for skipped/planned).
355
+ */
356
+ stdout: string;
357
+ /**
358
+ * Structured-output run only: the JSON value extracted from the final
359
+ * answer and validated against the requested schema. `null` when no schema
360
+ * was requested, or when no JSON value could be extracted. Carries the
361
+ * last-attempted value even when it failed validation, so a consumer can
362
+ * see what the harness produced.
363
+ */
364
+ structured: unknown;
365
+ /**
366
+ * Best-effort final assistant text; `null` when extraction is impossible.
367
+ */
368
+ text: string | null;
369
+ /**
370
+ * How `text` was extracted (e.g. `json:result`, `raw`); `null` when absent.
371
+ */
372
+ text_source: string | null;
373
+ usage: Usage;
374
+ /**
375
+ * How `usage` was read (e.g. `json`); `null` when nothing was found.
376
+ */
377
+ usage_source: string | null;
378
+ [k: string]: unknown;
379
+ }
380
+ /**
381
+ * Best-effort token/cost accounting; every field is `null` when the harness
382
+ * does not report it. Always present so consumers can read a stable shape.
383
+ */
384
+ export interface Usage {
385
+ /**
386
+ * Prompt tokens served from the provider's prompt cache (a cheap read of a
387
+ * previously-written prefix), when the harness reports them. `None` when the
388
+ * harness does not surface cache counts — never `0` as a guess.
389
+ */
390
+ cache_read_tokens: number | null;
391
+ /**
392
+ * Prompt tokens written to the provider's prompt cache (a.k.a. cache
393
+ * creation), when the harness reports them. `None` when not surfaced.
394
+ */
395
+ cache_write_tokens: number | null;
396
+ /**
397
+ * Total cost in USD, when the harness reports it (often absent on
398
+ * subscription auth, where there is no per-call dollar figure).
399
+ */
400
+ cost_usd: number | null;
401
+ /**
402
+ * Prompt/input tokens billed, when the harness reports them.
403
+ */
404
+ input_tokens: number | null;
405
+ /**
406
+ * Completion/output tokens billed, when the harness reports them.
407
+ */
408
+ output_tokens: number | null;
409
+ [k: string]: unknown;
410
+ }
411
+ /**
412
+ * The uniform session handle for a run (`--session`). Present on
413
+ * [`RunReport::session`] only when `--session <name>` was requested.
414
+ */
415
+ export interface SessionReport {
416
+ /**
417
+ * The caller's stable handle (`--session <name>`, sanitized for the store).
418
+ */
419
+ name: string;
420
+ /**
421
+ * Whether this run created the named session (no prior token) or continued
422
+ * an existing one.
423
+ */
424
+ phase: "create" | "continue";
425
+ /**
426
+ * The session store file backing the handle (absolute); the programmatic
427
+ * handle to the persisted state.
428
+ */
429
+ store_file: string | null;
430
+ /**
431
+ * The harness native token now bound to the name: the id resumed on a
432
+ * continue, or the id captured on a create. `null` only when a create run
433
+ * exposed no session id (the handle then cannot be continued — a warning is
434
+ * emitted), or under `--print-command` on a create (nothing ran).
435
+ */
436
+ token: string | null;
437
+ [k: string]: unknown;
438
+ }
@@ -0,0 +1,2 @@
1
+ /* Generated from oneharness-core. Do not edit. */
2
+ export {};