@neta-art/cohub-cli 8.2.0 → 8.3.1

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 (76) hide show
  1. package/dist/commands/runtime.d.ts +0 -12
  2. package/dist/commands/runtime.js +129 -144
  3. package/dist/commands/search.js +3 -9
  4. package/dist/commands/spaces.js +5 -2
  5. package/dist/output.d.ts +6 -0
  6. package/dist/output.js +11 -0
  7. package/dist/runtime/codex-usage.d.ts +0 -2
  8. package/dist/runtime/codex-usage.js +0 -12
  9. package/dist/runtime/connection.d.ts +5 -5
  10. package/dist/runtime/connection.js +27 -16
  11. package/dist/runtime/harness.d.ts +8 -16
  12. package/dist/runtime/harness.js +1 -366
  13. package/dist/runtime/instance.d.ts +8 -1
  14. package/dist/runtime/instance.js +15 -8
  15. package/dist/runtime/launch.js +11 -5
  16. package/dist/runtime/native/adapters.d.ts +32 -0
  17. package/dist/runtime/native/adapters.js +79 -0
  18. package/dist/runtime/{native-archive.d.ts → native/archive.d.ts} +0 -3
  19. package/dist/runtime/{native-archive.js → native/archive.js} +2 -17
  20. package/dist/runtime/native/attach.d.ts +38 -0
  21. package/dist/runtime/native/attach.js +113 -0
  22. package/dist/runtime/native/codex-link.d.ts +40 -0
  23. package/dist/runtime/native/codex-link.js +249 -0
  24. package/dist/runtime/native/config.d.ts +16 -0
  25. package/dist/runtime/native/config.js +24 -0
  26. package/dist/runtime/native/daemon.d.ts +88 -0
  27. package/dist/runtime/native/daemon.js +367 -0
  28. package/dist/runtime/native/execution.d.ts +45 -0
  29. package/dist/runtime/native/execution.js +337 -0
  30. package/dist/runtime/native/identity.d.ts +9 -0
  31. package/dist/runtime/native/identity.js +15 -0
  32. package/dist/runtime/native/ingest.d.ts +241 -0
  33. package/dist/runtime/native/ingest.js +823 -0
  34. package/dist/runtime/native/install.d.ts +9 -0
  35. package/dist/runtime/native/install.js +113 -0
  36. package/dist/runtime/native/pi-extension.d.ts +35 -0
  37. package/dist/runtime/native/pi-extension.js +160 -0
  38. package/dist/runtime/native/pi-link.d.ts +67 -0
  39. package/dist/runtime/native/pi-link.js +238 -0
  40. package/dist/runtime/native/results.d.ts +41 -0
  41. package/dist/runtime/native/results.js +128 -0
  42. package/dist/runtime/native/sessions.d.ts +45 -0
  43. package/dist/runtime/native/sessions.js +92 -0
  44. package/dist/runtime/{native-transcript.d.ts → native/transcript.d.ts} +5 -5
  45. package/dist/runtime/{native-transcript.js → native/transcript.js} +37 -11
  46. package/dist/runtime/native/translate.d.ts +44 -0
  47. package/dist/runtime/native/translate.js +263 -0
  48. package/dist/runtime/native/transport.d.ts +7 -0
  49. package/dist/runtime/native/transport.js +16 -0
  50. package/dist/runtime/native/version.d.ts +5 -0
  51. package/dist/runtime/native/version.js +18 -0
  52. package/dist/runtime/presentation.d.ts +7 -10
  53. package/dist/runtime/presentation.js +35 -10
  54. package/dist/runtime/projection-store.d.ts +6 -13
  55. package/dist/runtime/projection-store.js +11 -74
  56. package/dist/runtime/supervisor.d.ts +2 -0
  57. package/dist/runtime/supervisor.js +36 -110
  58. package/dist/runtime/turn-projection.d.ts +1 -3
  59. package/dist/runtime/turn-projection.js +3 -10
  60. package/package.json +4 -5
  61. package/dist/runtime/native-attach.d.ts +0 -28
  62. package/dist/runtime/native-attach.js +0 -47
  63. package/dist/runtime/native-codex-hook.d.ts +0 -1
  64. package/dist/runtime/native-codex-hook.js +0 -28
  65. package/dist/runtime/native-install.d.ts +0 -21
  66. package/dist/runtime/native-install.js +0 -130
  67. package/dist/runtime/native-ipc.d.ts +0 -37
  68. package/dist/runtime/native-ipc.js +0 -118
  69. package/dist/runtime/native-pi-extension.d.ts +0 -20
  70. package/dist/runtime/native-pi-extension.js +0 -47
  71. package/dist/runtime/native-sync-store.d.ts +0 -116
  72. package/dist/runtime/native-sync-store.js +0 -495
  73. package/dist/runtime/native-sync.d.ts +0 -65
  74. package/dist/runtime/native-sync.js +0 -326
  75. package/dist/runtime/session-store.d.ts +0 -84
  76. package/dist/runtime/session-store.js +0 -601
