@neta-art/cohub-cli 10.6.0 → 10.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +8 -5
  2. package/dist/board-kernel.js +207 -119
  3. package/dist/commands/boards/examples.js +16 -16
  4. package/dist/commands/boards.js +1 -1
  5. package/dist/commands/generations.js +23 -2
  6. package/dist/commands/runtime.js +3 -14
  7. package/dist/runtime/harnesses/chained-transcript.d.ts +24 -0
  8. package/dist/runtime/harnesses/chained-transcript.js +62 -0
  9. package/dist/runtime/harnesses/claude-transcript.d.ts +8 -0
  10. package/dist/runtime/harnesses/claude-transcript.js +138 -0
  11. package/dist/runtime/harnesses/claude.js +79 -2
  12. package/dist/runtime/harnesses/codex.js +1 -1
  13. package/dist/runtime/harnesses/cursor-transcript.d.ts +37 -0
  14. package/dist/runtime/harnesses/cursor-transcript.js +183 -0
  15. package/dist/runtime/harnesses/cursor.d.ts +24 -0
  16. package/dist/runtime/harnesses/cursor.js +620 -0
  17. package/dist/runtime/harnesses/index.d.ts +1 -3
  18. package/dist/runtime/harnesses/index.js +4 -10
  19. package/dist/runtime/harnesses/pi.js +1 -1
  20. package/dist/runtime/harnesses/stream-json.d.ts +7 -1
  21. package/dist/runtime/harnesses/stream-json.js +35 -12
  22. package/dist/runtime/harnesses/types.d.ts +10 -5
  23. package/dist/runtime/harnesses/workbuddy-transcript.d.ts +8 -0
  24. package/dist/runtime/harnesses/workbuddy-transcript.js +127 -0
  25. package/dist/runtime/harnesses/workbuddy.d.ts +1 -1
  26. package/dist/runtime/harnesses/workbuddy.js +31 -7
  27. package/dist/runtime/json-rpc.d.ts +2 -0
  28. package/dist/runtime/json-rpc.js +9 -0
  29. package/dist/runtime/native/attach.d.ts +1 -1
  30. package/dist/runtime/native/attach.js +1 -4
  31. package/dist/runtime/native/execution.js +14 -3
  32. package/dist/runtime/native/ingest.d.ts +14 -0
  33. package/dist/runtime/native/ingest.js +51 -5
  34. package/dist/runtime/native/sessions.js +6 -1
  35. package/dist/runtime/native/transcript.d.ts +19 -0
  36. package/dist/runtime/native/transcript.js +33 -2
  37. package/package.json +3 -3
@@ -31,7 +31,10 @@ export class NativeSessions {
31
31
  if (restored) {
32
32
  // A restored copy is history the server already has, even where it carries no markers.
33
33
  const through = (await readTranscript(restored.path, restored.harness).catch(() => null))?.turns.at(-1)?.key ?? null;
34
- this.options.ingest.adopt(restored, input.sessionId, { turnKey: through, turnId: input.context.throughTurnId });
34
+ if (through)
35
+ this.options.ingest.adopt(restored, input.sessionId, { turnKey: through, turnId: input.context.throughTurnId });
36
+ else
37
+ this.options.ingest.adopt(restored, input.sessionId);
35
38
  return { session: restored, resume: "restored" };
36
39
  }
37
40
  }
