@oneharness/sdk 0.4.5 → 0.5.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,6 +2,7 @@ import { z } from "zod";
2
2
  import type { ActionEvent, BatchReport, FailureKind, FallThrough, FallbackReport, OutputFormat, RunReport, RunResult, SessionReport, Status, ToolCallStatus, Usage } from "./contracts.js";
3
3
  import type { DetectInfo, DetectReport } from "./detection.js";
4
4
  import type { HistoryRecord } from "./history.js";
5
+ import type { HistoryLine } from "./history-line.js";
5
6
  import type { HistoryList, HistorySessionSummary } from "./history-list.js";
6
7
  import type { HistoryListOptions } from "./history-list-options.js";
7
8
  import type { HistoryLookup, HistoryLookupByLast, HistoryLookupBySession } from "./history-lookup.js";
@@ -24,6 +25,7 @@ export declare const FallThroughSchema: z.ZodType<FallThrough>;
24
25
  export declare const FallbackReportSchema: z.ZodType<FallbackReport>;
25
26
  export declare const HarnessInfoSchema: z.ZodType<HarnessInfo>;
26
27
  export declare const HistoryLabelsSchema: z.ZodType<HistoryLabels>;
28
+ export declare const HistoryLineSchema: z.ZodType<HistoryLine>;
27
29
  export declare const HistoryListSchema: z.ZodType<HistoryList>;
28
30
  export declare const HistoryListOptionsSchema: z.ZodType<HistoryListOptions>;
29
31
  export declare const HistoryLookupSchema: z.ZodType<HistoryLookup>;
@@ -90,6 +90,215 @@ export const HistoryLabelsSchema = z.record(z
90
90
  .refine((value) => !new RegExp("[\\u0000-\\u001f\\u007f-\\u009f]", "u").test(value), {
91
91
  message: "Invalid string: must not contain [\\u0000-\\u001f\\u007f-\\u009f]",
92
92
  }));