@@ -1,326 +0,0 @@
1
- import { readFile, readdir, stat } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { basename, join } from "node:path";
4
- import { createClient } from "../client.js";
5
- import { currentIdentityKey } from "../space.js";
6
- import { canonicalRuntimeRoot, getRuntimeSpaceBinding } from "./space-binding.js";
7
- import { readNativeTranscript, readNativeTranscriptHeader, codexSessionsDirectory, codexArchivedSessionsDirectory, codexRolloutIdOf } from "./native-transcript.js";
8
- import { findRuntimeNativeSession } from "./session-store.js";
9
- import { listNativeSyncStores, nativeIdentityHash, NativeSyncStore } from "./native-sync-store.js";
10
- import { serializeDiagnosticError } from "./diagnostics.js";
11
- export const nativeRuntimeRoot = (spaceId) => join(homedir(), ".local", "state", "cohub", "runtime", spaceId);
12
- export const nativeSyncConfigPath = (runtimeRoot, identity) => join(runtimeRoot, "native", nativeIdentityHash(identity), "config.json");
13
- const piSessionDirectory = (cwd) => {
14
- const custom = process.env.PI_CODING_AGENT_SESSION_DIR?.trim();
15
- if (custom)
16
- return custom;
17
- const agentDir = process.env.PI_CODING_AGENT_DIR?.trim() || join(homedir(), ".pi", "agent");
18
- const safePath = `--${cwd.replace(/^[/\\\\]/, "").replace(/[/\\\\:]/g, "-")}--`;
19
- return join(agentDir, "sessions", safePath);
20
- };
21
- const LARGE_TRANSCRIPT_BYTES = 64 * 1024 * 1024;
22
- async function nativeTranscriptPaths(root, harness, signal) {
23
- // Codex keeps rollouts active or archived, plain or `.zst`-compressed; Pi writes plain JSONL.
24
- const pending = harness === "pi" ? [piSessionDirectory(root)] : [codexSessionsDirectory(), codexArchivedSessionsDirectory()];
25
- const accepts = harness === "pi" ? (name) => name.endsWith(".jsonl") : (name) => name.endsWith(".jsonl") || name.endsWith(".jsonl.zst");
26
- const paths = [];
27
- const errors = [];
28
- while (pending.length) {
29
- signal?.throwIfAborted();
30
- const directory = pending.pop();
31
- if (!directory)
32
- continue;
33
- const entries = await readdir(directory, { withFileTypes: true }).catch((error) => {
34
- if (error.code === "ENOENT")
35
- return [];
36
- errors.push({ harness, path: directory, message: error instanceof Error ? error.message : String(error) });
37
- return [];
38
- });
39
- for (const entry of entries) {
40
- const path = join(directory, entry.name);
41
- if (entry.isDirectory())
42
- pending.push(path);
43
- else if (entry.isFile() && accepts(entry.name))
44
- paths.push(path);
45
- }
46
- }
47
- return { paths, errors };
48
- }
49
- /** Find existing native transcripts for exactly one bound project. */
50
- export async function discoverNativeImportCandidates(root, harnesses, options = {}) {
51
- const candidates = [];
52
- const errors = [];
53
- /** Same-thread rollouts left out of import; they may carry Turns no candidate contains. */
54
- const superseded = [];
55
- for (const harness of harnesses) {
56
- options.signal?.throwIfAborted();
57
- const scanned = await nativeTranscriptPaths(root, harness, options.signal);
58
- errors.push(...scanned.errors);
59
- // One rollout chain per thread: every rollout referenced by a same-thread file is
60
- // superseded by it (rollover / revert); a parent referenced only from a different thread
61
- // (fork) still owns its own Turns and stays importable. Headers are read once and reused.
62
- const headers = new Map();
63
- const absorbedRollouts = new Set();
64
- if (harness === "codex") {
65
- // Bounded concurrency: one descriptor per open file across the global Codex sessions tree.
66
- let next = 0;
67
- const readHeaders = async () => {
68
- for (;;) {
69
- const path = scanned.paths[next++];
70
- if (!path)
71
- return;
72
- try {
73
- headers.set(path, await readNativeTranscriptHeader(path, harness, options.signal));
74
- }
75
- catch (error) {
76
- if (options.signal?.aborted)
77
- throw error;
78
- errors.push({ harness, path, message: error instanceof Error ? error.message : String(error) });
79
- }
80
- }
81
- };
82
- await Promise.all(Array.from({ length: Math.min(16, scanned.paths.length) }, readHeaders));
83
- const threadByRollout = new Map();
84
- for (const header of headers.values())
85
- if (header.rolloutId)
86
- threadByRollout.set(header.rolloutId, header.nativeSessionId);
87
- for (const header of headers.values()) {
88
- const base = header.historyBase;
89
- if (base && threadByRollout.get(base.rolloutId) === header.nativeSessionId)
90
- absorbedRollouts.add(base.rolloutId);
91
- }
92
- }
93
- // One candidate per thread: several same-thread rollouts can survive absorption (two
94
- // siblings reverting from the same parent). The newest by rollout filename timestamp wins;
95
- // the losers are reported, never imported twice under one nativeSessionId.
96
- const supersededSiblings = new Set();
97
- if (harness === "codex") {
98
- const byThread = new Map();
99
- for (const [path, header] of headers) {
100
- if (absorbedRollouts.has(header.rolloutId ?? ""))
101
- continue;
102
- const list = byThread.get(header.nativeSessionId) ?? [];
103
- list.push({ path, rolloutId: header.rolloutId ?? "", sortKey: basename(path) });
104
- byThread.set(header.nativeSessionId, list);
105
- }
106
- for (const list of byThread.values()) {
107
- if (list.length < 2)
108
- continue;
109
- const newest = list.reduce((a, b) => (a.sortKey > b.sortKey ? a : b));
110
- for (const sibling of list)
111
- if (sibling !== newest)
112
- supersededSiblings.add(sibling.path);
113
- }
114
- }
115
- let start = 0;
116
- while (start < scanned.paths.length) {
117
- options.signal?.throwIfAborted();
118
- const paths = scanned.paths.slice(start, start + 2);
119
- const sizes = await Promise.all(paths.map((path) => stat(path).then((value) => value.size).catch(() => 0)));
120
- const concurrency = sizes.some((size) => size > LARGE_TRANSCRIPT_BYTES) ? 1 : 2;
121
- const batch = await Promise.all(paths.slice(0, concurrency).map(async (path) => {
122
- try {
123
- // Pre-scan failures are already reported; do not read the header again.
124
- const header = headers.get(path) ?? (harness === "pi" ? await readNativeTranscriptHeader(path, harness, options.signal) : null);
125
- if (!header)
126
- return null;
127
- if (await canonicalRuntimeRoot(header.cwd) !== root)
128
- return null;
129
- if (header.rolloutId && absorbedRollouts.has(header.rolloutId))
130
- return null;
131
- if (supersededSiblings.has(path))
132
- return { superseded: { path, nativeSessionId: header.nativeSessionId, message: "Superseded by a newer rollout of the same conversation; its Turns are not imported" } };
133
- const transcript = await readNativeTranscript(path, harness, { settled: true, signal: options.signal });
134
- if (transcript.turns.length === 0)
135
- return { error: { harness, path, message: "Transcript contains no importable Turns" } };
136
- return { harness, path, nativeSessionId: transcript.nativeSessionId, turnCount: transcript.turns.length,
137
- sessionStartedAt: transcript.turns.map((turn) => turn.startedAt).sort()[0] };
138
- }
139
- catch (error) {
140
- return { error: { harness, path, message: error instanceof Error ? error.message : String(error) } };
141
- }
142
- }));
143
- for (const result of batch) {
144
- options.signal?.throwIfAborted();
145
- if (!result)
146
- continue;
147
- if ("superseded" in result)
148
- superseded.push(result.superseded);
149
- else if ("error" in result)
150
- errors.push(result.error);
151
- else
152
- candidates.push(result);
153
- options.onProgress?.({ harness, scanned: start + batch.length, candidates: candidates.length });
154
- }
155
- start += concurrency;
156
- }
157
- }
158
- return { candidates, errors, superseded };
159
- }
160
- export function nativeArchiveTransport(spaceId, identity) {
161
- const client = createClient().space(spaceId);
162
- const guard = async (task) => {
163
- if (currentIdentityKey() !== identity)
164
- throw new Error("Native sync account changed");
165
- const result = await task();
166
- if (currentIdentityKey() !== identity)
167
- throw new Error("Native sync account changed");
168
- return result;
169
- };
170
- return {
171
- prepareRuntimeArchive: (...args) => guard(() => client.prepareRuntimeArchive(...args)),
172
- commitRuntimeArchive: (...args) => guard(() => client.commitRuntimeArchive(...args)),
173
- getRuntimeArchive: (...args) => guard(() => client.getRuntimeArchive(...args)),
174
- };
175
- }
176
- export async function readNativeSyncConfig(runtimeRoot, identity) {
177
- try {
178
- const config = JSON.parse(await readFile(nativeSyncConfigPath(runtimeRoot, identity), "utf8"));
179
- if (config.version !== 1 || config.identity !== identity || !Array.isArray(config.harnesses))
180
- throw new Error("Invalid native sync configuration");
181
- return config;
182
- }
183
- catch (error) {
184
- if (error.code === "ENOENT")
185
- return null;
186
- throw error;
187
- }
188
- }
189
- const nativeStores = new Map();
190
- const missing = (error) => error.code === "ENOENT";
191
- /** Canonical alias, but a missing target reports itself instead of failing capture. */
192
- const canonicalPathOrNull = async (path) => canonicalRuntimeRoot(path).catch((error) => { if (missing(error))
193
- return null; throw error; });
194
- /** Local capture only. Neither Pi callbacks nor Codex hooks wait for Cohub's network. */
195
- export async function captureNativeSession(input) {
196
- if (process.env.COHUB_TURN_ID || process.env.COHUB_EXECUTION_TOKEN)
197
- return null;
198
- const identity = currentIdentityKey();
199
- if (!identity)
200
- return null;
201
- // Pi and Codex create their transcripts lazily, so a pending capture for a file that does
202
- // not exist yet (or a workspace that has since been removed) is not a sync failure.
203
- const root = await canonicalPathOrNull(input.cwd);
204
- if (!root)
205
- return null;
206
- const space = await getRuntimeSpaceBinding(root, identity);
207
- if (!space)
208
- return null;
209
- const runtimeRoot = nativeRuntimeRoot(space.spaceId);
210
- const config = await readNativeSyncConfig(runtimeRoot, identity);
211
- if (!config || config.root !== root || config.spaceId !== space.spaceId || !config.harnesses.includes(input.harness))
212
- return null;
213
- const path = await canonicalPathOrNull(input.path);
214
- if (!path)
215
- return null;
216
- const transcript = await readNativeTranscript(path, input.harness, input);
217
- if (await canonicalPathOrNull(transcript.cwd) !== root || input.nativeSessionId && transcript.nativeSessionId !== input.nativeSessionId)
218
- throw new Error("Native transcript belongs to another project or Session");
219
- // The path stays in the cache key: restored Pi working copies share a native ID but not a file.
220
- const key = JSON.stringify([identity, space.spaceId, input.harness, transcript.nativeSessionId, path]);
221
- let store = nativeStores.get(key);
222
- if (!store) {
223
- const transport = nativeArchiveTransport(space.spaceId, identity);
224
- const lineage = input.harness === "codex" ? transcript.lineageRolloutIds ?? [] : [];
225
- const candidates = (await listNativeSyncStores(runtimeRoot, space.spaceId, identity, transport))
226
- .filter((candidate) => candidate.options.harness === input.harness && candidate.options.nativeSessionId === transcript.nativeSessionId);
227
- // A Codex rollover moves appends to a fresh rollout that references the old one: a store
228
- // bound anywhere in the new leaf's lineage is the same conversation, so keep using it —
229
- // the binding migrates inside capture. Exact-path matches win; lineage matches follow.
230
- for (const candidate of candidates) {
231
- const binding = await candidate.binding();
232
- if (binding.path === path) {
233
- store = candidate;
234
- break;
235
- }
236
- if (!store && lineage.includes(codexRolloutIdOf(basename(binding.path)) ?? ""))
237
- store = candidate;
238
- }
239
- if (!store) {
240
- const managed = await findRuntimeNativeSession(runtimeRoot, input.harness, transcript.nativeSessionId, path);
241
- const managedPath = managed ? await canonicalPathOrNull(managed.path) : null;
242
- if (candidates.length && managedPath !== path && !(managedPath && lineage.includes(codexRolloutIdOf(basename(managedPath)) ?? "")))
243
- throw new Error("Native path changed; original bindings retained");
244
- // Restored Pi working copies can share a native ID. Existing Cohub sidecars disambiguate them.
245
- store = new NativeSyncStore({ runtimeRoot, spaceId: space.spaceId, identity, harness: input.harness, nativeSessionId: transcript.nativeSessionId,
246
- instanceKey: managedPath === path ? path : undefined, transport });
247
- }
248
- if (nativeStores.size >= 256)
249
- nativeStores.delete(nativeStores.keys().next().value ?? "");
250
- nativeStores.set(key, store);
251
- }
252
- await store.capture(path, transcript, { sessionStartedAt: input.sessionStartedAt, origin: input.origin, signal: input.signal });
253
- return store;
254
- }
255
- /** The existing Runtime supervisor retries receipts even after the original terminal has exited. */
256
- export function nativeWebSocketTransport(spaceId, identity, send) {
257
- const archive = nativeArchiveTransport(spaceId, identity);
258
- return {
259
- ...archive,
260
- startNativeTurn: async (input) => await send({ type: "start", input }),
261
- completeNativeTurn: async (sessionId, turnId, result) => await send({ type: "complete", sessionId, turnId, result }),
262
- updateNativeTurn: async (sessionId, turnId, progress) => await send({ type: "progress", sessionId, turnId, progress }),
263
- heartbeatNativeTurn: async (sessionId, turnId) => await send({ type: "heartbeat", sessionId, turnId }),
264
- };
265
- }
266
- export const NATIVE_SYNC_SESSION_CONCURRENCY = 4;
267
- export async function runNativeSyncPool(items, concurrency, signal, processItem) {
268
- let next = 0;
269
- const worker = async () => {
270
- while (!signal.aborted) {
271
- const index = next++;
272
- if (index >= items.length)
273
- return;
274
- await processItem(items[index]);
275
- }
276
- };
277
- const results = await Promise.allSettled(Array.from({ length: Math.min(items.length, Math.max(1, concurrency)) }, worker));
278
- const failure = results.find((result) => result.status === "rejected");
279
- if (failure)
280
- throw failure.reason;
281
- }
282
- export function summarizeNativeSyncErrors(errors) {
283
- const groups = new Map();
284
- for (const error of errors) {
285
- const value = serializeDiagnosticError(error);
286
- const key = JSON.stringify([value.name, value.code, value.status, value.causeCode, value.message]);
287
- const group = groups.get(key);
288
- if (group)
289
- group.count += 1;
290
- else
291
- groups.set(key, { name: value.name ?? "Error", message: value.message, ...(value.code ? { code: value.code } : {}), ...(value.status ? { status: value.status } : {}), count: 1 });
292
- }
293
- return [...groups.values()];
294
- }
295
- export async function flushNativeSessions(spaceId, identity, signal, report, transportOverride) {
296
- if (currentIdentityKey() !== identity)
297
- return false;
298
- const runtimeRoot = nativeRuntimeRoot(spaceId);
299
- const config = await readNativeSyncConfig(runtimeRoot, identity);
300
- if (!config || config.spaceId !== spaceId)
301
- return false;
302
- const transport = transportOverride;
303
- if (!transport)
304
- return false;
305
- const stores = await listNativeSyncStores(runtimeRoot, spaceId, identity, transport);
306
- let failed = false;
307
- await runNativeSyncPool(stores, NATIVE_SYNC_SESSION_CONCURRENCY, signal, async (store) => {
308
- try {
309
- signal.throwIfAborted();
310
- const binding = await store.binding();
311
- if (!config.harnesses.includes(binding.harness))
312
- return;
313
- // Codex hooks trigger captures; re-reading here also closes gaps after reconnects or missed hooks.
314
- if (binding.harness === "codex")
315
- await store.capture(binding.path, await readNativeTranscript(binding.path, "codex"));
316
- await store.flush(AbortSignal.any([signal, AbortSignal.timeout(30_000)]));
317
- }
318
- catch (error) {
319
- if (!signal.aborted) {
320
- failed = true;
321
- report(error);
322
- }
323
- }
324
- });
325
- return failed;
326
- }
@@ -1,84 +0,0 @@
1
- import { type RuntimeExecutionEvent, type HarnessArchive, type RuntimePendingExecution, type RuntimeTurnInput } from "@neta-art/cohub";
2
- import { RuntimeArchiveStore, type ArchiveTransport } from "./archive-store.js";
3
- import type { CodexTokenTotals } from "./codex-usage.js";
4
- import { type RuntimeDiagnosticContext, type RuntimeDiagnostics } from "./diagnostics.js";
5
- import type { SessionTurnProjectionClient } from "./turn-projection.js";
6
- export declare class ContextRequiredError extends Error {
7
- }
8
- export type RuntimeSessionStoreOptions = {
9
- stateRoot?: string;
10
- /** Explicit, so a serve path can never silently lose archive uploads. */
11
- archiveTransport: ArchiveTransport | null;
12
- projectionSource: SessionTurnProjectionClient;
13
- };
14
- export type NativeSession = {
15
- version: 1;
16
- sessionId: string;
17
- harness: "pi" | "codex";
18
- nativeSessionId: string;
19
- path: string;
20
- cwd?: string;
21
- throughTurnId: string | null;
22
- revision: string;
23
- checksum: string;
24
- pendingTurnId: string | null;
25
- resultChecksum?: string;
26
- archivePendingTurnId?: string;
27
- codexTokenTotals?: CodexTokenTotals;
28
- projectionVersion?: 1;
29
- sourceSequence?: number | null;
30
- sourceTurnId?: string | null;
31
- sourceFingerprint?: string | null;
32
- nativeLeafId?: string | null;
33
- };
34
- /** Reverse lookup for native clients; the existing Runtime state remains authoritative. */
35
- export declare function findRuntimeNativeSession(root: string, harness: "pi" | "codex", nativeSessionId: string, path?: string): Promise<NativeSession | null>;
36
- /** Local references are hints validated against actual native files, never cloud existence claims. */
37
- export declare class RuntimeSessionStore {
38
- readonly root: string;
39
- readonly archives: RuntimeArchiveStore;
40
- private archiveFlush;
41
- private diagnostics;
42
- private readonly nativeWrites;
43
- private readonly projectionStore;
44
- constructor(spaceId: string, options: RuntimeSessionStoreOptions);
45
- setDiagnostics(diagnostics: RuntimeDiagnostics): void;
46
- private statePath;
47
- private nativePath;
48
- private withNativeWrite;
49
- private syncParentDirectory;
50
- private writeNativeFile;
51
- private appendNativeFile;
52
- private lastNativeRecordId;
53
- private reportProjectionWarnings;
54
- private syncNativeProjection;
55
- pendingExecutionBatches(): AsyncGenerator<RuntimePendingExecution[]>;
56
- flushArchives(signal: AbortSignal): Promise<void>;
57
- private flushArchiveOutbox;
58
- pendingTurnIds(sessionId: string): Promise<string[]>;
59
- prepare(input: RuntimeTurnInput, cwd: string, signal?: AbortSignal): Promise<{
60
- state: NativeSession;
61
- resume: "native" | "restored" | "handoff" | "new";
62
- }>;
63
- started(state: NativeSession, turnId: string): Promise<void>;
64
- private resultPath;
65
- recordResult(state: NativeSession, requestId: string | null, events: RuntimeExecutionEvent[], options?: {
66
- uncertainCleanup?: {
67
- processGroupId: number;
68
- error: string;
69
- };
70
- }): Promise<void>;
71
- recoverResult(input: Pick<RuntimeTurnInput, "sessionId" | "harness" | "turnId">, requestId?: string): Promise<{
72
- state: NativeSession;
73
- events: RuntimeExecutionEvent[];
74
- } | null>;
75
- /**
76
- * Receipt-less recovery: the native file is the authority. When a Turn completed but no
77
- * receipt survived (a crash between native completion and recordResult), rebuild the
78
- * result from transcript bytes appended after the Turn started, without ever rewriting
79
- * native data. Anything ambiguous stays unrecovered.
80
- */
81
- private reconstructResult;
82
- archive(state: NativeSession, turnId: string, diagnosticContext?: RuntimeDiagnosticContext): Promise<HarnessArchive | null>;
83
- acknowledge(state: NativeSession, turnId: string, revision: string): Promise<void>;
84
- }