@oneharness/sdk 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ const checked: RunReport = RunReportSchema.parse(report);
11
11
  console.log(checked.results[0]?.text, checked.results[0]?.usage.input_tokens);
12
12
  ```
13
13
 
14
- The client covers every verb the CLI exposes, so nothing needs a hand-built command line: `run`, `runMock`, `runStream`, `list`, `detect`, `config`, `sync`, `init`, `usage`, `gate`, `mock`, `interrupt`, `history`, `historyList`, `historyWatch`, `historyClear`, and `historyMigrate`. That coverage is gated, not asserted here. `runMock` uses the deterministic responder shipped in the CLI; see [Testing patterns](../../docs/testing-patterns.md). Both streaming methods return async iterators:
14
+ The client covers every verb the CLI exposes, so nothing needs a hand-built command line: `run`, `runMock`, `runStream`, `list`, `detect`, `config`, `sync`, `init`, `usage`, `gate`, `mock`, `interrupt`, `history`, `historyList`, `historyWatch`, `historyClear`, `historyMigrate`, and `historyPointers`. That coverage is gated, not asserted here. `runMock` uses the deterministic responder shipped in the CLI; see [Testing patterns](../../docs/testing-patterns.md). Both streaming methods return async iterators:
15
15
 
16
16
  ```ts