@@ -45,6 +48,8 @@ export class NativeSessions {
45
48
  const id = randomUUID();
46
49
  const raw = join(this.options.root, "native", "restored", `${id}.jsonl`);
47
50
  const format = harnessAdapter(input.harness).session.archive;
51
+ if (!format)
52
+ throw new ArchiveNotRestorableError(`${input.harness} archives are not restorable`);
48
53
  try {
49
54
  const restored = await this.options.archives.restore(archive, raw, signal);
50
55
  const path = format.target({ id, nativeSessionId: restored.nativeSessionId, cwd: input.cwd });
@@ -18,6 +18,8 @@ export type NativeTranscriptTurn = {
18
18
  boundaries: Record<number, string>;
19
19
  sha256: string;
20
20
  result: NativeTurnComplete | null;
21
+ /** The file itself recorded this Turn's end, so it settles without waiting for a quiet period. */
22
+ ended?: boolean;
21
23
  path?: string;
22
24
  rolloutId?: string;
23
25
  sequence?: number;
@@ -31,6 +33,14 @@ export type NativeTranscript = {
31
33
  lineagePaths?: string[];
32
34
  lineageRolloutIds?: string[];
33
35
  };
36
+ export type TranscriptLine = {
37
+ value: JsonRecord;
38
+ startBytes: number;
39
+ endBytes: number;
40
+ sha256: string;
41
+ };
42
+ type Line = TranscriptLine;
43
+ export declare const iso: (value: unknown) => string;
34
44
  export declare const NATIVE_MAX_RECORD_BYTES: number;
35
45
  export declare const NATIVE_MAX_TRANSCRIPT_BYTES: number;
36
46
  export declare const codexSessionsDirectory: () => string;
@@ -50,6 +60,8 @@ export type NativeTranscriptHeader = {
50
60
  };
51
61
  /** Read exactly the first JSONL record of a stream without re-concatenating per chunk. */
52
62
  export declare function readFirstRecord(stream: AsyncIterable<Buffer>): Promise<JsonRecord>;
63
+ /** The first record a format without a header line identifies its session by; `null` when none is found early. */
64
+ export declare function readFirstMatchingRecord(stream: AsyncIterable<Buffer>, match: (value: JsonRecord) => boolean): Promise<JsonRecord | null>;
53
65
  /** Read only the session header before deciding whether a global Codex file belongs to this project. */
54
66
  export declare function readNativeTranscriptHeader(path: string, harness: "pi" | "codex", signal?: AbortSignal): Promise<NativeTranscriptHeader>;
55
67
  /** Raw JSONL bytes of a rollout, transparently decompressing `.zst` and honoring a lineage cutoff. */
@@ -58,9 +70,16 @@ export declare function codexRolloutBytes(path: string, endBytes: number | null,
58
70
  export declare function codexRolloutCachePath(source: string, cacheDirectory: string): string;
59
71
  /** Archives need plain JSONL bytes; decompress a cold `.zst` rollout into a cache once. */
60
72
  export declare function ensurePlainCodexRollout(path: string, cacheDirectory: string, signal?: AbortSignal, maxBytes?: number): Promise<string>;
73
+ type TranscriptLines = {
74
+ lines: Line[];
75
+ prefixes: Map<number, string>;
76
+ };
77
+ /** Newline framing with exact byte offsets and rolling checksums; a partial tail stays unparsed. */
78
+ export declare function readTranscriptLines(stream: AsyncIterable<Buffer>): Promise<TranscriptLines>;
61
79
  /** Partial trailing records are retried, never parsed or acknowledged as complete. */
62
80
  export declare function readNativeTranscript(path: string, harness: "pi" | "codex", options?: {
63
81
  settled?: boolean;
64
82
  leafId?: string | null;
65
83
  signal?: AbortSignal;
66
84
  }): Promise<NativeTranscript>;
85
+ export {};
@@ -12,7 +12,7 @@ import { codexTokenTotals, codexUsage } from "../codex-usage.js";
12
12
  import { record } from "../json-rpc.js";
13
13
  const text = (value) => typeof value === "string" ? value : "";
14
14
  const list = (value) => Array.isArray(value) ? value : [];
15
- const iso = (value) => {
15
+ export const iso = (value) => {
16
16
  const date = new Date(typeof value === "number" || typeof value === "string" ? value : 0);
17
17
  if (!Number.isFinite(date.getTime()))
18
18
  throw new Error("Invalid native timestamp");
@@ -22,6 +22,8 @@ const missing = (error) => error?.code === "ENOENT";
22
22
  export const NATIVE_MAX_RECORD_BYTES = 256 * 1024 * 1024;
23
23
  export const NATIVE_MAX_TRANSCRIPT_BYTES = 1024 * 1024 * 1024;
24
24
  const NATIVE_HEADER_MAX_BYTES = 1024 * 1024;
25
+ /** Formats without a header line are searched this far for the record naming the session's project. */
26
+ const NATIVE_HEADER_SCAN_MAX_BYTES = 16 * 1024 * 1024;
25
27
  const CODEX_HISTORY_MAX_DEPTH = 64;
26
28
  const CODEX_ROLLOUT_INDEX_TTL_MS = 2_000;
27
29
  const CODEX_ROLLOUT_NAME = /^rollout-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-([0-9a-f-]{36})(?:_([0-9a-f-]{36}))?\.(?:jsonl|jsonl\.zst)$/;
@@ -64,6 +66,35 @@ export async function readFirstRecord(stream) {
64
66
  }
65
67
  throw new Error("Native transcript is empty");
66
68
  }
69
+ /** The first record a format without a header line identifies its session by; `null` when none is found early. */
70
+ export async function readFirstMatchingRecord(stream, match) {
71
+ let fragments = [], scanned = 0;
72
+ for await (const chunk of stream) {
73
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
74
+ let start = 0;
75
+ for (let end = bytes.indexOf(10, start); end >= 0; end = bytes.indexOf(10, start)) {
76
+ const line = Buffer.concat([...fragments, bytes.subarray(start, end)]);
77
+ fragments = [];
78
+ start = end + 1;
79
+ if (!line.length)
80
+ continue;
81
+ let value;
82
+ try {
83
+ value = record(JSON.parse(line.toString("utf8")));
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ if (match(value))
89
+ return value;
90
+ }
91
+ fragments.push(bytes.subarray(start));
92
+ scanned += bytes.length;
93
+ if (scanned > NATIVE_HEADER_SCAN_MAX_BYTES)
94
+ return null;
95
+ }
96
+ return null;
97
+ }
67
98
  async function readCodexHeader(path, signal) {
68
99
  const value = await readFirstRecord(codexRolloutBytes(path, null, signal));
69
100
  const payload = record(value.payload);
@@ -188,7 +219,7 @@ export async function ensurePlainCodexRollout(path, cacheDirectory, signal, maxB
188
219
  }
189
220
  }
190
221
  /** Newline framing with exact byte offsets and rolling checksums; a partial tail stays unparsed. */
191
- async function readTranscriptLines(stream) {
222
+ export async function readTranscriptLines(stream) {
192
223
  const lines = [];
193
224
  const prefixes = new Map();
194
225
  let fragments = [], pendingBytes = 0, offset = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "10.6.0",
3
+ "version": "10.8.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -18,8 +18,8 @@
18
18
  "NOTICE"
19
19
  ],
20
20
  "dependencies": {
21
- "@neta-art/cohub": "11.5.0",
22
- "@neta-art/generation": "^0.1.39",
21
+ "@neta-art/cohub": "12.0.0",
22
+ "@neta-art/generation": "^0.1.40",
23
23
  "commander": "^15.0.0",
24
24
  "sharp": "^0.35.4",
25
25
  "ws": "^8.21.3"