93
+ export const HistoryLineSchema = z.union([
94
+ z.looseObject({
95
+ event: z
96
+ .union([
97
+ z.intersection(z.lazy(() => ActionEventSchema), z.looseObject({
98
+ duration_ms: z
99
+ .int()
100
+ .gte(0)
101
+ .refine((value) => value !== undefined, { message: "Required" }),
102
+ finished_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
103
+ kind: z.literal("tool_call").refine((value) => value !== undefined, { message: "Required" }),
104
+ started_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
105
+ status: z.literal("completed").refine((value) => value !== undefined, { message: "Required" }),
106
+ tool_call_id: z
107
+ .string()
108
+ .min(1)
109
+ .refine((value) => value !== undefined, { message: "Required" }),
110
+ })),
111
+ z.intersection(z.lazy(() => ActionEventSchema), z.looseObject({
112
+ duration_ms: z
113
+ .int()
114
+ .gte(0)
115
+ .refine((value) => value !== undefined, { message: "Required" }),
116
+ finished_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
117
+ kind: z.literal("tool_call").refine((value) => value !== undefined, { message: "Required" }),
118
+ started_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
119
+ status: z.literal("failed").refine((value) => value !== undefined, { message: "Required" }),
120
+ tool_call_id: z
121
+ .string()
122
+ .min(1)
123
+ .refine((value) => value !== undefined, { message: "Required" }),
124
+ })),
125
+ z.intersection(z.lazy(() => ActionEventSchema), z.looseObject({
126
+ kind: z.literal("tool_call").refine((value) => value !== undefined, { message: "Required" }),
127
+ started_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
128
+ status: z.literal("timeout").refine((value) => value !== undefined, { message: "Required" }),
129
+ tool_call_id: z
130
+ .string()
131
+ .min(1)
132
+ .refine((value) => value !== undefined, { message: "Required" }),
133
+ })),
134
+ z.intersection(z.lazy(() => ActionEventSchema), z.looseObject({
135
+ kind: z.literal("tool_call").refine((value) => value !== undefined, { message: "Required" }),
136
+ started_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
137
+ status: z.literal("interrupted").refine((value) => value !== undefined, { message: "Required" }),
138
+ tool_call_id: z
139
+ .string()
140
+ .min(1)
141
+ .refine((value) => value !== undefined, { message: "Required" }),
142
+ })),
143
+ z.intersection(z.lazy(() => ActionEventSchema), z.looseObject({
144
+ kind: z
145
+ .string()
146
+ .refine((value) => !new RegExp("^tool_call$", "u").test(value), {
147
+ message: "Invalid string: must not contain ^tool_call$",
148
+ })
149
+ .optional(),
150
+ })),
151
+ ])
152
+ .refine((value) => value !== undefined, { message: "Required" }),
153
+ harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
154
+ run_id: z
155
+ .string()
156
+ .min(36)
157
+ .regex(new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", "u"))
158
+ .refine((value) => [...value].length <= 36, { message: "Too long: expected at most 36 characters" })
159
+ .refine((value) => value !== undefined, { message: "Required" }),
160
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
161
+ type: z.literal("event").refine((value) => value !== undefined, { message: "Required" }),
162
+ }),
163
+ z.union([
164
+ z.looseObject({
165
+ duration_ms: z
166
+ .int()
167
+ .gte(0)
168
+ .refine((value) => value !== undefined, { message: "Required" }),
169
+ exit_code: z.union([z.int(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
170
+ failure_kind: z
171
+ .union([z.lazy(() => FailureKindSchema), z.null()])
172
+ .refine((value) => value !== undefined, { message: "Required" }),
173
+ finished_at: z.string().refine((value) => value !== undefined, { message: "Required" }),
174
+ harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
175
+ history_id: z
176
+ .string()
177
+ .min(36)
178
+ .regex(new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", "u"))
179
+ .refine((value) => [...value].length <= 36, { message: "Too long: expected at most 36 characters" })
180
+ .refine((value) => value !== undefined, { message: "Required" }),
181
+ labels: z.lazy(() => HistoryLabelsSchema).optional(),
182
+ model: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
183
+ model_ms: z
184
+ .int()
185
+ .gte(0)
186
+ .refine((value) => value !== undefined, { message: "Required" }),
187
+ name: z.string().refine((value) => value !== undefined, { message: "Required" }),
188
+ permission_mode: z
189
+ .lazy(() => PermissionModeSchema)
190
+ .refine((value) => value !== undefined, { message: "Required" }),
191
+ project: z.string().refine((value) => value !== undefined, { message: "Required" }),
192
+ prompt: z.string().refine((value) => value !== undefined, { message: "Required" }),
193
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
194
+ session: z.string().refine((value) => value !== undefined, { message: "Required" }),
195
+ session_id: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
196
+ started_at: z
197
+ .string()
198
+ .min(1)
199
+ .refine((value) => value !== undefined, { message: "Required" }),
200
+ status: z
201
+ .union([z.literal("ok"), z.literal("nonzero")])
202
+ .refine((value) => value !== undefined, { message: "Required" }),
203
+ text: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
204
+ text_source: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
205
+ time_to_first_token_ms: z.union([z.int().gte(0), z.null()]).optional(),
206
+ timestamp: z.string().refine((value) => value !== undefined, { message: "Required" }),
207
+ tool_ms: z
208
+ .int()
209
+ .gte(0)
210
+ .refine((value) => value !== undefined, { message: "Required" }),
211
+ type: z.literal("run").refine((value) => value !== undefined, { message: "Required" }),
212
+ usage: z.lazy(() => UsageSchema).refine((value) => value !== undefined, { message: "Required" }),
213
+ }),
214
+ z.looseObject({
215
+ duration_ms: z
216
+ .int()
217
+ .gte(0)
218
+ .refine((value) => value !== undefined, { message: "Required" }),
219
+ exit_code: z.union([z.int(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
220
+ failure_kind: z
221
+ .union([z.lazy(() => FailureKindSchema), z.null()])
222
+ .refine((value) => value !== undefined, { message: "Required" }),
223
+ finished_at: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
224
+ harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
225
+ history_id: z
226
+ .string()
227
+ .min(36)
228
+ .regex(new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", "u"))
229
+ .refine((value) => [...value].length <= 36, { message: "Too long: expected at most 36 characters" })
230
+ .refine((value) => value !== undefined, { message: "Required" }),
231
+ labels: z.lazy(() => HistoryLabelsSchema).optional(),
232
+ model: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
233
+ model_ms: z
234
+ .int()
235
+ .gte(0)
236
+ .refine((value) => value !== undefined, { message: "Required" }),
237
+ name: z.string().refine((value) => value !== undefined, { message: "Required" }),
238
+ permission_mode: z
239
+ .lazy(() => PermissionModeSchema)
240
+ .refine((value) => value !== undefined, { message: "Required" }),
241
+ project: z.string().refine((value) => value !== undefined, { message: "Required" }),
242
+ prompt: z.string().refine((value) => value !== undefined, { message: "Required" }),
243
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
244
+ session: z.string().refine((value) => value !== undefined, { message: "Required" }),
245
+ session_id: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
246
+ started_at: z
247
+ .string()
248
+ .min(1)
249
+ .refine((value) => value !== undefined, { message: "Required" }),
250
+ status: z
251
+ .union([z.literal("timeout"), z.literal("spawn_error"), z.literal("skipped"), z.literal("planned")])
252
+ .refine((value) => value !== undefined, { message: "Required" }),
253
+ text: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
254
+ text_source: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
255
+ time_to_first_token_ms: z.union([z.int().gte(0), z.null()]).optional(),
256
+ timestamp: z.string().refine((value) => value !== undefined, { message: "Required" }),
257
+ tool_ms: z
258
+ .int()
259
+ .gte(0)
260
+ .refine((value) => value !== undefined, { message: "Required" }),
261
+ type: z.literal("run").refine((value) => value !== undefined, { message: "Required" }),
262
+ usage: z.lazy(() => UsageSchema).refine((value) => value !== undefined, { message: "Required" }),
263
+ }),
264
+ z.looseObject({
265
+ duration_ms: z.union([z.int().gte(0), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
266
+ exit_code: z.union([z.int(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
267
+ failure_kind: z
268
+ .union([z.lazy(() => FailureKindSchema), z.null()])
269
+ .refine((value) => value !== undefined, { message: "Required" }),
270
+ finished_at: z.null().refine((value) => value !== undefined, { message: "Required" }),
271
+ harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
272
+ history_id: z
273
+ .string()
274
+ .min(36)
275
+ .regex(new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$", "u"))
276
+ .refine((value) => [...value].length <= 36, { message: "Too long: expected at most 36 characters" })
277
+ .refine((value) => value !== undefined, { message: "Required" }),
278
+ labels: z.lazy(() => HistoryLabelsSchema).optional(),
279
+ model: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
280
+ model_ms: z.never().optional(),
281
+ name: z.string().refine((value) => value !== undefined, { message: "Required" }),
282
+ permission_mode: z
283
+ .lazy(() => PermissionModeSchema)
284
+ .refine((value) => value !== undefined, { message: "Required" }),
285
+ project: z.string().refine((value) => value !== undefined, { message: "Required" }),
286
+ prompt: z.string().refine((value) => value !== undefined, { message: "Required" }),
287
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
288
+ session: z.string().refine((value) => value !== undefined, { message: "Required" }),
289
+ session_id: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
290
+ started_at: z.never().optional(),
291
+ status: z.lazy(() => StatusSchema).refine((value) => value !== undefined, { message: "Required" }),
292
+ text: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
293
+ text_source: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
294
+ time_to_first_token_ms: z.never().optional(),
295
+ timestamp: z.string().refine((value) => value !== undefined, { message: "Required" }),
296
+ tool_ms: z.never().optional(),
297
+ type: z.literal("run").refine((value) => value !== undefined, { message: "Required" }),
298
+ usage: z.lazy(() => UsageSchema).refine((value) => value !== undefined, { message: "Required" }),
299
+ }),
300
+ ]),
301
+ ]);
93
302
  export const HistoryListSchema = z.array(z.lazy(() => HistorySessionSummarySchema));
94
303
  export const HistoryListOptionsSchema = z.strictObject({
95
304
  allProjects: z.boolean().optional(),
@@ -201,7 +410,7 @@ export const HistoryRecordSchema = z.union([
201
410
  permission_mode: z.lazy(() => PermissionModeSchema).refine((value) => value !== undefined, { message: "Required" }),
202
411
  project: z.string().refine((value) => value !== undefined, { message: "Required" }),
203
412
  prompt: z.string().refine((value) => value !== undefined, { message: "Required" }),
204
- schema_version: z.literal("0.3").refine((value) => value !== undefined, { message: "Required" }),
413
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
205
414
  session: z.string().refine((value) => value !== undefined, { message: "Required" }),
206
415
  session_id: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
207
416
  started_at: z
@@ -260,7 +469,7 @@ export const HistoryRecordSchema = z.union([
260
469
  permission_mode: z.lazy(() => PermissionModeSchema).refine((value) => value !== undefined, { message: "Required" }),
261
470
  project: z.string().refine((value) => value !== undefined, { message: "Required" }),
262
471
  prompt: z.string().refine((value) => value !== undefined, { message: "Required" }),
263
- schema_version: z.literal("0.3").refine((value) => value !== undefined, { message: "Required" }),
472
+ schema_version: z.literal("1.0").refine((value) => value !== undefined, { message: "Required" }),
264
473
  session: z.string().refine((value) => value !== undefined, { message: "Required" }),
265
474
  session_id: z.union([z.string(), z.null()]).refine((value) => value !== undefined, { message: "Required" }),
266
475
  started_at: z.never().optional(),
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ import type { RunStreamEnvelope } from "./generated/run-stream-envelope.js";
12
12
  export type { ActionEvent, BatchReport, FailureKind, FallbackReport, FallThrough, OutputFormat, RunReport, RunResult, SessionReport, Status, Usage, } from "./generated/contracts.js";
13
13
  export type { DetectInfo, DetectReport } from "./generated/detection.js";
14
14
  export type { HistoryRecord } from "./generated/history.js";
15
+ export type { HistoryLine } from "./generated/history-line.js";
15
16
  export type { HistoryList, HistorySessionSummary, } from "./generated/history-list.js";
16
17
  export type { HistoryListOptions } from "./generated/history-list-options.js";
17
18
  export type { HistoryLookup, HistoryLookupByLast, HistoryLookupBySession, } from "./generated/history-lookup.js";
@@ -28,6 +29,13 @@ export type OneHarnessOptions = {
28
29
  executableArgs?: readonly string[];
29
30
  env?: Readonly<Record<string, string>>;
30
31
  };
32
+ /** Script for the shipped deterministic provider substitute. */
33
+ export type MockHarnessScript = {
34
+ stdout?: string;
35
+ stderr?: string;
36
+ exitCode?: number;
37
+ latencyMs?: number;
38
+ };
31
39
  /** A non-zero exit from the oneharness subprocess. */
32
40
  export declare class OneHarnessProcessError extends Error {
33
41
  readonly exitCode: number | null;
@@ -42,6 +50,7 @@ export declare class OneHarness {
42
50
  private readonly options;
43
51
  constructor(options?: OneHarnessOptions);
44
52
  run(options: RunOptions): Promise<RunReport>;
53
+ runMock(harness: string, options: RunOptions, script?: MockHarnessScript): Promise<RunReport>;
45
54
  runStream(options: RunOptions): AsyncGenerator<RunStreamEnvelope>;
46
55
  list(): Promise<HarnessInfo[]>;
47
56
  detect(harnesses?: readonly string[]): Promise<Detection[]>;
package/dist/index.js CHANGED
@@ -194,6 +194,24 @@ export class OneHarness {
194
194
  const value = await invokeWith(this.options, args, input.cwd, true);
195
195
  return parseContract(RunReportSchema, value, "invalid oneharness run contract");
196
196
  }
197
+ async runMock(harness, options, script = {}) {
198
+ if (harness.length === 0)
199
+ throw new Error("mock harness must not be empty");
200
+ const input = parseContract(RunOptionsSchema, options, "invalid oneharness run options");
201
+ const env = { ...(input.env ?? {}) };
202
+ if (script.stdout !== undefined)
203
+ env.MOCK_STDOUT = script.stdout;
204
+ if (script.stderr !== undefined)
205
+ env.MOCK_STDERR = script.stderr;
206
+ if (script.exitCode !== undefined)
207
+ env.MOCK_EXIT = String(script.exitCode);
208
+ if (script.latencyMs !== undefined)
209
+ env.MOCK_SLEEP_MS = String(script.latencyMs);
210
+ const args = runArguments({ ...input, harnesses: [harness], env }, false);
211
+ args.push("--mock-harness", harness);
212
+ const value = await invokeWith(this.options, args, input.cwd, true);
213
+ return parseContract(RunReportSchema, value, "invalid oneharness run contract");
214
+ }
197
215
  runStream(options) {
198
216
  const input = parseContract(RunOptionsSchema, options, "invalid oneharness run options");
199
217
  return invokeStreamWith(this.options, runArguments(input, true), RunStreamEnvelopeSchema, "invalid oneharness run stream contract", input.cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneharness/sdk",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "Typed Node.js SDK for oneharness",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "node": ">=20"
20
20
  },
21
21
  "dependencies": {
22
- "oneharness-cli": "0.4.5",
22
+ "oneharness-cli": "0.5.0",
23
23
  "zod": "^4.4.3"
24
24
  }
25
25
  }