17
17
  for await (const envelope of oneharness.runStream({
@@ -250,6 +250,11 @@ export declare const CAPABILITIES: {
250
250
  readonly flag: "--history-dir";
251
251
  readonly kind: "value";
252
252
  readonly unless: null;
253
+ }, {
254
+ readonly option: "historyPointerFile";
255
+ readonly flag: "--history-pointer-file";
256
+ readonly kind: "value";
257
+ readonly unless: null;
253
258
  }, {
254
259
  readonly option: "historyName";
255
260
  readonly flag: "--history-name";
@@ -490,6 +495,11 @@ export declare const CAPABILITIES: {
490
495
  readonly flag: "--history-dir";
491
496
  readonly kind: "value";
492
497
  readonly unless: null;
498
+ }, {
499
+ readonly option: "historyPointerFile";
500
+ readonly flag: "--history-pointer-file";
501
+ readonly kind: "value";
502
+ readonly unless: null;
493
503
  }, {
494
504
  readonly option: "historyName";
495
505
  readonly flag: "--history-name";
@@ -1044,5 +1054,22 @@ export declare const CAPABILITIES: {
1044
1054
  }];
1045
1055
  readonly uncovered: readonly [];
1046
1056
  };
1057
+ readonly historyPointers: {
1058
+ readonly method: "historyPointers";
1059
+ readonly argv: readonly ["history", "pointers"];
1060
+ readonly options: "history_pointers_options";
1061
+ readonly output: "history_pointers";
1062
+ readonly stdout: "json";
1063
+ readonly stdin: false;
1064
+ readonly rust: "oneharness_core::io::history::read_pointers";
1065
+ readonly always: readonly ["--compact", "--format", "json"];
1066
+ readonly bindings: readonly [{
1067
+ readonly option: "file";
1068
+ readonly flag: "";
1069
+ readonly kind: "positional";
1070
+ readonly unless: null;
1071
+ }];
1072
+ readonly uncovered: readonly [];
1073
+ };
1047
1074
  };
1048
1075
  export type CapabilityMethod = keyof typeof CAPABILITIES;
@@ -253,6 +253,12 @@ export const CAPABILITIES = {
253
253
  kind: "value",
254
254
  unless: null,
255
255
  },
256
+ {
257
+ option: "historyPointerFile",
258
+ flag: "--history-pointer-file",
259
+ kind: "value",
260
+ unless: null,
261
+ },
256
262
  {
257
263
  option: "historyName",
258
264
  flag: "--history-name",
@@ -541,6 +547,12 @@ export const CAPABILITIES = {
541
547
  kind: "value",
542
548
  unless: null,
543
549
  },
550
+ {
551
+ option: "historyPointerFile",
552
+ flag: "--history-pointer-file",
553
+ kind: "value",
554
+ unless: null,
555
+ },
544
556
  {
545
557
  option: "historyName",
546
558
  flag: "--history-name",
@@ -1184,4 +1196,23 @@ export const CAPABILITIES = {
1184
1196
  ],
1185
1197
  uncovered: [],
1186
1198
  },
1199
+ historyPointers: {
1200
+ method: "historyPointers",
1201
+ argv: ["history", "pointers"],
1202
+ options: "history_pointers_options",
1203
+ output: "history_pointers",
1204
+ stdout: "json",
1205
+ stdin: false,
1206
+ rust: "oneharness_core::io::history::read_pointers",
1207
+ always: ["--compact", "--format", "json"],
1208
+ bindings: [
1209
+ {
1210
+ option: "file",
1211
+ flag: "",
1212
+ kind: "positional",
1213
+ unless: null,
1214
+ },
1215
+ ],
1216
+ uncovered: [],
1217
+ },
1187
1218
  };
@@ -56,6 +56,7 @@ export interface ConfigReport {
56
56
  history_labels: {
57
57
  [k: string]: Field3;
58
58
  };
59
+ history_pointer_file: Field3;
59
60
  hooks: Field10;
60
61
  max_parallel: Field8;
61
62
  mode: Field4;
@@ -384,7 +384,7 @@ export interface RunResult {
384
384
  */
385
385
  bin: string;
386
386
  /**
387
- * The exact argv oneharness built (argv[0] is the binary).
387
+ * The exact argv oneharness built; its first element is the binary.
388
388
  */
389
389
  command: string[];
390
390
  /**
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Options accepted by the language SDKs' `historyPointers()`: the pointer
3
+ * file a run was given as `historyPointerFile`.
4
+ */
5
+ export interface HistoryPointersOptions {
6
+ /**
7
+ * The pointer file to read. A file that does not exist reads as empty.
8
+ */
9
+ file: string;
10
+ }
@@ -0,0 +1,2 @@
1
+ /* Generated from oneharness-core. Do not edit. */
2
+ export {};
@@ -0,0 +1,100 @@
1
+ /**
2
+ * What [`read_pointers`] read: every well-formed [`HistoryPointer`] line in
3
+ * file order, and how many lines were not one.
4
+ */
5
+ export interface HistoryPointers {
6
+ /**
7
+ * The pointer lines, in the order they were appended.
8
+ */
9
+ pointers: HistoryPointer[];
10
+ /**
11
+ * Lines that were not one complete pointer object — a torn tail left by
12
+ * an interrupted writer, or a foreign line — counted rather than failing
13
+ * the read.
14
+ */
15
+ skipped: number;
16
+ [k: string]: unknown;
17
+ }
18
+ /**
19
+ * One line of a run's **pointer file**: where one harness run's history went.
20
+ *
21
+ * A consumer that starts many `oneharness` processes (or in-process runs) names
22
+ * one file, and every run with history on appends one of these per harness run
23
+ * it begins — so "which sessions did this run launch, and where are they" is a
24
+ * read of that one small file rather than a scan of the whole store, wherever
25
+ * the store lives. `history_dir` / `history_project` / `history_session` are
26
+ * spelled as the `oneharness-session` artifact `oneagentgraph` publishes, so a
27
+ * reader already resolving that through `io::history::find_session_path`
28
+ * resolves these unchanged.
29
+ *
30
+ * Built only through [`HistoryPointer::new`] over a [`PointerSession`], and
31
+ * read back only through a deserialization that re-checks every invariant
32
+ * `new` establishes — the four session spellings compose into one file, the
33
+ * two identity spellings are the parsed [`HarnessIdentity`]'s own, the version
34
+ * is one this reader knows — so a line that parses IS a pointer, and a foreign
35
+ * object that happens to carry these keys is counted as skipped rather than
36
+ * read as one.
37
+ * The fields are read through accessors for the same reason: a Rust caller
38
+ * gets a line from one of those two doors, never assembles or edits one.
39
+ */
40
+ export interface HistoryPointer {
41
+ /**
42
+ * The harness id's base, e.g. `claude-code`.
43
+ */
44
+ harness: string;
45
+ /**
46
+ * The whole configured id, e.g. `claude-code:primary` — always `harness`
47
+ * with `:variant` when there is one.
48
+ */
49
+ harness_id: string;
50
+ /**
51
+ * The store the session is under, absolute.
52
+ */
53
+ history_dir: string;
54
+ /**
55
+ * The session file, absolute; the same path the run report echoes, and
56
+ * always `<history_dir>/<history_project>/<history_session>.jsonl`.
57
+ */
58
+ history_file: string;
59
+ /**
60
+ * The history id of the record this harness run will close with — the
61
+ * exact id `history show <history-id>` resolves.
62
+ */
63
+ history_id: string;
64
+ /**
65
+ * The project slug — the session file's parent directory name.
66
+ */
67
+ history_project: string;
68
+ /**
69
+ * The session id — the session file's stem.
70
+ */
71
+ history_session: string;
72
+ labels?: HistoryLabels | undefined;
73
+ /**
74
+ * The session's human-meaningful name (see [`session_name`]).
75
+ */
76
+ name: string;
77
+ /**
78
+ * The project directory the run operates in, canonical.
79
+ */
80
+ project: string;
81
+ /**
82
+ * [`POINTER_SCHEMA_VERSION`]; a reader accepts any `1.<minor>`.
83
+ */
84
+ schema_version: string;
85
+ /**
86
+ * RFC 3339 UTC, when this harness run began.
87
+ */
88
+ started: string;
89
+ /**
90
+ * The variant, omitted for a bare harness.
91
+ */
92
+ variant?: string | null | undefined;
93
+ [k: string]: unknown;
94
+ }
95
+ /**
96
+ * The session's validated labels, omitted when empty.
97
+ */
98
+ export interface HistoryLabels {
99
+ [k: string]: string;
100
+ }
@@ -0,0 +1,2 @@
1
+ /* Generated from oneharness-core. Do not edit. */
2
+ export {};
@@ -69,6 +69,14 @@ export interface RunOptions {
69
69
  historyDir?: string | undefined;
70
70
  historyLabels?: HistoryLabels | undefined;
71
71
  historyName?: string | undefined;
72
+ /**
73
+ * The run's pointer file: with history on, every harness run this run
74
+ * begins appends one `HistoryPointer` line here saying where its session
75
+ * went (see `historyPointers()`), the store itself staying where
76
+ * `historyDir` puts it. An empty path is refused rather than read as
77
+ * unset — leave the option out to name no file.
78
+ */
79
+ historyPointerFile?: string | undefined;
72
80
  /**
73
81
  * Maximum harnesses (or, in a batch, prompts) to run concurrently.
74
82
  */
@@ -458,7 +458,7 @@ export interface RunResult {
458
458
  */
459
459
  bin: string;
460
460
  /**
461
- * The exact argv oneharness built (argv[0] is the binary).
461
+ * The exact argv oneharness built; its first element is the binary.
462
462
  */
463
463
  command: string[];
464
464
  /**
@@ -261,6 +261,12 @@
261
261
  "kind": "value",
262
262
  "unless": null
263
263
  },
264
+ {
265
+ "option": "historyPointerFile",
266
+ "flag": "--history-pointer-file",
267
+ "kind": "value",
268
+ "unless": null
269
+ },
264
270
  {
265
271
  "option": "historyName",
266
272
  "flag": "--history-name",
@@ -554,6 +560,12 @@
554
560
  "kind": "value",
555
561
  "unless": null
556
562
  },
563
+ {
564
+ "option": "historyPointerFile",
565
+ "flag": "--history-pointer-file",
566
+ "kind": "value",
567
+ "unless": null
568
+ },
557
569
  {
558
570
  "option": "historyName",
559
571
  "flag": "--history-name",
@@ -1272,6 +1284,32 @@
1272
1284
  }
1273
1285
  ],
1274
1286
  "uncovered": []
1287
+ },
1288
+ {
1289
+ "method": "historyPointers",
1290
+ "argv": [
1291
+ "history",
1292
+ "pointers"
1293
+ ],
1294
+ "options": "history_pointers_options",
1295
+ "output": "history_pointers",
1296
+ "stdout": "json",
1297
+ "stdin": false,
1298
+ "rust": "oneharness_core::io::history::read_pointers",
1299
+ "always": [
1300
+ "--compact",
1301
+ "--format",
1302
+ "json"
1303
+ ],
1304
+ "bindings": [
1305
+ {
1306
+ "option": "file",
1307
+ "flag": "",
1308
+ "kind": "positional",
1309
+ "unless": null
1310
+ }
1311
+ ],
1312
+ "uncovered": []
1275
1313
  }
1276
1314
  ],
1277
1315
  "run_report": {
@@ -1842,7 +1880,7 @@
1842
1880
  "const": "quota"
1843
1881
  },
1844
1882
  {
1845
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
1883
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
1846
1884
  "type": "string",
1847
1885
  "const": "session_not_found"
1848
1886
  },
@@ -2057,7 +2095,7 @@
2057
2095
  "type": "string"
2058
2096
  },
2059
2097
  "command": {
2060
- "description": "The exact argv oneharness built (argv[0] is the binary).",
2098
+ "description": "The exact argv oneharness built; its first element is the binary.",
2061
2099
  "type": "array",
2062
2100
  "items": {
2063
2101
  "type": "string"
@@ -2923,7 +2961,7 @@
2923
2961
  "const": "quota"
2924
2962
  },
2925
2963
  {
2926
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
2964
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
2927
2965
  "type": "string",
2928
2966
  "const": "session_not_found"
2929
2967
  },
@@ -3302,7 +3340,7 @@
3302
3340
  "type": "string"
3303
3341
  },
3304
3342
  "command": {
3305
- "description": "The exact argv oneharness built (argv[0] is the binary).",
3343
+ "description": "The exact argv oneharness built; its first element is the binary.",
3306
3344
  "type": "array",
3307
3345
  "items": {
3308
3346
  "type": "string"
@@ -3798,6 +3836,11 @@
3798
3836
  "historyName": {
3799
3837
  "type": "string"
3800
3838
  },
3839
+ "historyPointerFile": {
3840
+ "description": "The run's pointer file: with history on, every harness run this run\nbegins appends one `HistoryPointer` line here saying where its session\nwent (see `historyPointers()`), the store itself staying where\n`historyDir` puts it. An empty path is refused rather than read as\nunset — leave the option out to name no file.",
3841
+ "type": "string",
3842
+ "minLength": 1
3843
+ },
3801
3844
  "maxParallel": {
3802
3845
  "description": "Maximum harnesses (or, in a batch, prompts) to run concurrently.",
3803
3846
  "type": "integer",
@@ -5974,7 +6017,7 @@
5974
6017
  "const": "quota"
5975
6018
  },
5976
6019
  {
5977
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
6020
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
5978
6021
  "type": "string",
5979
6022
  "const": "session_not_found"
5980
6023
  },
@@ -8976,7 +9019,7 @@
8976
9019
  "const": "quota"
8977
9020
  },
8978
9021
  {
8979
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
9022
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
8980
9023
  "type": "string",
8981
9024
  "const": "session_not_found"
8982
9025
  },
@@ -9360,7 +9403,7 @@
9360
9403
  "const": "quota"
9361
9404
  },
9362
9405
  {
9363
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
9406
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
9364
9407
  "type": "string",
9365
9408
  "const": "session_not_found"
9366
9409
  },
@@ -12482,7 +12525,7 @@
12482
12525
  "const": "quota"
12483
12526
  },
12484
12527
  {
12485
- "description": "The session this run asked to continue does not exist for the identity it\nran as, so the harness refused before doing any work (see\n[`unknown_session_rejection`]). Distinct from every other kind because the\ntask itself is fine: another identity — or a fresh session on this one —\ncan still run it.",
12528
+ "description": "The session this run asked to continue does not exist for the identity\nit ran as, so the harness refused before doing any work (see the private\n`unknown_session_rejection` rule). Distinct from every other kind\nbecause the task itself is fine: another identity — or a fresh session\non this one — can still run it.",
12486
12529
  "type": "string",
12487
12530
  "const": "session_not_found"
12488
12531
  },
@@ -15958,6 +16001,9 @@
15958
16001
  "$ref": "#/$defs/Field3"
15959
16002
  }
15960
16003
  },
16004
+ "history_pointer_file": {
16005
+ "$ref": "#/$defs/Field3"
16006
+ },
15961
16007
  "hooks": {
15962
16008
  "$ref": "#/$defs/Field10"
15963
16009
  },
@@ -16035,6 +16081,7 @@
16035
16081
  "require_available",
16036
16082
  "history",
16037
16083
  "history_dir",
16084
+ "history_pointer_file",
16038
16085
  "history_labels",
16039
16086
  "allowed_tools",
16040
16087
  "denied_tools",
@@ -16760,7 +16807,7 @@
16760
16807
  "usage_report": {
16761
16808
  "$schema": "https://json-schema.org/draft/2020-12/schema",
16762
16809
  "title": "UsageReport",
16763
- "description": "One usage report: every probed identity, stamped with a single observation\ntime supplied by the caller (this module reads no clock).\n\nDeserializing is a consumer boundary — `oneharness-core` is published for\nsibling tools — so the envelope is validated on the way in rather than\ntrusted: see [`UsageReportWire`].",
16810
+ "description": "One usage report: every probed identity, stamped with a single observation\ntime supplied by the caller (this module reads no clock).\n\nDeserializing is a consumer boundary — `oneharness-core` is published for\nsibling tools — so the envelope is validated on the way in rather than\ntrusted: see the private `UsageReportWire` shape.",
16764
16811
  "type": "object",
16765
16812
  "properties": {
16766
16813
  "identities": {
@@ -17057,7 +17104,7 @@
17057
17104
  ]
17058
17105
  },
17059
17106
  "UsageIdentity": {
17060
- "description": "One harness identity's headroom.\n\nDeserializing is a consumer boundary like the envelope's, so every string an\nidentity carries is flattened through [`without_control_chars`] on the way in\n— see [`UsageIdentityWire`].",
17107
+ "description": "One harness identity's headroom.\n\nDeserializing is a consumer boundary like the envelope's, so every string an\nidentity carries is flattened through the private `without_control_chars`\nfilter on the way in — see the private `UsageIdentityWire` shape.",
17061
17108
  "type": "object",
17062
17109
  "properties": {
17063
17110
  "auth_mode": {
@@ -17634,5 +17681,149 @@
17634
17681
  ]
17635
17682
  }
17636
17683
  }
17684
+ },
17685
+ "history_pointers_options": {
17686
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
17687
+ "title": "HistoryPointersOptions",
17688
+ "description": "Options accepted by the language SDKs' `historyPointers()`: the pointer\nfile a run was given as `historyPointerFile`.",
17689
+ "type": "object",
17690
+ "properties": {
17691
+ "file": {
17692
+ "description": "The pointer file to read. A file that does not exist reads as empty.",
17693
+ "type": "string",
17694
+ "minLength": 1
17695
+ }
17696
+ },
17697
+ "additionalProperties": false,
17698
+ "required": [
17699
+ "file"
17700
+ ]
17701
+ },
17702
+ "history_pointers": {
17703
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
17704
+ "title": "HistoryPointers",
17705
+ "description": "What [`read_pointers`] read: every well-formed [`HistoryPointer`] line in\nfile order, and how many lines were not one.",
17706
+ "type": "object",
17707
+ "properties": {
17708
+ "pointers": {
17709
+ "description": "The pointer lines, in the order they were appended.",
17710
+ "type": "array",
17711
+ "items": {
17712
+ "$ref": "#/$defs/HistoryPointer"
17713
+ }
17714
+ },
17715
+ "skipped": {
17716
+ "description": "Lines that were not one complete pointer object — a torn tail left by\nan interrupted writer, or a foreign line — counted rather than failing\nthe read.",
17717
+ "type": "integer",
17718
+ "format": "uint",
17719
+ "minimum": 0
17720
+ }
17721
+ },
17722
+ "required": [
17723
+ "pointers",
17724
+ "skipped"
17725
+ ],
17726
+ "$defs": {
17727
+ "HistoryLabels": {
17728
+ "type": "object",
17729
+ "additionalProperties": {
17730
+ "type": "string",
17731
+ "maxLength": 256,
17732
+ "minLength": 1,
17733
+ "not": {
17734
+ "pattern": "[\\u0000-\\u001f\\u007f-\\u009f]"
17735
+ }
17736
+ },
17737
+ "propertyNames": {
17738
+ "type": "string",
17739
+ "maxLength": 64,
17740
+ "not": {
17741
+ "pattern": "[^A-Za-z0-9._-]"
17742
+ },
17743
+ "pattern": "^[A-Za-z0-9]"
17744
+ }
17745
+ },
17746
+ "HistoryPointer": {
17747
+ "description": "One line of a run's **pointer file**: where one harness run's history went.\n\nA consumer that starts many `oneharness` processes (or in-process runs) names\none file, and every run with history on appends one of these per harness run\nit begins — so \"which sessions did this run launch, and where are they\" is a\nread of that one small file rather than a scan of the whole store, wherever\nthe store lives. `history_dir` / `history_project` / `history_session` are\nspelled as the `oneharness-session` artifact `oneagentgraph` publishes, so a\nreader already resolving that through `io::history::find_session_path`\nresolves these unchanged.\n\nBuilt only through [`HistoryPointer::new`] over a [`PointerSession`], and\nread back only through a deserialization that re-checks every invariant\n`new` establishes — the four session spellings compose into one file, the\ntwo identity spellings are the parsed [`HarnessIdentity`]'s own, the version\nis one this reader knows — so a line that parses IS a pointer, and a foreign\nobject that happens to carry these keys is counted as skipped rather than\nread as one.\nThe fields are read through accessors for the same reason: a Rust caller\ngets a line from one of those two doors, never assembles or edits one.",
17748
+ "type": "object",
17749
+ "properties": {
17750
+ "harness": {
17751
+ "description": "The harness id's base, e.g. `claude-code`.",
17752
+ "type": "string"
17753
+ },
17754
+ "harness_id": {
17755
+ "description": "The whole configured id, e.g. `claude-code:primary` — always `harness`\nwith `:variant` when there is one.",
17756
+ "type": "string"
17757
+ },
17758
+ "history_dir": {
17759
+ "description": "The store the session is under, absolute.",
17760
+ "type": "string"
17761
+ },
17762
+ "history_file": {
17763
+ "description": "The session file, absolute; the same path the run report echoes, and\nalways `<history_dir>/<history_project>/<history_session>.jsonl`.",
17764
+ "type": "string"
17765
+ },
17766
+ "history_id": {
17767
+ "description": "The history id of the record this harness run will close with — the\nexact id `history show <history-id>` resolves.",
17768
+ "type": "string",
17769
+ "maxLength": 36,
17770
+ "minLength": 36,
17771
+ "pattern": "^[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}$"
17772
+ },
17773
+ "history_project": {
17774
+ "description": "The project slug — the session file's parent directory name.",
17775
+ "type": "string"
17776
+ },
17777
+ "history_session": {
17778
+ "description": "The session id — the session file's stem.",
17779
+ "type": "string"
17780
+ },
17781
+ "labels": {
17782
+ "description": "The session's validated labels, omitted when empty.",
17783
+ "$ref": "#/$defs/HistoryLabels"
17784
+ },
17785
+ "name": {
17786
+ "description": "The session's human-meaningful name (see [`session_name`]).",
17787
+ "type": "string"
17788
+ },
17789
+ "project": {
17790
+ "description": "The project directory the run operates in, canonical.",
17791
+ "type": "string"
17792
+ },
17793
+ "schema_version": {
17794
+ "description": "[`POINTER_SCHEMA_VERSION`]; a reader accepts any `1.<minor>`.",
17795
+ "type": "string"
17796
+ },
17797
+ "started": {
17798
+ "description": "RFC 3339 UTC, when this harness run began.",
17799
+ "$ref": "#/$defs/UtcInstant"
17800
+ },
17801
+ "variant": {
17802
+ "description": "The variant, omitted for a bare harness.",
17803
+ "type": [
17804
+ "string",
17805
+ "null"
17806
+ ]
17807
+ }
17808
+ },
17809
+ "required": [
17810
+ "schema_version",
17811
+ "history_id",
17812
+ "history_dir",
17813
+ "history_project",
17814
+ "history_session",
17815
+ "history_file",
17816
+ "name",
17817
+ "project",
17818
+ "harness",
17819
+ "harness_id",
17820
+ "started"
17821
+ ]
17822
+ },
17823
+ "UtcInstant": {
17824
+ "description": "An RFC 3339 timestamp in UTC (`Z`), in oneharness's canonical spelling.",
17825
+ "type": "string"
17826
+ }
17827
+ }
17637
17828
  }
17638
17829
  }
@@ -145,7 +145,7 @@ export type UnknownReason = {
145
145
  *
146
146
  * Deserializing is a consumer boundary — `oneharness-core` is published for
147
147
  * sibling tools — so the envelope is validated on the way in rather than
148
- * trusted: see [`UsageReportWire`].
148
+ * trusted: see the private `UsageReportWire` shape.
149
149
  */
150
150
  export interface UsageReport {
151
151
  identities: UsageIdentity[];
@@ -163,8 +163,8 @@ export interface UsageReport {
163
163
  * One harness identity's headroom.
164
164
  *
165
165
  * Deserializing is a consumer boundary like the envelope's, so every string an
166
- * identity carries is flattened through [`without_control_chars`] on the way in
167
- * — see [`UsageIdentityWire`].
166
+ * identity carries is flattened through the private `without_control_chars`
167
+ * filter on the way in — see the private `UsageIdentityWire` shape.
168
168
  */
169
169
  export interface UsageIdentity {
170
170
  auth_mode: AuthMode;
@@ -14,6 +14,8 @@ import type { HistoryListOptions } from "./history-list-options.js";
14
14
  import type { HistoryLookup, HistoryLookupByLast, HistoryLookupBySession } from "./history-lookup.js";
15
15
  import type { HistoryMigrateOptions } from "./history-migrate-options.js";
16
16
  import type { HistoryMigrateReport, MigrationSummary } from "./history-migrate-report.js";
17
+ import type { HistoryPointer, HistoryPointers } from "./history-pointers.js";
18
+ import type { HistoryPointersOptions } from "./history-pointers-options.js";
17
19
  import type { HistoryRecords } from "./history-records.js";
18
20
  import type { HistoryEventLine, HistoryStreamEnvelope } from "./history-stream-envelope.js";
19
21
  import type { HistoryWatchOptions } from "./history-watch-options.js";
@@ -83,6 +85,9 @@ export declare const HistoryLookupByLastSchema: z.ZodType<HistoryLookupByLast>;
83
85
  export declare const HistoryLookupBySessionSchema: z.ZodType<HistoryLookupBySession>;
84
86
  export declare const HistoryMigrateOptionsSchema: z.ZodType<HistoryMigrateOptions>;
85
87
  export declare const HistoryMigrateReportSchema: z.ZodType<HistoryMigrateReport>;
88
+ export declare const HistoryPointerSchema: z.ZodType<HistoryPointer>;
89
+ export declare const HistoryPointersSchema: z.ZodType<HistoryPointers>;
90
+ export declare const HistoryPointersOptionsSchema: z.ZodType<HistoryPointersOptions>;
86
91
  export declare const HistoryRecordSchema: z.ZodType<HistoryRecord>;
87
92
  export declare const HistoryRecordsSchema: z.ZodType<HistoryRecords>;
88
93
  export declare const HistorySessionSummarySchema: z.ZodType<HistorySessionSummary>;
@@ -57,6 +57,7 @@ export const ConfigReportSchema = z.looseObject({
57
57
  history_labels: z
58
58
  .record(z.string(), z.lazy(() => Field3Schema))
59
59
  .refine((value) => value !== undefined, { message: "Required" }),
60
+ history_pointer_file: z.lazy(() => Field3Schema).refine((value) => value !== undefined, { message: "Required" }),
60
61
  hooks: z.lazy(() => Field10Schema).refine((value) => value !== undefined, { message: "Required" }),
61
62
  max_parallel: z.lazy(() => Field8Schema).refine((value) => value !== undefined, { message: "Required" }),
62
63
  mode: z.lazy(() => Field4Schema).refine((value) => value !== undefined, { message: "Required" }),
@@ -1134,6 +1135,39 @@ export const HistoryMigrateReportSchema = z.looseObject({
1134
1135
  .gte(0)
1135
1136
  .refine((value) => value !== undefined, { message: "Required" }),
1136
1137
  });
1138
+ export const HistoryPointerSchema = z.looseObject({
1139
+ harness: z.string().refine((value) => value !== undefined, { message: "Required" }),
1140
+ harness_id: z.string().refine((value) => value !== undefined, { message: "Required" }),
1141
+ history_dir: z.string().refine((value) => value !== undefined, { message: "Required" }),
1142
+ history_file: z.string().refine((value) => value !== undefined, { message: "Required" }),
1143
+ history_id: z
1144
+ .string()
1145
+ .min(36)
1146
+ .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"))
1147
+ .refine((value) => [...value].length <= 36, { message: "Too long: expected at most 36 characters" })
1148
+ .refine((value) => value !== undefined, { message: "Required" }),
1149
+ history_project: z.string().refine((value) => value !== undefined, { message: "Required" }),
1150
+ history_session: z.string().refine((value) => value !== undefined, { message: "Required" }),
1151
+ labels: z.lazy(() => HistoryLabelsSchema).optional(),
1152
+ name: z.string().refine((value) => value !== undefined, { message: "Required" }),
1153
+ project: z.string().refine((value) => value !== undefined, { message: "Required" }),
1154
+ schema_version: z.string().refine((value) => value !== undefined, { message: "Required" }),
1155
+ started: z.lazy(() => UtcInstantSchema).refine((value) => value !== undefined, { message: "Required" }),
1156
+ variant: z.union([z.string(), z.null()]).optional(),
1157
+ });
1158
+ export const HistoryPointersSchema = z.looseObject({
1159
+ pointers: z.array(z.lazy(() => HistoryPointerSchema)).refine((value) => value !== undefined, { message: "Required" }),
1160
+ skipped: z
1161
+ .int()
1162
+ .gte(0)
1163
+ .refine((value) => value !== undefined, { message: "Required" }),
1164
+ });
1165
+ export const HistoryPointersOptionsSchema = z.strictObject({
1166
+ file: z
1167
+ .string()
1168
+ .min(1)
1169
+ .refine((value) => value !== undefined, { message: "Required" }),
1170
+ });
1137
1171
  export const HistoryRecordSchema = z.intersection(z.intersection(z.intersection(z.intersection(z.intersection(z.union([
1138
1172
  z.looseObject({
1139
1173
  duration_ms: z
@@ -2321,6 +2355,7 @@ export const RunOptionsSchema = z.strictObject({
2321
2355
  historyDir: z.string().optional(),
2322
2356
  historyLabels: z.lazy(() => HistoryLabelsSchema).optional(),
2323
2357
  historyName: z.string().optional(),
2358
+ historyPointerFile: z.string().min(1).optional(),
2324
2359
  maxParallel: z.int().gte(0).optional(),
2325
2360
  mockHarnesses: z.array(z.string()).optional(),
2326
2361
  mockRules: z.string().optional(),
package/dist/index.d.ts CHANGED
@@ -12,6 +12,8 @@ import type { HistoryListOptions } from "./generated/history-list-options.js";
12
12
  import type { HistoryLookup } from "./generated/history-lookup.js";
13
13
  import type { HistoryMigrateOptions } from "./generated/history-migrate-options.js";
14
14
  import type { HistoryMigrateReport } from "./generated/history-migrate-report.js";
15
+ import type { HistoryPointers } from "./generated/history-pointers.js";
16
+ import type { HistoryPointersOptions } from "./generated/history-pointers-options.js";
15
17
  import type { HistoryStreamEnvelope } from "./generated/history-stream-envelope.js";
16
18
  import type { HistoryWatchOptions } from "./generated/history-watch-options.js";
17
19
  import type { InitOptions } from "./generated/init-options.js";
@@ -42,6 +44,8 @@ export type { HistoryListOptions } from "./generated/history-list-options.js";
42
44
  export type { HistoryLookup, HistoryLookupByLast, HistoryLookupBySession, } from "./generated/history-lookup.js";
43
45
  export type { HistoryMigrateOptions } from "./generated/history-migrate-options.js";
44
46
  export type { HistoryMigrateReport } from "./generated/history-migrate-report.js";
47
+ export type { HistoryPointer, HistoryPointers, } from "./generated/history-pointers.js";
48
+ export type { HistoryPointersOptions } from "./generated/history-pointers-options.js";
45
49
  export type { HistoryRecords } from "./generated/history-records.js";
46
50
  export type { HistoryStreamEnvelope } from "./generated/history-stream-envelope.js";
47
51
  export type { HistoryWatchOptions } from "./generated/history-watch-options.js";
@@ -146,5 +150,13 @@ export declare class OneHarness {
146
150
  historyClear(options?: HistoryClearOptions): Promise<HistoryClearReport>;
147
151
  /** Rewrite legacy session files to the current record version. */
148
152
  historyMigrate(options?: HistoryMigrateOptions): Promise<HistoryMigrateReport>;
153
+ /**
154
+ * Read a run's pointer file (`historyPointerFile` on `run()`): one line per
155
+ * harness run begun with history on, naming where its session went.
156
+ *
157
+ * A missing file reads as empty; a torn or foreign line is counted in
158
+ * `skipped` rather than failing the read.
159
+ */
160
+ historyPointers(options: HistoryPointersOptions): Promise<HistoryPointers>;
149
161
  historyWatch(options?: HistoryWatchOptions): AsyncGenerator<HistoryStreamEnvelope>;
150
162
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { createRequire } from "node:module";
3
3
  import { createInterface } from "node:readline";
4
4
  import { CAPABILITIES, } from "./generated/capabilities.js";
5
- import { ConfigOptionsSchema, ConfigReportSchema, DetectOptionsSchema, DetectReportSchema, GateOptionsSchema, HistoryClearOptionsSchema, HistoryClearReportSchema, HistoryListOptionsSchema, HistoryListSchema, HistoryLookupSchema, HistoryMigrateOptionsSchema, HistoryMigrateReportSchema, HistoryRecordsSchema, HistoryStreamEnvelopeSchema, HistoryWatchOptionsSchema, InitOptionsSchema, InterruptOptionsSchema, InterruptResponseSchema, ListReportSchema, MockOptionsSchema, RunOptionsSchema, RunReportSchema, RunStreamEnvelopeSchema, SyncOptionsSchema, SyncReportSchema, UsageOptionsSchema, UsageReportSchema, } from "./generated/zod.js";
5
+ import { ConfigOptionsSchema, ConfigReportSchema, DetectOptionsSchema, DetectReportSchema, GateOptionsSchema, HistoryClearOptionsSchema, HistoryClearReportSchema, HistoryListOptionsSchema, HistoryListSchema, HistoryLookupSchema, HistoryMigrateOptionsSchema, HistoryMigrateReportSchema, HistoryPointersOptionsSchema, HistoryPointersSchema, HistoryRecordsSchema, HistoryStreamEnvelopeSchema, HistoryWatchOptionsSchema, InitOptionsSchema, InterruptOptionsSchema, InterruptResponseSchema, ListReportSchema, MockOptionsSchema, RunOptionsSchema, RunReportSchema, RunStreamEnvelopeSchema, SyncOptionsSchema, SyncReportSchema, UsageOptionsSchema, UsageReportSchema, } from "./generated/zod.js";
6
6
  export { CAPABILITIES } from "./generated/capabilities.js";
7
7
  export * from "./generated/zod.js";
8
8
  /** A non-zero exit from the oneharness subprocess. */
@@ -482,6 +482,16 @@ export class OneHarness {
482
482
  async historyMigrate(options = {}) {
483
483
  return await this.call("historyMigrate", options, HistoryMigrateOptionsSchema, HistoryMigrateReportSchema);
484
484
  }
485
+ /**
486
+ * Read a run's pointer file (`historyPointerFile` on `run()`): one line per
487
+ * harness run begun with history on, naming where its session went.
488
+ *
489
+ * A missing file reads as empty; a torn or foreign line is counted in
490
+ * `skipped` rather than failing the read.
491
+ */
492
+ async historyPointers(options) {
493
+ return await this.call("historyPointers", options, HistoryPointersOptionsSchema, HistoryPointersSchema);
494
+ }
485
495
  historyWatch(options = {}) {
486
496
  const input = parseContract(HistoryWatchOptionsSchema, options, "invalid oneharness history watch options");
487
497
  return invokeStreamWith(this.options, capabilityArguments("historyWatch", input), HistoryStreamEnvelopeSchema, "invalid oneharness history watch contract", undefined, true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneharness/sdk",
3
- "version": "0.15.0",
3
+ "version": "0.16.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.15.0",
22
+ "oneharness-cli": "0.16.0",
23
23
  "zod": "^4.4.3"
24
24
  }
25
25
  }