@codexhost/cli-linux-arm64 0.11.0 → 0.12.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.
@@ -5,6 +5,409 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
+ // ../../harness-discovery/dist/environment.js
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ function targetPath(platform) {
12
+ return platform === "win32" ? path.win32 : path.posix;
13
+ }
14
+ function environmentValue(environment, name) {
15
+ const lowered = name.toLowerCase();
16
+ for (const [key, value] of Object.entries(environment)) {
17
+ if (key.toLowerCase() === lowered)
18
+ return value;
19
+ }
20
+ return void 0;
21
+ }
22
+ function pathDelimiter(platform) {
23
+ return platform === "win32" ? ";" : ":";
24
+ }
25
+ function pathDirectories(environment, platform) {
26
+ return (environmentValue(environment, "PATH") ?? "").split(pathDelimiter(platform)).map((directory) => directory.trim().replace(/^"|"$/gu, "")).filter(Boolean);
27
+ }
28
+ function executableExtensions(command, platform, environment) {
29
+ if (platform !== "win32")
30
+ return [""];
31
+ if (targetPath(platform).extname(command) !== "")
32
+ return [""];
33
+ return (environmentValue(environment, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((extension) => extension.trim()).filter(Boolean);
34
+ }
35
+ function isExecutableFile(filePath, platform) {
36
+ try {
37
+ fs.accessSync(filePath, platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK);
38
+ return fs.statSync(filePath).isFile();
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ function subdirectoryNames(directory) {
44
+ try {
45
+ return fs.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
46
+ } catch {
47
+ return [];
48
+ }
49
+ }
50
+ function newestFirst(names) {
51
+ return [...names].sort((left, right) => right.localeCompare(left, void 0, { numeric: true }));
52
+ }
53
+
54
+ // ../../harness-discovery/dist/invocation.js
55
+ import path2 from "node:path";
56
+ function commandInvocation(command, arguments_, environment, platform = process.platform) {
57
+ const extension = path2.win32.extname(command).toLowerCase();
58
+ if (platform !== "win32" || ![".cmd", ".bat"].includes(extension)) {
59
+ return { command, arguments: [...arguments_], windowsVerbatimArguments: false };
60
+ }
61
+ const quote = (value) => `"${value.replaceAll("%", "%%").replaceAll('"', '""')}"`;
62
+ const commandLine = [command, ...arguments_].map(quote).join(" ");
63
+ return {
64
+ command: environmentValue(environment, "ComSpec") ?? "cmd.exe",
65
+ arguments: ["/d", "/v:off", "/s", "/c", `"${commandLine}"`],
66
+ windowsVerbatimArguments: true
67
+ };
68
+ }
69
+
70
+ // ../../harness-discovery/dist/node-runtime.js
71
+ import path3 from "node:path";
72
+ function withNodeRuntimeOnPath(environment, runtimeExecutable = process.execPath, platform = process.platform) {
73
+ const pathKey = Object.keys(environment).find((name) => name.toLowerCase() === "path") ?? "PATH";
74
+ const delimiter = platform === "win32" ? ";" : ":";
75
+ const runtimeDirectory = path3.dirname(runtimeExecutable);
76
+ const directories = (environment[pathKey] ?? "").split(delimiter).filter(Boolean);
77
+ const equal = platform === "win32" ? (value) => value.toLowerCase() : (value) => value;
78
+ if (!directories.some((directory) => equal(directory) === equal(runtimeDirectory))) {
79
+ directories.push(runtimeDirectory);
80
+ }
81
+ return { ...environment, [pathKey]: directories.join(delimiter) };
82
+ }
83
+
84
+ // ../../harness-discovery/dist/resolve.js
85
+ import os from "node:os";
86
+
87
+ // ../../harness-discovery/dist/version-managers.js
88
+ function versionManagerBinaryDirectories(context) {
89
+ const { platform, environment, home } = context;
90
+ const list = context.subdirectories ?? subdirectoryNames;
91
+ const pathFlavor = targetPath(platform);
92
+ const value = (name) => environmentValue(environment, name);
93
+ const directories = [];
94
+ const versioned = (root, ...tail) => {
95
+ for (const version2 of newestFirst(list(root))) {
96
+ directories.push(pathFlavor.join(root, version2, ...tail));
97
+ }
98
+ };
99
+ if (platform === "win32") {
100
+ const appData = value("APPDATA") ?? pathFlavor.join(home, "AppData", "Roaming");
101
+ const localAppData = value("LOCALAPPDATA") ?? pathFlavor.join(home, "AppData", "Local");
102
+ versioned(pathFlavor.join(appData, "nvm"));
103
+ versioned(value("FNM_DIR") ?? pathFlavor.join(appData, "fnm", "node-versions"), "installation");
104
+ directories.push(pathFlavor.join(localAppData, "Volta", "bin"));
105
+ directories.push(pathFlavor.join(home, ".bun", "bin"));
106
+ const pnpmHome2 = value("PNPM_HOME");
107
+ if (pnpmHome2)
108
+ directories.push(pnpmHome2);
109
+ directories.push(pathFlavor.join(localAppData, "pnpm"));
110
+ return directories;
111
+ }
112
+ versioned(pathFlavor.join(value("NVM_DIR") ?? pathFlavor.join(home, ".nvm"), "versions", "node"), "bin");
113
+ const fnmRoots = [
114
+ value("FNM_DIR"),
115
+ pathFlavor.join(home, ".local", "share", "fnm"),
116
+ pathFlavor.join(home, "Library", "Application Support", "fnm")
117
+ ].filter((root) => root !== void 0);
118
+ for (const root of fnmRoots) {
119
+ versioned(pathFlavor.join(root, "node-versions"), "installation", "bin");
120
+ }
121
+ const voltaHome = value("VOLTA_HOME") ?? pathFlavor.join(home, ".volta");
122
+ versioned(pathFlavor.join(voltaHome, "tools", "image", "node"), "bin");
123
+ directories.push(pathFlavor.join(voltaHome, "bin"));
124
+ const asdfHome = value("ASDF_DATA_DIR") ?? pathFlavor.join(home, ".asdf");
125
+ versioned(pathFlavor.join(asdfHome, "installs", "nodejs"), "bin");
126
+ directories.push(pathFlavor.join(asdfHome, "shims"));
127
+ const nodenvHome = value("NODENV_ROOT") ?? pathFlavor.join(home, ".nodenv");
128
+ versioned(pathFlavor.join(nodenvHome, "versions"), "bin");
129
+ directories.push(pathFlavor.join(nodenvHome, "shims"));
130
+ versioned(pathFlavor.join(value("N_PREFIX") ?? "/usr/local", "n", "versions", "node"), "bin");
131
+ directories.push(pathFlavor.join(home, ".bun", "bin"));
132
+ const pnpmHome = value("PNPM_HOME");
133
+ if (pnpmHome)
134
+ directories.push(pnpmHome);
135
+ directories.push(pathFlavor.join(home, "Library", "pnpm"));
136
+ directories.push(pathFlavor.join(home, ".local", "share", "pnpm"));
137
+ for (const prefix of ["/opt/homebrew/opt", "/usr/local/opt"]) {
138
+ for (const formula of newestFirst(list(prefix).filter((name) => name.startsWith("node@")))) {
139
+ directories.push(pathFlavor.join(prefix, formula, "bin"));
140
+ }
141
+ }
142
+ return directories;
143
+ }
144
+
145
+ // ../../harness-discovery/dist/resolve.js
146
+ var VERSION_MANAGER_ROOTS = "<version-managers>";
147
+ function installRootExtensions(platform) {
148
+ return platform === "win32" ? [".exe", ".cmd"] : [""];
149
+ }
150
+ function resolveHomeDirectory(input) {
151
+ return input.homeDirectory ?? environmentValue(input.environment, "HOME") ?? environmentValue(input.environment, "USERPROFILE") ?? os.homedir();
152
+ }
153
+ function templateVariables(environment, platform, home) {
154
+ const pathFlavor = targetPath(platform);
155
+ return {
156
+ HOME: home,
157
+ USERPROFILE: home,
158
+ APPDATA: environmentValue(environment, "APPDATA") ?? pathFlavor.join(home, "AppData", "Roaming"),
159
+ LOCALAPPDATA: environmentValue(environment, "LOCALAPPDATA") ?? pathFlavor.join(home, "AppData", "Local")
160
+ };
161
+ }
162
+ function joinTemplate(platform, value) {
163
+ const pathFlavor = targetPath(platform);
164
+ const segments = value.split("/");
165
+ const head = segments[0] ?? "";
166
+ const tail = segments.slice(1);
167
+ if (head === "")
168
+ return pathFlavor.join(pathFlavor.sep, ...tail);
169
+ return tail.length === 0 ? head : pathFlavor.join(head, ...tail);
170
+ }
171
+ function expandInstallRoot(template, environment, platform, home) {
172
+ const variables = templateVariables(environment, platform, home);
173
+ let unresolved = false;
174
+ const substituted = template.replace(/^~(?=\/|$)/u, home).replace(/\$\{(\w+)\}/gu, (_match, name) => {
175
+ const value = variables[name] ?? environmentValue(environment, name);
176
+ if (value === void 0 || value === "") {
177
+ unresolved = true;
178
+ return "";
179
+ }
180
+ return value;
181
+ });
182
+ return unresolved ? void 0 : joinTemplate(platform, substituted);
183
+ }
184
+ function installRootDirectories(spec, input, dependencies, platform, home) {
185
+ const templates = (platform === "win32" ? spec.installRoots?.windows : spec.installRoots?.posix) ?? [];
186
+ const directories = [];
187
+ for (const template of templates) {
188
+ if (template === VERSION_MANAGER_ROOTS) {
189
+ directories.push(...versionManagerBinaryDirectories({
190
+ platform,
191
+ environment: input.environment,
192
+ home,
193
+ ...dependencies.subdirectories ? { subdirectories: dependencies.subdirectories } : {}
194
+ }));
195
+ continue;
196
+ }
197
+ const directory = expandInstallRoot(template, input.environment, platform, home);
198
+ if (directory !== void 0)
199
+ directories.push(directory);
200
+ }
201
+ return directories;
202
+ }
203
+ function harnessCandidates(spec, input, dependencies = {}) {
204
+ const platform = input.platform ?? process.platform;
205
+ const pathFlavor = targetPath(platform);
206
+ const configured = input.command ?? (spec.commandEnvironmentVariable ? environmentValue(input.environment, spec.commandEnvironmentVariable) : void 0);
207
+ const command = configured ?? spec.command;
208
+ if (pathFlavor.isAbsolute(command) || command.includes("/") || command.includes("\\")) {
209
+ return [{ candidate: command, source: "configured" }];
210
+ }
211
+ const candidates = [];
212
+ const extensions = executableExtensions(command, platform, input.environment);
213
+ for (const directory of pathDirectories(input.environment, platform)) {
214
+ for (const extension of extensions) {
215
+ candidates.push({
216
+ candidate: pathFlavor.join(directory, command + extension),
217
+ source: "path"
218
+ });
219
+ }
220
+ }
221
+ if (configured !== void 0)
222
+ return candidates;
223
+ const home = resolveHomeDirectory(input);
224
+ for (const directory of installRootDirectories(spec, input, dependencies, platform, home)) {
225
+ for (const extension of installRootExtensions(platform)) {
226
+ candidates.push({
227
+ candidate: pathFlavor.join(directory, spec.command + extension),
228
+ source: "install-root"
229
+ });
230
+ }
231
+ }
232
+ return candidates;
233
+ }
234
+ function resolveHarnessExecutable(spec, input, dependencies = {}) {
235
+ const platform = input.platform ?? process.platform;
236
+ const isExecutable = dependencies.isExecutable ?? ((candidate) => isExecutableFile(candidate, platform));
237
+ const runnable = spec.runnableCandidate ?? ((candidate) => candidate);
238
+ const context = { platform, isExecutable };
239
+ for (const { candidate, source } of harnessCandidates(spec, input, dependencies)) {
240
+ const executable = runnable(candidate, context);
241
+ if (executable !== void 0 && isExecutable(executable))
242
+ return { executable, source };
243
+ }
244
+ return void 0;
245
+ }
246
+
247
+ // ../../harness-discovery/dist/installation.js
248
+ import { execFile } from "node:child_process";
249
+ import { homedir } from "node:os";
250
+ function runInstallationCommand(command, args, environment, timeout = 3e4) {
251
+ const env = withNodeRuntimeOnPath(environment);
252
+ const invocation = /\.[cm]?js$/i.test(command) ? commandInvocation(process.execPath, [command, ...args], env) : commandInvocation(command, args, env);
253
+ return new Promise((resolve, reject) => {
254
+ const child = execFile(invocation.command, invocation.arguments, {
255
+ env,
256
+ cwd: env.HOME ?? env.USERPROFILE ?? homedir(),
257
+ encoding: "utf8",
258
+ timeout,
259
+ killSignal: "SIGKILL",
260
+ maxBuffer: 1024 * 1024,
261
+ windowsHide: true,
262
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments
263
+ }, (error51, stdout, stderr) => {
264
+ if (error51)
265
+ reject(new Error(error51.killed ? "Harness command timed out" : "Harness command failed; check the native installation and retry"));
266
+ else
267
+ resolve((stdout || stderr).trim());
268
+ });
269
+ child.stdin?.end();
270
+ });
271
+ }
272
+ function installationVersion(value) {
273
+ if (typeof value !== "string" || !/^v?\d+(?:\.\d+){1,3}(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(value.trim()))
274
+ throw new Error("Harness returned an invalid version");
275
+ return value.trim().replace(/^v/, "");
276
+ }
277
+ function versionFromOutput(output) {
278
+ return installationVersion(output.match(/\b\d+(?:\.\d+){1,3}(?:-[\w.-]+)?(?:\+[\w.-]+)?\b/)?.[0]);
279
+ }
280
+ function newerInstallationVersion(current, latest) {
281
+ const a = installationVersion(current).replace(/\+.*/, "");
282
+ const b = installationVersion(latest).replace(/\+.*/, "");
283
+ const coreA = a.replace(/-.*/, "").split(".").map(BigInt);
284
+ const coreB = b.replace(/-.*/, "").split(".").map(BigInt);
285
+ for (let i = 0; i < Math.max(coreA.length, coreB.length); i++) {
286
+ if ((coreA[i] ?? 0n) !== (coreB[i] ?? 0n))
287
+ return (coreB[i] ?? 0n) > (coreA[i] ?? 0n);
288
+ }
289
+ const preA = a.includes("-") ? a.slice(a.indexOf("-") + 1).split(".") : [];
290
+ const preB = b.includes("-") ? b.slice(b.indexOf("-") + 1).split(".") : [];
291
+ if (!preA.length || !preB.length)
292
+ return !!preA.length && !preB.length;
293
+ for (let i = 0; i < Math.max(preA.length, preB.length); i++) {
294
+ const x = preA[i];
295
+ const y = preB[i];
296
+ if (x === y)
297
+ continue;
298
+ if (x === void 0 || y === void 0)
299
+ return x === void 0;
300
+ const numericX = /^\d+$/.test(x);
301
+ const numericY = /^\d+$/.test(y);
302
+ if (numericX && numericY) {
303
+ if (BigInt(x) === BigInt(y))
304
+ continue;
305
+ return BigInt(y) > BigInt(x);
306
+ }
307
+ if (numericX !== numericY)
308
+ return numericX;
309
+ return y > x;
310
+ }
311
+ return false;
312
+ }
313
+ function createInstallationManager(options) {
314
+ let checking;
315
+ let updating;
316
+ const check2 = () => checking ??= options.check().finally(() => {
317
+ checking = void 0;
318
+ });
319
+ return (action) => {
320
+ if (updating)
321
+ return updating;
322
+ if (action === "check")
323
+ return check2();
324
+ updating = (async () => {
325
+ const before = await check2();
326
+ if (!before.updateAvailable)
327
+ return before;
328
+ if (!before.canUpdate)
329
+ throw new Error(before.message ?? "This installation cannot be updated automatically");
330
+ await options.update(before);
331
+ const after = await check2();
332
+ if (after.currentVersion === before.currentVersion)
333
+ throw new Error("The Harness version did not change; check the native updater and retry");
334
+ return after;
335
+ })().finally(() => {
336
+ updating = void 0;
337
+ });
338
+ return updating;
339
+ };
340
+ }
341
+ async function fetchInstallationText(url2) {
342
+ const request = async () => {
343
+ const response = await fetch(url2, { signal: AbortSignal.timeout(15e3) });
344
+ if (!response.ok)
345
+ throw new Error("Could not check the latest Harness version");
346
+ return response.text();
347
+ };
348
+ try {
349
+ return await request();
350
+ } catch (error51) {
351
+ if (error51 instanceof DOMException && (error51.name === "AbortError" || error51.name === "TimeoutError"))
352
+ throw error51;
353
+ return request();
354
+ }
355
+ }
356
+
357
+ // ../../harness-discovery/dist/installer.js
358
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
359
+ import { tmpdir } from "node:os";
360
+ import { join } from "node:path";
361
+ function createHarnessInstaller(environment, source) {
362
+ return async () => {
363
+ if (!["darwin", "linux", "win32"].includes(process.platform))
364
+ throw new Error("Unsupported installation platform");
365
+ const env = withNodeRuntimeOnPath(environment);
366
+ if ("npm" in source) {
367
+ const npm = resolveHarnessExecutable({
368
+ id: "npm",
369
+ command: "npm",
370
+ installRoots: {
371
+ posix: [
372
+ "~/.npm-global/bin",
373
+ "~/.local/bin",
374
+ VERSION_MANAGER_ROOTS,
375
+ "/opt/homebrew/bin",
376
+ "/usr/local/bin",
377
+ "/usr/bin"
378
+ ],
379
+ windows: [
380
+ "${APPDATA}/npm",
381
+ VERSION_MANAGER_ROOTS,
382
+ "${ProgramFiles}/nodejs",
383
+ "${LOCALAPPDATA}/Programs/nodejs"
384
+ ]
385
+ }
386
+ }, { environment: env });
387
+ if (!npm)
388
+ throw new Error("Install Node.js and npm first");
389
+ await runInstallationCommand(npm.executable, ["install", "--global", source.npm], {
390
+ ...env,
391
+ ...source.allowScripts ? { npm_config_allow_scripts: source.allowScripts } : {}
392
+ }, 6e5);
393
+ return;
394
+ }
395
+ const windows = process.platform === "win32";
396
+ const url2 = windows ? source.windows : source.posix;
397
+ if (new URL(url2).protocol !== "https:")
398
+ throw new Error("Installer requires HTTPS");
399
+ const script = await fetchInstallationText(url2);
400
+ const directory = await mkdtemp(join(tmpdir(), "codexhost-installer-"));
401
+ try {
402
+ const file2 = join(directory, windows ? "install.ps1" : "install.sh");
403
+ await writeFile(file2, script, { mode: 384 });
404
+ await runInstallationCommand(windows ? "powershell.exe" : source.shell ?? "bash", windows ? ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", file2] : [file2], env, 6e5);
405
+ } finally {
406
+ await rm(directory, { recursive: true, force: true });
407
+ }
408
+ };
409
+ }
410
+
8
411
  // ../../../node_modules/diff/libesm/diff/base.js
9
412
  var Diff = class {
10
413
  diff(oldStr, newStr, options = {}) {
@@ -105,16 +508,16 @@ var Diff = class {
105
508
  }
106
509
  }
107
510
  }
108
- addToPath(path7, added, removed, oldPosInc, options) {
109
- const last = path7.lastComponent;
511
+ addToPath(path8, added, removed, oldPosInc, options) {
512
+ const last = path8.lastComponent;
110
513
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
111
514
  return {
112
- oldPos: path7.oldPos + oldPosInc,
515
+ oldPos: path8.oldPos + oldPosInc,
113
516
  lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
114
517
  };
115
518
  } else {
116
519
  return {
117
- oldPos: path7.oldPos + oldPosInc,
520
+ oldPos: path8.oldPos + oldPosInc,
118
521
  lastComponent: { count: 1, added, removed, previousComponent: last }
119
522
  };
120
523
  }
@@ -527,8 +930,8 @@ function splitLines(text) {
527
930
 
528
931
  // dist/omp-adapter.js
529
932
  import { randomUUID as randomUUID2 } from "node:crypto";
530
- import { realpath as realpath2, stat } from "node:fs/promises";
531
- import path5 from "node:path";
933
+ import { realpath as realpath3, stat as stat2 } from "node:fs/promises";
934
+ import path6 from "node:path";
532
935
 
533
936
  // ../../../node_modules/zod/v4/classic/external.js
534
937
  var external_exports = {};
@@ -1296,10 +1699,10 @@ function mergeDefs(...defs) {
1296
1699
  function cloneDef(schema) {
1297
1700
  return mergeDefs(schema._zod.def);
1298
1701
  }
1299
- function getElementAtPath(obj, path7) {
1300
- if (!path7)
1702
+ function getElementAtPath(obj, path8) {
1703
+ if (!path8)
1301
1704
  return obj;
1302
- return path7.reduce((acc, key) => acc?.[key], obj);
1705
+ return path8.reduce((acc, key) => acc?.[key], obj);
1303
1706
  }
1304
1707
  function promiseAllObject(promisesObj) {
1305
1708
  const keys = Object.keys(promisesObj);
@@ -1708,11 +2111,11 @@ function explicitlyAborted(x, startIndex = 0) {
1708
2111
  }
1709
2112
  return false;
1710
2113
  }
1711
- function prefixIssues(path7, issues) {
2114
+ function prefixIssues(path8, issues) {
1712
2115
  return issues.map((iss) => {
1713
2116
  var _a3;
1714
2117
  (_a3 = iss).path ?? (_a3.path = []);
1715
- iss.path.unshift(path7);
2118
+ iss.path.unshift(path8);
1716
2119
  return iss;
1717
2120
  });
1718
2121
  }
@@ -1859,16 +2262,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
1859
2262
  }
1860
2263
  function formatError(error51, mapper = (issue2) => issue2.message) {
1861
2264
  const fieldErrors = { _errors: [] };
1862
- const processError = (error52, path7 = []) => {
2265
+ const processError = (error52, path8 = []) => {
1863
2266
  for (const issue2 of error52.issues) {
1864
2267
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1865
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
2268
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
1866
2269
  } else if (issue2.code === "invalid_key") {
1867
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
2270
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1868
2271
  } else if (issue2.code === "invalid_element") {
1869
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
2272
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1870
2273
  } else {
1871
- const fullpath = [...path7, ...issue2.path];
2274
+ const fullpath = [...path8, ...issue2.path];
1872
2275
  if (fullpath.length === 0) {
1873
2276
  fieldErrors._errors.push(mapper(issue2));
1874
2277
  } else {
@@ -1895,17 +2298,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
1895
2298
  }
1896
2299
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
1897
2300
  const result = { errors: [] };
1898
- const processError = (error52, path7 = []) => {
2301
+ const processError = (error52, path8 = []) => {
1899
2302
  var _a3, _b;
1900
2303
  for (const issue2 of error52.issues) {
1901
2304
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1902
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
2305
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
1903
2306
  } else if (issue2.code === "invalid_key") {
1904
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
2307
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1905
2308
  } else if (issue2.code === "invalid_element") {
1906
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
2309
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1907
2310
  } else {
1908
- const fullpath = [...path7, ...issue2.path];
2311
+ const fullpath = [...path8, ...issue2.path];
1909
2312
  if (fullpath.length === 0) {
1910
2313
  result.errors.push(mapper(issue2));
1911
2314
  continue;
@@ -1937,8 +2340,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
1937
2340
  }
1938
2341
  function toDotPath(_path) {
1939
2342
  const segs = [];
1940
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1941
- for (const seg of path7) {
2343
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2344
+ for (const seg of path8) {
1942
2345
  if (typeof seg === "number")
1943
2346
  segs.push(`[${seg}]`);
1944
2347
  else if (typeof seg === "symbol")
@@ -14630,13 +15033,13 @@ function resolveRef(ref, ctx) {
14630
15033
  if (!ref.startsWith("#")) {
14631
15034
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
14632
15035
  }
14633
- const path7 = ref.slice(1).split("/").filter(Boolean);
14634
- if (path7.length === 0) {
15036
+ const path8 = ref.slice(1).split("/").filter(Boolean);
15037
+ if (path8.length === 0) {
14635
15038
  return ctx.rootSchema;
14636
15039
  }
14637
15040
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
14638
- if (path7[0] === defsKey) {
14639
- const key = path7[1];
15041
+ if (path8[0] === defsKey) {
15042
+ const key = path8[1];
14640
15043
  if (!key || !ctx.defs[key]) {
14641
15044
  throw new Error(`Reference not found: ${ref}`);
14642
15045
  }
@@ -15130,6 +15533,7 @@ var CONSOLE_HOST_METHODS = Object.freeze([
15130
15533
  "codexhost/harness/display-settings/get",
15131
15534
  "codexhost/harness/display-settings/set",
15132
15535
  "codexhost/harness/inspect",
15536
+ "codexhost/harness/installation",
15133
15537
  "codexhost/harness/launch-settings/get",
15134
15538
  "codexhost/harness/launch-settings/set",
15135
15539
  "codexhost/harness/web-ui/open",
@@ -15776,14 +16180,17 @@ var harnessLaunchSettingsSchema = external_exports.object({
15776
16180
  // ../../shared-contracts/dist/harness-installation.js
15777
16181
  var harnessInstallationParamsSchema = external_exports.object({
15778
16182
  harnessId: harnessIdSchema,
15779
- action: external_exports.enum(["check", "update"])
16183
+ action: external_exports.enum(["check", "update", "install"])
15780
16184
  }).strict();
15781
16185
  var harnessInstallationStateSchema = external_exports.object({
15782
16186
  currentVersion: external_exports.string().min(1).max(128),
15783
16187
  latestVersion: external_exports.string().min(1).max(128),
15784
16188
  updateAvailable: external_exports.boolean(),
15785
16189
  canUpdate: external_exports.boolean(),
15786
- message: external_exports.string().max(2048).optional()
16190
+ // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
16191
+ message: external_exports.string().max(2048).optional(),
16192
+ messageCode: external_exports.string().min(1).max(128).optional(),
16193
+ latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15787
16194
  }).strict();
15788
16195
 
15789
16196
  // ../../shared-contracts/dist/codex-accounts.js
@@ -16085,6 +16492,38 @@ var updateStatusResultSchema = external_exports.strictObject({
16085
16492
  status: updateStatusSchema.nullable()
16086
16493
  });
16087
16494
 
16495
+ // ../../shared-contracts/dist/remote-runtime.js
16496
+ var remoteUpdatePhaseSchema = external_exports.enum([
16497
+ "idle",
16498
+ "installing",
16499
+ "restarting",
16500
+ "succeeded",
16501
+ "failed"
16502
+ ]);
16503
+ var runtimeStatusSchema = external_exports.object({
16504
+ runningVersion: external_exports.string().nullable(),
16505
+ installedVersion: external_exports.string().nullable(),
16506
+ restartRequired: external_exports.boolean(),
16507
+ remote: external_exports.boolean(),
16508
+ updateSupported: external_exports.boolean(),
16509
+ update: external_exports.object({
16510
+ phase: remoteUpdatePhaseSchema,
16511
+ targetVersion: external_exports.string().nullable(),
16512
+ error: external_exports.string().nullable()
16513
+ })
16514
+ });
16515
+ var remoteUpdateParamsSchema = external_exports.object({
16516
+ version: external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
16517
+ }).strict();
16518
+ var remoteSshSetupParamsSchema = external_exports.object({
16519
+ hostname: external_exports.string().min(1).max(1024).refine((value) => !value.startsWith("-") && !/\s/u.test(value)),
16520
+ port: external_exports.number().int().min(1).max(65535).nullable(),
16521
+ identity: external_exports.string().max(4096).nullable(),
16522
+ action: external_exports.enum(["inspect", "install", "update", "repair"]),
16523
+ version: external_exports.string().regex(/^\d+\.\d+\.\d+$/u).nullable()
16524
+ }).strict();
16525
+ var remoteSshSetupResultSchema = external_exports.object({ state: external_exports.enum(["installed", "not-installed"]) }).strict();
16526
+
16088
16527
  // ../../shared-contracts/dist/index.js
16089
16528
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
16090
16529
 
@@ -16792,512 +17231,502 @@ function mapOmpSnapshot(history, state) {
16792
17231
  const changed = modelChange(entry);
16793
17232
  if (changed)
16794
17233
  effectiveModel = changed;
16795
- }
16796
- index = end;
16797
- }
16798
- return { turns };
16799
- }
16800
- function resolveOmpLastTurnBoundary(history) {
16801
- const users = activeOmpEntries(history).filter((entry) => messageRole(entry) === "user");
16802
- const last = users.at(-1);
16803
- return last ? { lastUserEntryId: last.id, sourceTurnCount: users.length } : null;
16804
- }
16805
- function resolveOmpForkBoundary(history, checkpointId) {
16806
- const active = activeOmpEntries(history);
16807
- const users = active.filter((entry) => messageRole(entry) === "user");
16808
- const targetTurnIndex = users.findIndex((entry) => entry.id === checkpointId);
16809
- if (targetTurnIndex < 0)
16810
- throw new Error("Omp Checkpoint is not on the active branch");
16811
- return {
16812
- targetTurnIndex,
16813
- nextUserEntryId: users[targetTurnIndex + 1]?.id ?? null
16814
- };
16815
- }
16816
-
16817
- // dist/omp-session-file.js
16818
- import { createReadStream } from "node:fs";
16819
- import { open, realpath } from "node:fs/promises";
16820
- import readline from "node:readline";
16821
- var MAX_SESSION_HEADER_BYTES = 64 * 1024;
16822
- var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
16823
- function isRecord5(value) {
16824
- return typeof value === "object" && value !== null && !Array.isArray(value);
16825
- }
16826
- function historyEntry(value) {
16827
- if (!isRecord5(value) || value.type === "title" || value.type === "session" || typeof value.id !== "string" || value.id.length === 0 || value.parentId !== null && typeof value.parentId !== "string" || typeof value.type !== "string") {
16828
- return null;
16829
- }
16830
- return value;
16831
- }
16832
- async function readOmpSessionHistory(sessionFile, maxBytes) {
16833
- const entries = [];
16834
- let leafId = null;
16835
- const lines = readline.createInterface({
16836
- input: createReadStream(sessionFile, {
16837
- encoding: "utf8",
16838
- ...maxBytes === void 0 ? {} : { end: maxBytes - 1 }
16839
- }),
16840
- crlfDelay: Number.POSITIVE_INFINITY
16841
- });
16842
- try {
16843
- for await (const line of lines) {
16844
- if (line.length === 0)
16845
- continue;
16846
- let parsed;
16847
- try {
16848
- parsed = JSON.parse(line);
16849
- } catch {
16850
- continue;
16851
- }
16852
- const entry = historyEntry(parsed);
16853
- if (!entry)
16854
- continue;
16855
- entries.push(entry);
16856
- leafId = entry.id;
16857
- }
16858
- } finally {
16859
- lines.close();
16860
- }
16861
- return { entries, leafId };
16862
- }
16863
- async function readOmpSessionHeader(sessionFile) {
16864
- const handle = await open(sessionFile, "r");
16865
- try {
16866
- const buffer = Buffer.allocUnsafe(MAX_SESSION_HEADER_BYTES);
16867
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
16868
- const contents = buffer.subarray(0, bytesRead);
16869
- const newline = contents.indexOf(10);
16870
- if (newline < 0 && bytesRead === buffer.length) {
16871
- throw new Error("Omp Session header exceeds the supported size");
16872
- }
16873
- const contentsText = utf8Decoder.decode(contents);
16874
- for (const line of contentsText.split("\n")) {
16875
- if (line.length === 0)
16876
- continue;
16877
- let parsed;
16878
- try {
16879
- parsed = JSON.parse(line);
16880
- } catch {
16881
- continue;
16882
- }
16883
- if (!isRecord5(parsed) || parsed.type !== "session")
16884
- continue;
16885
- if (typeof parsed.id !== "string" || parsed.id.length === 0 || typeof parsed.cwd !== "string" || parsed.cwd.length === 0) {
16886
- throw new Error("Omp Session header is invalid");
16887
- }
16888
- return { type: "session", id: parsed.id, cwd: parsed.cwd };
16889
- }
16890
- throw new Error("Omp Session header is invalid");
16891
- } finally {
16892
- await handle.close();
16893
- }
16894
- }
16895
- async function verifyOmpSessionCwd(input) {
16896
- if (!input.sessionFile)
16897
- throw new Error("Omp Fork Session has no persisted Session file");
16898
- const header = await readOmpSessionHeader(input.sessionFile);
16899
- if (header.id !== input.sessionId) {
16900
- throw new Error("Omp Fork Session header identity does not match RPC state");
16901
- }
16902
- const [actualCwd, expectedCwd] = await Promise.all([
16903
- realpath(header.cwd),
16904
- realpath(input.expectedCwd)
16905
- ]);
16906
- if (actualCwd !== expectedCwd) {
16907
- throw new Error("Omp Fork Session did not bind the requested cwd");
16908
- }
16909
- }
16910
-
16911
- // dist/omp-last-turn-rollback.js
16912
- function modelFromState(state) {
16913
- if (state.provider === null && state.modelId === null)
16914
- return null;
16915
- if (state.provider === null || state.modelId === null) {
16916
- throw new Error("Omp state contains a partial Model identity");
16917
- }
16918
- return { provider: state.provider, id: state.modelId };
16919
- }
16920
- async function rollbackOmpLastTurn(transport, sourceSessionId, cwd) {
16921
- const startupState = transport.state;
16922
- const startupSessionId = startupState.sessionId;
16923
- const currentModel = modelFromState(startupState);
16924
- const currentThinking = startupState.thinkingLevel;
16925
- const copiedHistory = await transport.getEntries();
16926
- const boundary = resolveOmpLastTurnBoundary(copiedHistory);
16927
- if (!boundary)
16928
- return { ok: false, reason: "empty" };
16929
- const sourceSnapshot = mapOmpSnapshot(copiedHistory, {
16930
- sessionId: startupSessionId,
16931
- model: currentModel
16932
- });
16933
- let state = await transport.fork(boundary.lastUserEntryId);
16934
- if (state.sessionId === sourceSessionId || state.sessionId === startupSessionId) {
16935
- throw new Error("Omp last-Turn rollback did not create a distinct Native Session");
16936
- }
16937
- if (!sameOmpModel(modelFromState(state), currentModel)) {
16938
- if (!currentModel)
16939
- throw new Error("Omp last-Turn rollback changed the current Model");
16940
- state = await transport.selectModel(currentModel);
16941
- }
16942
- if (state.thinkingLevel !== currentThinking) {
16943
- if (!currentThinking)
16944
- throw new Error("Omp last-Turn rollback changed current Thinking");
16945
- state = await transport.selectThinkingOption(currentThinking);
16946
- }
16947
- if (!sameOmpModel(modelFromState(state), currentModel) || state.thinkingLevel !== currentThinking) {
16948
- throw new Error("Omp last-Turn rollback could not restore current Model and Thinking");
16949
- }
16950
- const snapshot = mapOmpSnapshot(await transport.getEntries(), {
16951
- sessionId: state.sessionId,
16952
- model: modelFromState(state)
16953
- });
16954
- const expectedTurnKeys = sourceSnapshot.turns.slice(0, -1).map((turn) => turn.nativeTurnRef.nativeTurnKey);
16955
- const actualTurnKeys = snapshot.turns.map((turn) => turn.nativeTurnRef.nativeTurnKey);
16956
- if (snapshot.turns.length !== boundary.sourceTurnCount - 1 || actualTurnKeys.some((key, index) => key !== expectedTurnKeys[index])) {
16957
- throw new Error("Omp last-Turn rollback did not produce the exact retained history prefix");
16958
- }
16959
- await transport.verifySessionCwd(cwd);
16960
- return { ok: true };
16961
- }
16962
-
16963
- // dist/omp-rpc-session.js
16964
- import { spawn, spawnSync } from "node:child_process";
16965
- import { randomUUID } from "node:crypto";
16966
- import path4 from "node:path";
16967
-
16968
- // ../../harness-discovery/dist/environment.js
16969
- import fs from "node:fs";
16970
- import path from "node:path";
16971
- function targetPath(platform) {
16972
- return platform === "win32" ? path.win32 : path.posix;
16973
- }
16974
- function environmentValue(environment, name) {
16975
- const lowered = name.toLowerCase();
16976
- for (const [key, value] of Object.entries(environment)) {
16977
- if (key.toLowerCase() === lowered)
16978
- return value;
16979
- }
16980
- return void 0;
16981
- }
16982
- function pathDelimiter(platform) {
16983
- return platform === "win32" ? ";" : ":";
16984
- }
16985
- function pathDirectories(environment, platform) {
16986
- return (environmentValue(environment, "PATH") ?? "").split(pathDelimiter(platform)).map((directory) => directory.trim().replace(/^"|"$/gu, "")).filter(Boolean);
16987
- }
16988
- function executableExtensions(command, platform, environment) {
16989
- if (platform !== "win32")
16990
- return [""];
16991
- if (targetPath(platform).extname(command) !== "")
16992
- return [""];
16993
- return (environmentValue(environment, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((extension) => extension.trim()).filter(Boolean);
16994
- }
16995
- function isExecutableFile(filePath, platform) {
16996
- try {
16997
- fs.accessSync(filePath, platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK);
16998
- return fs.statSync(filePath).isFile();
16999
- } catch {
17000
- return false;
17001
- }
17002
- }
17003
- function subdirectoryNames(directory) {
17004
- try {
17005
- return fs.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
17006
- } catch {
17007
- return [];
17234
+ }
17235
+ index = end;
17008
17236
  }
17237
+ return { turns };
17009
17238
  }
17010
- function newestFirst(names) {
17011
- return [...names].sort((left, right) => right.localeCompare(left, void 0, { numeric: true }));
17239
+ function resolveOmpLastTurnBoundary(history) {
17240
+ const users = activeOmpEntries(history).filter((entry) => messageRole(entry) === "user");
17241
+ const last = users.at(-1);
17242
+ return last ? { lastUserEntryId: last.id, sourceTurnCount: users.length } : null;
17012
17243
  }
17013
-
17014
- // ../../harness-discovery/dist/invocation.js
17015
- import path2 from "node:path";
17016
- function commandInvocation(command, arguments_, environment, platform = process.platform) {
17017
- const extension = path2.win32.extname(command).toLowerCase();
17018
- if (platform !== "win32" || ![".cmd", ".bat"].includes(extension)) {
17019
- return { command, arguments: [...arguments_], windowsVerbatimArguments: false };
17020
- }
17021
- const quote = (value) => `"${value.replaceAll("%", "%%").replaceAll('"', '""')}"`;
17022
- const commandLine = [command, ...arguments_].map(quote).join(" ");
17244
+ function resolveOmpForkBoundary(history, checkpointId) {
17245
+ const active = activeOmpEntries(history);
17246
+ const users = active.filter((entry) => messageRole(entry) === "user");
17247
+ const targetTurnIndex = users.findIndex((entry) => entry.id === checkpointId);
17248
+ if (targetTurnIndex < 0)
17249
+ throw new Error("Omp Checkpoint is not on the active branch");
17023
17250
  return {
17024
- command: environmentValue(environment, "ComSpec") ?? "cmd.exe",
17025
- arguments: ["/d", "/v:off", "/s", "/c", `"${commandLine}"`],
17026
- windowsVerbatimArguments: true
17251
+ targetTurnIndex,
17252
+ nextUserEntryId: users[targetTurnIndex + 1]?.id ?? null
17027
17253
  };
17028
17254
  }
17029
17255
 
17030
- // ../../harness-discovery/dist/node-runtime.js
17031
- import path3 from "node:path";
17032
- function withNodeRuntimeOnPath(environment, runtimeExecutable = process.execPath, platform = process.platform) {
17033
- const pathKey = Object.keys(environment).find((name) => name.toLowerCase() === "path") ?? "PATH";
17034
- const delimiter = platform === "win32" ? ";" : ":";
17035
- const runtimeDirectory = path3.dirname(runtimeExecutable);
17036
- const directories = (environment[pathKey] ?? "").split(delimiter).filter(Boolean);
17037
- const equal = platform === "win32" ? (value) => value.toLowerCase() : (value) => value;
17038
- if (!directories.some((directory) => equal(directory) === equal(runtimeDirectory))) {
17039
- directories.push(runtimeDirectory);
17256
+ // dist/omp-session-file.js
17257
+ import { createReadStream } from "node:fs";
17258
+ import { open, realpath } from "node:fs/promises";
17259
+ import readline from "node:readline";
17260
+ var MAX_SESSION_HEADER_BYTES = 64 * 1024;
17261
+ var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
17262
+ function isRecord5(value) {
17263
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17264
+ }
17265
+ function historyEntry(value) {
17266
+ if (!isRecord5(value) || value.type === "title" || value.type === "session" || typeof value.id !== "string" || value.id.length === 0 || value.parentId !== null && typeof value.parentId !== "string" || typeof value.type !== "string") {
17267
+ return null;
17040
17268
  }
17041
- return { ...environment, [pathKey]: directories.join(delimiter) };
17269
+ return value;
17042
17270
  }
17043
-
17044
- // ../../harness-discovery/dist/resolve.js
17045
- import os from "node:os";
17046
-
17047
- // ../../harness-discovery/dist/version-managers.js
17048
- function versionManagerBinaryDirectories(context) {
17049
- const { platform, environment, home } = context;
17050
- const list = context.subdirectories ?? subdirectoryNames;
17051
- const pathFlavor = targetPath(platform);
17052
- const value = (name) => environmentValue(environment, name);
17053
- const directories = [];
17054
- const versioned = (root, ...tail) => {
17055
- for (const version2 of newestFirst(list(root))) {
17056
- directories.push(pathFlavor.join(root, version2, ...tail));
17271
+ async function readOmpSessionHistory(sessionFile, maxBytes) {
17272
+ const entries = [];
17273
+ let leafId = null;
17274
+ const lines = readline.createInterface({
17275
+ input: createReadStream(sessionFile, {
17276
+ encoding: "utf8",
17277
+ ...maxBytes === void 0 ? {} : { end: maxBytes - 1 }
17278
+ }),
17279
+ crlfDelay: Number.POSITIVE_INFINITY
17280
+ });
17281
+ try {
17282
+ for await (const line of lines) {
17283
+ if (line.length === 0)
17284
+ continue;
17285
+ let parsed;
17286
+ try {
17287
+ parsed = JSON.parse(line);
17288
+ } catch {
17289
+ continue;
17290
+ }
17291
+ const entry = historyEntry(parsed);
17292
+ if (!entry)
17293
+ continue;
17294
+ entries.push(entry);
17295
+ leafId = entry.id;
17057
17296
  }
17058
- };
17059
- if (platform === "win32") {
17060
- const appData = value("APPDATA") ?? pathFlavor.join(home, "AppData", "Roaming");
17061
- const localAppData = value("LOCALAPPDATA") ?? pathFlavor.join(home, "AppData", "Local");
17062
- versioned(pathFlavor.join(appData, "nvm"));
17063
- versioned(value("FNM_DIR") ?? pathFlavor.join(appData, "fnm", "node-versions"), "installation");
17064
- directories.push(pathFlavor.join(localAppData, "Volta", "bin"));
17065
- directories.push(pathFlavor.join(home, ".bun", "bin"));
17066
- const pnpmHome2 = value("PNPM_HOME");
17067
- if (pnpmHome2)
17068
- directories.push(pnpmHome2);
17069
- directories.push(pathFlavor.join(localAppData, "pnpm"));
17070
- return directories;
17071
- }
17072
- versioned(pathFlavor.join(value("NVM_DIR") ?? pathFlavor.join(home, ".nvm"), "versions", "node"), "bin");
17073
- const fnmRoots = [
17074
- value("FNM_DIR"),
17075
- pathFlavor.join(home, ".local", "share", "fnm"),
17076
- pathFlavor.join(home, "Library", "Application Support", "fnm")
17077
- ].filter((root) => root !== void 0);
17078
- for (const root of fnmRoots) {
17079
- versioned(pathFlavor.join(root, "node-versions"), "installation", "bin");
17297
+ } finally {
17298
+ lines.close();
17080
17299
  }
17081
- const voltaHome = value("VOLTA_HOME") ?? pathFlavor.join(home, ".volta");
17082
- versioned(pathFlavor.join(voltaHome, "tools", "image", "node"), "bin");
17083
- directories.push(pathFlavor.join(voltaHome, "bin"));
17084
- const asdfHome = value("ASDF_DATA_DIR") ?? pathFlavor.join(home, ".asdf");
17085
- versioned(pathFlavor.join(asdfHome, "installs", "nodejs"), "bin");
17086
- directories.push(pathFlavor.join(asdfHome, "shims"));
17087
- const nodenvHome = value("NODENV_ROOT") ?? pathFlavor.join(home, ".nodenv");
17088
- versioned(pathFlavor.join(nodenvHome, "versions"), "bin");
17089
- directories.push(pathFlavor.join(nodenvHome, "shims"));
17090
- versioned(pathFlavor.join(value("N_PREFIX") ?? "/usr/local", "n", "versions", "node"), "bin");
17091
- directories.push(pathFlavor.join(home, ".bun", "bin"));
17092
- const pnpmHome = value("PNPM_HOME");
17093
- if (pnpmHome)
17094
- directories.push(pnpmHome);
17095
- directories.push(pathFlavor.join(home, "Library", "pnpm"));
17096
- directories.push(pathFlavor.join(home, ".local", "share", "pnpm"));
17097
- for (const prefix of ["/opt/homebrew/opt", "/usr/local/opt"]) {
17098
- for (const formula of newestFirst(list(prefix).filter((name) => name.startsWith("node@")))) {
17099
- directories.push(pathFlavor.join(prefix, formula, "bin"));
17300
+ return { entries, leafId };
17301
+ }
17302
+ async function readOmpSessionHeader(sessionFile) {
17303
+ const handle = await open(sessionFile, "r");
17304
+ try {
17305
+ const buffer = Buffer.allocUnsafe(MAX_SESSION_HEADER_BYTES);
17306
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
17307
+ const contents = buffer.subarray(0, bytesRead);
17308
+ const newline = contents.indexOf(10);
17309
+ if (newline < 0 && bytesRead === buffer.length) {
17310
+ throw new Error("Omp Session header exceeds the supported size");
17311
+ }
17312
+ const contentsText = utf8Decoder.decode(contents);
17313
+ for (const line of contentsText.split("\n")) {
17314
+ if (line.length === 0)
17315
+ continue;
17316
+ let parsed;
17317
+ try {
17318
+ parsed = JSON.parse(line);
17319
+ } catch {
17320
+ continue;
17321
+ }
17322
+ if (!isRecord5(parsed) || parsed.type !== "session")
17323
+ continue;
17324
+ if (typeof parsed.id !== "string" || parsed.id.length === 0 || typeof parsed.cwd !== "string" || parsed.cwd.length === 0) {
17325
+ throw new Error("Omp Session header is invalid");
17326
+ }
17327
+ return { type: "session", id: parsed.id, cwd: parsed.cwd };
17100
17328
  }
17329
+ throw new Error("Omp Session header is invalid");
17330
+ } finally {
17331
+ await handle.close();
17101
17332
  }
17102
- return directories;
17103
17333
  }
17104
-
17105
- // ../../harness-discovery/dist/resolve.js
17106
- var VERSION_MANAGER_ROOTS = "<version-managers>";
17107
- function installRootExtensions(platform) {
17108
- return platform === "win32" ? [".exe", ".cmd"] : [""];
17109
- }
17110
- function resolveHomeDirectory(input) {
17111
- return input.homeDirectory ?? environmentValue(input.environment, "HOME") ?? environmentValue(input.environment, "USERPROFILE") ?? os.homedir();
17334
+ async function verifyOmpSessionCwd(input) {
17335
+ if (!input.sessionFile)
17336
+ throw new Error("Omp Fork Session has no persisted Session file");
17337
+ const header = await readOmpSessionHeader(input.sessionFile);
17338
+ if (header.id !== input.sessionId) {
17339
+ throw new Error("Omp Fork Session header identity does not match RPC state");
17340
+ }
17341
+ const [actualCwd, expectedCwd] = await Promise.all([
17342
+ realpath(header.cwd),
17343
+ realpath(input.expectedCwd)
17344
+ ]);
17345
+ if (actualCwd !== expectedCwd) {
17346
+ throw new Error("Omp Fork Session did not bind the requested cwd");
17347
+ }
17112
17348
  }
17113
- function templateVariables(environment, platform, home) {
17114
- const pathFlavor = targetPath(platform);
17115
- return {
17116
- HOME: home,
17117
- USERPROFILE: home,
17118
- APPDATA: environmentValue(environment, "APPDATA") ?? pathFlavor.join(home, "AppData", "Roaming"),
17119
- LOCALAPPDATA: environmentValue(environment, "LOCALAPPDATA") ?? pathFlavor.join(home, "AppData", "Local")
17120
- };
17349
+
17350
+ // dist/session-import.js
17351
+ import { createReadStream as createReadStream2 } from "node:fs";
17352
+ import { opendir, realpath as realpath2, stat } from "node:fs/promises";
17353
+ import os2 from "node:os";
17354
+ import path4 from "node:path";
17355
+ import { createInterface } from "node:readline";
17356
+
17357
+ // ../../harness-adapter/dist/session-import.js
17358
+ import { lstat } from "node:fs/promises";
17359
+ var SessionImportChangedError = class extends Error {
17360
+ constructor(message3 = "Native Session changed during discovery; refresh and retry") {
17361
+ super(message3);
17362
+ this.name = "SessionImportChangedError";
17363
+ }
17364
+ };
17365
+ function isMissingFileError(error51) {
17366
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === "ENOENT";
17121
17367
  }
17122
- function joinTemplate(platform, value) {
17123
- const pathFlavor = targetPath(platform);
17124
- const segments = value.split("/");
17125
- const head = segments[0] ?? "";
17126
- const tail = segments.slice(1);
17127
- if (head === "")
17128
- return pathFlavor.join(pathFlavor.sep, ...tail);
17129
- return tail.length === 0 ? head : pathFlavor.join(head, ...tail);
17368
+ function sameFileFingerprint(left, right) {
17369
+ return left === null || right === null ? left === right : left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
17130
17370
  }
17131
- function expandInstallRoot(template, environment, platform, home) {
17132
- const variables = templateVariables(environment, platform, home);
17133
- let unresolved = false;
17134
- const substituted = template.replace(/^~(?=\/|$)/u, home).replace(/\$\{(\w+)\}/gu, (_match, name) => {
17135
- const value = variables[name] ?? environmentValue(environment, name);
17136
- if (value === void 0 || value === "") {
17137
- unresolved = true;
17138
- return "";
17139
- }
17140
- return value;
17141
- });
17142
- return unresolved ? void 0 : joinTemplate(platform, substituted);
17371
+ var STORAGE_ACCESS_ERRORS = /* @__PURE__ */ new Set(["EACCES", "EPERM", "EIO", "EMFILE", "ENFILE"]);
17372
+ function isStorageAccessError(error51) {
17373
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && typeof error51.code === "string" && STORAGE_ACCESS_ERRORS.has(error51.code);
17143
17374
  }
17144
- function installRootDirectories(spec, input, dependencies, platform, home) {
17145
- const templates = (platform === "win32" ? spec.installRoots?.windows : spec.installRoots?.posix) ?? [];
17146
- const directories = [];
17147
- for (const template of templates) {
17148
- if (template === VERSION_MANAGER_ROOTS) {
17149
- directories.push(...versionManagerBinaryDirectories({
17150
- platform,
17151
- environment: input.environment,
17152
- home,
17153
- ...dependencies.subdirectories ? { subdirectories: dependencies.subdirectories } : {}
17154
- }));
17155
- continue;
17156
- }
17157
- const directory = expandInstallRoot(template, input.environment, platform, home);
17158
- if (directory !== void 0)
17159
- directories.push(directory);
17375
+ async function optionalLstat(file2) {
17376
+ try {
17377
+ return await lstat(file2);
17378
+ } catch (error51) {
17379
+ if (isMissingFileError(error51))
17380
+ return null;
17381
+ throw error51;
17160
17382
  }
17161
- return directories;
17162
17383
  }
17163
- function harnessCandidates(spec, input, dependencies = {}) {
17164
- const platform = input.platform ?? process.platform;
17165
- const pathFlavor = targetPath(platform);
17166
- const configured = input.command ?? (spec.commandEnvironmentVariable ? environmentValue(input.environment, spec.commandEnvironmentVariable) : void 0);
17167
- const command = configured ?? spec.command;
17168
- if (pathFlavor.isAbsolute(command) || command.includes("/") || command.includes("\\")) {
17169
- return [{ candidate: command, source: "configured" }];
17384
+ async function readStableFiles(files, read) {
17385
+ const before = await Promise.all(files.map(optionalLstat));
17386
+ const value = await read(before);
17387
+ const after = await Promise.all(files.map(optionalLstat));
17388
+ if (before.some((info, index) => !sameFileFingerprint(info, after[index] ?? null)))
17389
+ throw new SessionImportChangedError();
17390
+ return value;
17391
+ }
17392
+ function sessionImportTitle(value, maxLength = HARNESS_SESSION_IMPORT_TITLE_MAX_LENGTH) {
17393
+ if (typeof value !== "string")
17394
+ return null;
17395
+ const normalized = value.replaceAll("\0", "").replaceAll(/\s+/gu, " ").trim();
17396
+ if (!normalized)
17397
+ return null;
17398
+ if (normalized.length <= maxLength)
17399
+ return normalized;
17400
+ const characters = [...normalized];
17401
+ if (characters.length <= maxLength)
17402
+ return normalized;
17403
+ return `${characters.slice(0, maxLength - 1).join("").trimEnd()}…`;
17404
+ }
17405
+ function sessionImportCandidate(input) {
17406
+ const time3 = typeof input.updatedAt === "number" ? input.updatedAt : typeof input.updatedAt === "string" ? Date.parse(input.updatedAt) : Number.NaN;
17407
+ const parsed = harnessSessionImportCandidateSchema.safeParse({
17408
+ nativeSessionId: input.nativeSessionId,
17409
+ cwd: input.cwd,
17410
+ title: typeof input.title === "string" && input.title.trim().length > 0 ? input.title : null,
17411
+ updatedAt: Math.floor(time3),
17412
+ // Unknown must stay unknown; only an explicit boolean is a native statement.
17413
+ running: typeof input.running === "boolean" ? input.running : null
17414
+ });
17415
+ return parsed.success ? parsed.data : null;
17416
+ }
17417
+ var SessionImportScope = class {
17418
+ #abort = new AbortController();
17419
+ #options;
17420
+ #pending = /* @__PURE__ */ new Set();
17421
+ constructor(options) {
17422
+ this.#options = options;
17170
17423
  }
17171
- const candidates = [];
17172
- const extensions = executableExtensions(command, platform, input.environment);
17173
- for (const directory of pathDirectories(input.environment, platform)) {
17174
- for (const extension of extensions) {
17175
- candidates.push({
17176
- candidate: pathFlavor.join(directory, command + extension),
17177
- source: "path"
17178
- });
17179
- }
17424
+ #closed() {
17425
+ return {
17426
+ ok: false,
17427
+ error: { code: "invalidState", message: this.#options.closedMessage, retryable: false }
17428
+ };
17180
17429
  }
17181
- if (configured !== void 0)
17182
- return candidates;
17183
- const home = resolveHomeDirectory(input);
17184
- for (const directory of installRootDirectories(spec, input, dependencies, platform, home)) {
17185
- for (const extension of installRootExtensions(platform)) {
17186
- candidates.push({
17187
- candidate: pathFlavor.join(directory, spec.command + extension),
17188
- source: "install-root"
17189
- });
17430
+ read(operation) {
17431
+ const signal = this.#abort.signal;
17432
+ if (signal.aborted)
17433
+ return Promise.resolve(this.#closed());
17434
+ let started;
17435
+ try {
17436
+ started = operation(signal);
17437
+ } catch (error51) {
17438
+ started = Promise.reject(error51 instanceof Error ? error51 : new Error("Session read failed"));
17190
17439
  }
17440
+ const request = started.then((value) => signal.aborted ? this.#closed() : { ok: true, value }).catch(() => signal.aborted ? this.#closed() : {
17441
+ ok: false,
17442
+ error: {
17443
+ code: "unavailable",
17444
+ message: this.#options.unavailableMessage,
17445
+ retryable: true
17446
+ }
17447
+ }).finally(() => this.#pending.delete(request));
17448
+ this.#pending.add(request);
17449
+ return request;
17191
17450
  }
17192
- return candidates;
17193
- }
17194
- function resolveHarnessExecutable(spec, input, dependencies = {}) {
17195
- const platform = input.platform ?? process.platform;
17196
- const isExecutable = dependencies.isExecutable ?? ((candidate) => isExecutableFile(candidate, platform));
17197
- const runnable = spec.runnableCandidate ?? ((candidate) => candidate);
17198
- const context = { platform, isExecutable };
17199
- for (const { candidate, source } of harnessCandidates(spec, input, dependencies)) {
17200
- const executable = runnable(candidate, context);
17201
- if (executable !== void 0 && isExecutable(executable))
17202
- return { executable, source };
17451
+ /** `read` for `resolveCandidate`: a null source is the selected Session having disappeared. */
17452
+ async resolve(operation) {
17453
+ const result = await this.read(operation);
17454
+ if (!result.ok)
17455
+ return result;
17456
+ return result.value ? { ok: true, value: result.value } : {
17457
+ ok: false,
17458
+ error: {
17459
+ code: "sessionNotFound",
17460
+ message: this.#options.notFoundMessage,
17461
+ retryable: false
17462
+ }
17463
+ };
17203
17464
  }
17204
- return void 0;
17205
- }
17465
+ /** Idempotent. Resolves after every started read has settled. */
17466
+ async close() {
17467
+ this.#abort.abort();
17468
+ await Promise.allSettled(this.#pending);
17469
+ }
17470
+ };
17206
17471
 
17207
- // ../../harness-discovery/dist/installation.js
17208
- import { execFile } from "node:child_process";
17209
- import { homedir } from "node:os";
17210
- function runInstallationCommand(command, args, environment, timeout = 3e4) {
17211
- const env = withNodeRuntimeOnPath(environment);
17212
- const invocation = /\.[cm]?js$/i.test(command) ? commandInvocation(process.execPath, [command, ...args], env) : commandInvocation(command, args, env);
17213
- return new Promise((resolve, reject) => {
17214
- const child = execFile(invocation.command, invocation.arguments, {
17215
- env,
17216
- cwd: env.HOME ?? env.USERPROFILE ?? homedir(),
17217
- encoding: "utf8",
17218
- timeout,
17219
- killSignal: "SIGKILL",
17220
- maxBuffer: 1024 * 1024,
17221
- windowsHide: true,
17222
- windowsVerbatimArguments: invocation.windowsVerbatimArguments
17223
- }, (error51, stdout, stderr) => {
17224
- if (error51)
17225
- reject(new Error(error51.killed ? "Harness command timed out" : "Harness command failed; check the native installation and retry"));
17226
- else
17227
- resolve((stdout || stderr).trim());
17228
- });
17229
- child.stdin?.end();
17230
- });
17472
+ // dist/session-import.js
17473
+ function isRecord6(value) {
17474
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17231
17475
  }
17232
- function installationVersion(value) {
17233
- if (typeof value !== "string" || !/^v?\d+(?:\.\d+){1,3}(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(value.trim()))
17234
- throw new Error("Harness returned an invalid version");
17235
- return value.trim().replace(/^v/, "");
17476
+ function expandDirectory(value, home) {
17477
+ return path4.resolve(value === "~" ? home : value.startsWith(`~${path4.sep}`) || value.startsWith("~/") ? path4.join(home, value.slice(2)) : value);
17236
17478
  }
17237
- function versionFromOutput(output) {
17238
- return installationVersion(output.match(/\b\d+(?:\.\d+){1,3}(?:-[\w.-]+)?(?:\+[\w.-]+)?\b/)?.[0]);
17479
+ function ompSessionImportDirectory(environment) {
17480
+ const home = (process.platform === "win32" ? environment.USERPROFILE : environment.HOME) || os2.homedir();
17481
+ const custom2 = environment.PI_CODING_AGENT_SESSION_DIR;
17482
+ if (custom2)
17483
+ return { directory: expandDirectory(custom2, home), flat: true };
17484
+ const agent = environment.PI_CODING_AGENT_DIR;
17485
+ return {
17486
+ directory: path4.join(agent ? expandDirectory(agent, home) : path4.join(home, ".omp", "agent"), "sessions"),
17487
+ flat: false
17488
+ };
17239
17489
  }
17240
- function newerInstallationVersion(current, latest) {
17241
- const a = installationVersion(current).replace(/\+.*/, "");
17242
- const b = installationVersion(latest).replace(/\+.*/, "");
17243
- const coreA = a.replace(/-.*/, "").split(".").map(BigInt);
17244
- const coreB = b.replace(/-.*/, "").split(".").map(BigInt);
17245
- for (let i = 0; i < Math.max(coreA.length, coreB.length); i++) {
17246
- if ((coreA[i] ?? 0n) !== (coreB[i] ?? 0n))
17247
- return (coreB[i] ?? 0n) > (coreA[i] ?? 0n);
17248
- }
17249
- const preA = a.includes("-") ? a.slice(a.indexOf("-") + 1).split(".") : [];
17250
- const preB = b.includes("-") ? b.slice(b.indexOf("-") + 1).split(".") : [];
17251
- if (!preA.length || !preB.length)
17252
- return !!preA.length && !preB.length;
17253
- for (let i = 0; i < Math.max(preA.length, preB.length); i++) {
17254
- const x = preA[i];
17255
- const y = preB[i];
17256
- if (x === y)
17257
- continue;
17258
- if (x === void 0 || y === void 0)
17259
- return x === void 0;
17260
- const numericX = /^\d+$/.test(x);
17261
- const numericY = /^\d+$/.test(y);
17262
- if (numericX && numericY) {
17263
- if (BigInt(x) === BigInt(y))
17490
+ function messageText(content) {
17491
+ if (typeof content === "string")
17492
+ return content;
17493
+ if (!Array.isArray(content))
17494
+ return "";
17495
+ return content.filter((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string").map((part) => String(part.text)).join(" ");
17496
+ }
17497
+ async function readFacts(file2, signal) {
17498
+ const stream = createReadStream2(file2, { encoding: "utf8", signal });
17499
+ const lines = createInterface({ input: stream, crlfDelay: Infinity });
17500
+ let header = null;
17501
+ let nativeTitle = null;
17502
+ let firstPrompt = null;
17503
+ let updatedAt = null;
17504
+ let leafHasUser = false;
17505
+ const hasUserAncestry = /* @__PURE__ */ new Map();
17506
+ try {
17507
+ for await (const line of lines) {
17508
+ signal.throwIfAborted();
17509
+ if (!line.trim())
17264
17510
  continue;
17265
- return BigInt(y) > BigInt(x);
17511
+ let entry;
17512
+ try {
17513
+ entry = JSON.parse(line);
17514
+ } catch {
17515
+ continue;
17516
+ }
17517
+ if (!isRecord6(entry))
17518
+ continue;
17519
+ if (entry.type === "title") {
17520
+ nativeTitle = sessionImportTitle(entry.title);
17521
+ continue;
17522
+ }
17523
+ if (entry.type === "session") {
17524
+ if (header)
17525
+ return null;
17526
+ if (typeof entry.id !== "string" || entry.id.length === 0 || typeof entry.cwd !== "string" || !path4.isAbsolute(entry.cwd))
17527
+ return null;
17528
+ header = { id: entry.id, cwd: entry.cwd };
17529
+ continue;
17530
+ }
17531
+ if (typeof entry.id !== "string" || entry.id.length === 0)
17532
+ continue;
17533
+ const message3 = isRecord6(entry.message) ? entry.message : null;
17534
+ const isUser = entry.type === "message" && message3?.role === "user";
17535
+ if (isUser)
17536
+ firstPrompt ??= sessionImportTitle(messageText(message3?.content));
17537
+ leafHasUser = isUser || typeof entry.parentId === "string" && hasUserAncestry.get(entry.parentId) === true;
17538
+ hasUserAncestry.set(entry.id, leafHasUser);
17539
+ if (entry.type === "message" && (message3?.role === "user" || message3?.role === "assistant")) {
17540
+ const time3 = typeof message3.timestamp === "number" ? message3.timestamp : typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : Number.NaN;
17541
+ if (Number.isFinite(time3) && time3 >= 0)
17542
+ updatedAt = time3;
17543
+ }
17266
17544
  }
17267
- if (numericX !== numericY)
17268
- return numericX;
17269
- return y > x;
17545
+ } finally {
17546
+ lines.close();
17547
+ stream.destroy();
17270
17548
  }
17271
- return false;
17272
- }
17273
- function createInstallationManager(options) {
17274
- let checking;
17275
- let updating;
17276
- const check2 = () => checking ??= options.check().finally(() => {
17277
- checking = void 0;
17549
+ if (!header || !leafHasUser)
17550
+ return null;
17551
+ return {
17552
+ nativeSessionId: header.id,
17553
+ cwd: header.cwd,
17554
+ title: nativeTitle ?? firstPrompt,
17555
+ updatedAt
17556
+ };
17557
+ }
17558
+ var OmpSessionImport = class {
17559
+ #location;
17560
+ #scope = new SessionImportScope({
17561
+ closedMessage: "Omp Session import is closed",
17562
+ unavailableMessage: "Omp sessions could not be read; close native clients, check storage access and retry",
17563
+ notFoundMessage: "Omp Session is no longer importable"
17278
17564
  });
17279
- return (action) => {
17280
- if (updating)
17281
- return updating;
17282
- if (action === "check")
17283
- return check2();
17284
- updating = (async () => {
17285
- const before = await check2();
17286
- if (!before.updateAvailable)
17287
- return before;
17288
- if (!before.canUpdate)
17289
- throw new Error(before.message ?? "This installation cannot be updated automatically");
17290
- await options.update(before);
17291
- const after = await check2();
17292
- if (after.currentVersion === before.currentVersion)
17293
- throw new Error("The Harness version did not change; check the native updater and retry");
17294
- return after;
17295
- })().finally(() => {
17296
- updating = void 0;
17565
+ constructor(environment) {
17566
+ this.#location = ompSessionImportDirectory(environment);
17567
+ }
17568
+ listCandidates() {
17569
+ return this.#scope.read(async (signal) => [...(await this.#sources(signal)).values()].flatMap((sources) => (
17570
+ // One identity in two files cannot be resumed unambiguously.
17571
+ sources.length === 1 && sources[0] ? [sources[0].candidate] : []
17572
+ )));
17573
+ }
17574
+ resolveCandidate(nativeSessionId) {
17575
+ return this.#scope.resolve(async (signal) => {
17576
+ const sources = (await this.#sources(signal)).get(nativeSessionId);
17577
+ if (sources?.length !== 1 || !sources[0])
17578
+ return null;
17579
+ const source = await this.#source(sources[0].file, signal);
17580
+ return source?.nativeRef.nativeSessionId === nativeSessionId ? { candidate: source.candidate, nativeRef: source.nativeRef } : null;
17297
17581
  });
17298
- return updating;
17299
- };
17582
+ }
17583
+ close() {
17584
+ return this.#scope.close();
17585
+ }
17586
+ async #sessionFiles(signal) {
17587
+ const { directory, flat } = this.#location;
17588
+ const info = await optionalLstat(directory);
17589
+ if (!info)
17590
+ return [];
17591
+ if (!info.isDirectory())
17592
+ throw new Error("Omp Session store is not a directory");
17593
+ const files = [];
17594
+ const visit = async (current, projectLevel) => {
17595
+ const entries = await opendir(current).catch((error51) => {
17596
+ if (isMissingFileError(error51))
17597
+ return null;
17598
+ throw error51;
17599
+ });
17600
+ if (!entries)
17601
+ return;
17602
+ for await (const entry of entries) {
17603
+ signal.throwIfAborted();
17604
+ if (projectLevel && entry.isDirectory())
17605
+ await visit(path4.join(current, entry.name), false);
17606
+ else if (!projectLevel && entry.isFile() && entry.name.endsWith(".jsonl"))
17607
+ files.push(path4.join(current, entry.name));
17608
+ }
17609
+ };
17610
+ await visit(directory, !flat);
17611
+ return files;
17612
+ }
17613
+ /** Session ID to every readable file claiming it. Files changing under the read are skipped. */
17614
+ async #sources(signal) {
17615
+ const found = /* @__PURE__ */ new Map();
17616
+ for (const file2 of await this.#sessionFiles(signal)) {
17617
+ signal.throwIfAborted();
17618
+ try {
17619
+ const source = await this.#source(file2, signal);
17620
+ if (!source)
17621
+ continue;
17622
+ const sources = found.get(source.nativeRef.nativeSessionId) ?? [];
17623
+ sources.push(source);
17624
+ found.set(source.nativeRef.nativeSessionId, sources);
17625
+ } catch (error51) {
17626
+ if (!(error51 instanceof SessionImportChangedError))
17627
+ throw error51;
17628
+ }
17629
+ }
17630
+ return found;
17631
+ }
17632
+ async #source(file2, signal) {
17633
+ signal.throwIfAborted();
17634
+ return readStableFiles([file2], async ([info]) => {
17635
+ if (!info?.isFile() || info.size === 0)
17636
+ return null;
17637
+ try {
17638
+ const facts = await readFacts(file2, signal);
17639
+ if (!facts)
17640
+ return null;
17641
+ const cwd = await realpath2(facts.cwd);
17642
+ if (!(await stat(cwd)).isDirectory())
17643
+ return null;
17644
+ const candidate = sessionImportCandidate({
17645
+ nativeSessionId: facts.nativeSessionId,
17646
+ cwd,
17647
+ title: facts.title,
17648
+ updatedAt: facts.updatedAt ?? info.mtimeMs
17649
+ // OMP has no cross-process activity marker: idleness cannot be established.
17650
+ });
17651
+ if (!candidate)
17652
+ return null;
17653
+ return {
17654
+ file: file2,
17655
+ candidate,
17656
+ // Resume needs the file itself: OMP is started with `--session <file>`.
17657
+ nativeRef: nativeSessionRefSchema.parse({
17658
+ harnessId: "omp",
17659
+ nativeSessionId: facts.nativeSessionId,
17660
+ locator: { sessionFile: await realpath2(file2) },
17661
+ formatVersion: 1
17662
+ })
17663
+ };
17664
+ } catch (error51) {
17665
+ signal.throwIfAborted();
17666
+ if (isStorageAccessError(error51))
17667
+ throw error51;
17668
+ return null;
17669
+ }
17670
+ });
17671
+ }
17672
+ };
17673
+
17674
+ // dist/omp-last-turn-rollback.js
17675
+ function modelFromState(state) {
17676
+ if (state.provider === null && state.modelId === null)
17677
+ return null;
17678
+ if (state.provider === null || state.modelId === null) {
17679
+ throw new Error("Omp state contains a partial Model identity");
17680
+ }
17681
+ return { provider: state.provider, id: state.modelId };
17300
17682
  }
17683
+ async function rollbackOmpLastTurn(transport, sourceSessionId, cwd) {
17684
+ const startupState = transport.state;
17685
+ const startupSessionId = startupState.sessionId;
17686
+ const currentModel = modelFromState(startupState);
17687
+ const currentThinking = startupState.thinkingLevel;
17688
+ const copiedHistory = await transport.getEntries();
17689
+ const boundary = resolveOmpLastTurnBoundary(copiedHistory);
17690
+ if (!boundary)
17691
+ return { ok: false, reason: "empty" };
17692
+ const sourceSnapshot = mapOmpSnapshot(copiedHistory, {
17693
+ sessionId: startupSessionId,
17694
+ model: currentModel
17695
+ });
17696
+ let state = await transport.fork(boundary.lastUserEntryId);
17697
+ if (state.sessionId === sourceSessionId || state.sessionId === startupSessionId) {
17698
+ throw new Error("Omp last-Turn rollback did not create a distinct Native Session");
17699
+ }
17700
+ if (!sameOmpModel(modelFromState(state), currentModel)) {
17701
+ if (!currentModel)
17702
+ throw new Error("Omp last-Turn rollback changed the current Model");
17703
+ state = await transport.selectModel(currentModel);
17704
+ }
17705
+ if (state.thinkingLevel !== currentThinking) {
17706
+ if (!currentThinking)
17707
+ throw new Error("Omp last-Turn rollback changed current Thinking");
17708
+ state = await transport.selectThinkingOption(currentThinking);
17709
+ }
17710
+ if (!sameOmpModel(modelFromState(state), currentModel) || state.thinkingLevel !== currentThinking) {
17711
+ throw new Error("Omp last-Turn rollback could not restore current Model and Thinking");
17712
+ }
17713
+ const snapshot = mapOmpSnapshot(await transport.getEntries(), {
17714
+ sessionId: state.sessionId,
17715
+ model: modelFromState(state)
17716
+ });
17717
+ const expectedTurnKeys = sourceSnapshot.turns.slice(0, -1).map((turn) => turn.nativeTurnRef.nativeTurnKey);
17718
+ const actualTurnKeys = snapshot.turns.map((turn) => turn.nativeTurnRef.nativeTurnKey);
17719
+ if (snapshot.turns.length !== boundary.sourceTurnCount - 1 || actualTurnKeys.some((key, index) => key !== expectedTurnKeys[index])) {
17720
+ throw new Error("Omp last-Turn rollback did not produce the exact retained history prefix");
17721
+ }
17722
+ await transport.verifySessionCwd(cwd);
17723
+ return { ok: true };
17724
+ }
17725
+
17726
+ // dist/omp-rpc-session.js
17727
+ import { spawn, spawnSync } from "node:child_process";
17728
+ import { randomUUID } from "node:crypto";
17729
+ import path5 from "node:path";
17301
17730
 
17302
17731
  // dist/command.js
17303
17732
  var ompDiscoverySpec = {
@@ -17326,14 +17755,14 @@ function resolveOmpExecutable(input, dependencies = {}) {
17326
17755
  }
17327
17756
 
17328
17757
  // dist/omp-usage.js
17329
- function isRecord6(value) {
17758
+ function isRecord7(value) {
17330
17759
  return typeof value === "object" && value !== null && !Array.isArray(value);
17331
17760
  }
17332
17761
  function nonNegativeSafeInteger(value) {
17333
17762
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
17334
17763
  }
17335
17764
  function optionalOmpCacheHitRatePercent(value) {
17336
- if (!isRecord6(value) || value.role !== "assistant" || !isRecord6(value.usage))
17765
+ if (!isRecord7(value) || value.role !== "assistant" || !isRecord7(value.usage))
17337
17766
  return null;
17338
17767
  const input = nonNegativeSafeInteger(value.usage.input);
17339
17768
  const cacheRead = nonNegativeSafeInteger(value.usage.cacheRead);
@@ -17346,20 +17775,20 @@ function optionalOmpCacheHitRatePercent(value) {
17346
17775
  function latestOmpCacheHitRatePercent(history) {
17347
17776
  let latest = null;
17348
17777
  for (const entry of activeOmpEntries(history)) {
17349
- if (entry.type === "message" && isRecord6(entry.message) && entry.message.role === "assistant") {
17778
+ if (entry.type === "message" && isRecord7(entry.message) && entry.message.role === "assistant") {
17350
17779
  latest = optionalOmpCacheHitRatePercent(entry.message);
17351
17780
  }
17352
17781
  }
17353
17782
  return latest;
17354
17783
  }
17355
17784
  function responseData(response, operation) {
17356
- if (!isRecord6(response.data)) {
17785
+ if (!isRecord7(response.data)) {
17357
17786
  throw new Error(`Omp RPC ${operation} response has no data`);
17358
17787
  }
17359
17788
  return response.data;
17360
17789
  }
17361
17790
  function contextUsage(value) {
17362
- if (!isRecord6(value))
17791
+ if (!isRecord7(value))
17363
17792
  throw new Error("Omp RPC context Usage is invalid");
17364
17793
  return parseHostUsage({
17365
17794
  contextUsedTokens: value.tokens,
@@ -17369,15 +17798,15 @@ function contextUsage(value) {
17369
17798
  function parseOmpSessionUsage(response) {
17370
17799
  const data = responseData(response, "Session stats");
17371
17800
  const tokens = data.tokens;
17372
- if (tokens !== void 0 && !isRecord6(tokens)) {
17801
+ if (tokens !== void 0 && !isRecord7(tokens)) {
17373
17802
  throw new Error("Omp RPC Session stats tokens are invalid");
17374
17803
  }
17375
17804
  return parseHostUsage({
17376
- ...isRecord6(tokens) && tokens.input !== void 0 ? { inputTokens: tokens.input } : {},
17377
- ...isRecord6(tokens) && tokens.cacheRead !== void 0 ? { cachedInputTokens: tokens.cacheRead } : {},
17378
- ...isRecord6(tokens) && tokens.cacheWrite !== void 0 ? { cacheWriteInputTokens: tokens.cacheWrite } : {},
17379
- ...isRecord6(tokens) && tokens.output !== void 0 ? { outputTokens: tokens.output } : {},
17380
- ...isRecord6(tokens) && tokens.total !== void 0 ? { totalTokens: tokens.total } : {},
17805
+ ...isRecord7(tokens) && tokens.input !== void 0 ? { inputTokens: tokens.input } : {},
17806
+ ...isRecord7(tokens) && tokens.cacheRead !== void 0 ? { cachedInputTokens: tokens.cacheRead } : {},
17807
+ ...isRecord7(tokens) && tokens.cacheWrite !== void 0 ? { cacheWriteInputTokens: tokens.cacheWrite } : {},
17808
+ ...isRecord7(tokens) && tokens.output !== void 0 ? { outputTokens: tokens.output } : {},
17809
+ ...isRecord7(tokens) && tokens.total !== void 0 ? { totalTokens: tokens.total } : {},
17381
17810
  ...data.cost !== void 0 ? { totalCostUsd: data.cost } : {},
17382
17811
  ...data.contextUsage !== void 0 ? contextUsage(data.contextUsage) : {}
17383
17812
  });
@@ -17492,7 +17921,7 @@ var OmpRpcUnsupportedCommandError = class extends Error {
17492
17921
  }
17493
17922
  };
17494
17923
  var textDecoder = new TextDecoder("utf-8", { fatal: true });
17495
- function isRecord7(value) {
17924
+ function isRecord8(value) {
17496
17925
  return typeof value === "object" && value !== null && !Array.isArray(value);
17497
17926
  }
17498
17927
  function message2(value) {
@@ -17504,13 +17933,13 @@ function nonBlankString(value) {
17504
17933
  function parseNativeModel(value, context) {
17505
17934
  if (value === null || value === void 0)
17506
17935
  return null;
17507
- if (!isRecord7(value) || !nonBlankString(value.provider) || !nonBlankString(value.id)) {
17936
+ if (!isRecord8(value) || !nonBlankString(value.provider) || !nonBlankString(value.id)) {
17508
17937
  throw new OmpRpcFaultError("protocolError", `Omp RPC returned an invalid ${context} Model`);
17509
17938
  }
17510
17939
  return { provider: value.provider, id: value.id };
17511
17940
  }
17512
17941
  function sessionStateData(response) {
17513
- const data = isRecord7(response.data) ? response.data : null;
17942
+ const data = isRecord8(response.data) ? response.data : null;
17514
17943
  if (!data)
17515
17944
  throw new OmpRpcFaultError("protocolError", "Omp RPC state response has no data");
17516
17945
  return data;
@@ -17525,7 +17954,7 @@ function parseSessionState(response) {
17525
17954
  if (thinkingLevel !== null && !thinkingLevel.success) {
17526
17955
  throw new OmpRpcFaultError("protocolError", "Omp RPC state has an invalid Thinking level");
17527
17956
  }
17528
- const thinking = isRecord7(data.model) && isRecord7(data.model.thinking) ? data.model.thinking : null;
17957
+ const thinking = isRecord8(data.model) && isRecord8(data.model.thinking) ? data.model.thinking : null;
17529
17958
  const availableThinkingLevels = thinking && Array.isArray(thinking.efforts) ? thinking.efforts.flatMap((level) => {
17530
17959
  const parsed = harnessThinkingOptionIdSchema.safeParse(level);
17531
17960
  return parsed.success ? [parsed.data] : [];
@@ -17548,25 +17977,25 @@ function parseSessionStreaming(response) {
17548
17977
  return isStreaming;
17549
17978
  }
17550
17979
  function parseAvailableModels(response) {
17551
- const data = isRecord7(response.data) ? response.data : null;
17980
+ const data = isRecord8(response.data) ? response.data : null;
17552
17981
  if (!data || !Array.isArray(data.models)) {
17553
17982
  throw new OmpRpcFaultError("protocolError", "Omp RPC Model catalog response has no models");
17554
17983
  }
17555
17984
  return data.models.map((model) => {
17556
17985
  const parsed = parseNativeModel(model, "catalog");
17557
- if (!parsed || !isRecord7(model) || typeof model.reasoning !== "boolean") {
17986
+ if (!parsed || !isRecord8(model) || typeof model.reasoning !== "boolean") {
17558
17987
  throw new OmpRpcFaultError("protocolError", "Omp RPC catalog contains a Model without reasoning capability");
17559
17988
  }
17560
17989
  return { ...parsed, reasoning: model.reasoning };
17561
17990
  });
17562
17991
  }
17563
17992
  function parseSubagentMessages(response) {
17564
- const data = isRecord7(response.data) ? response.data : null;
17993
+ const data = isRecord8(response.data) ? response.data : null;
17565
17994
  if (!data || !nonBlankString(data.sessionFile) || !Number.isSafeInteger(data.fromByte) || !Number.isSafeInteger(data.nextByte) || data.fromByte < 0 || data.nextByte < data.fromByte || typeof data.reset !== "boolean" || !Array.isArray(data.entries) || !Array.isArray(data.messages)) {
17566
17995
  throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript response is invalid");
17567
17996
  }
17568
- const entries = data.entries.flatMap((value) => isRecord7(value) ? [value] : []);
17569
- const messages = data.messages.flatMap((value) => isRecord7(value) ? [value] : []);
17997
+ const entries = data.entries.flatMap((value) => isRecord8(value) ? [value] : []);
17998
+ const messages = data.messages.flatMap((value) => isRecord8(value) ? [value] : []);
17570
17999
  if (entries.length !== data.entries.length || messages.length !== data.messages.length) {
17571
18000
  throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript contains invalid data");
17572
18001
  }
@@ -17587,17 +18016,17 @@ function subagentStatus(value) {
17587
18016
  throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent status is invalid");
17588
18017
  }
17589
18018
  function assistantText(value) {
17590
- if (!isRecord7(value) || value.role !== "assistant" || !Array.isArray(value.content))
18019
+ if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
17591
18020
  return null;
17592
- return value.content.filter((content) => isRecord7(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
18021
+ return value.content.filter((content) => isRecord8(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
17593
18022
  }
17594
18023
  function assistantMessageId(value) {
17595
- if (!isRecord7(value) || value.role !== "assistant")
18024
+ if (!isRecord8(value) || value.role !== "assistant")
17596
18025
  return null;
17597
18026
  return nonBlankString(value.responseId) ? value.responseId : null;
17598
18027
  }
17599
18028
  function extractReasoningText(content) {
17600
- if (!isRecord7(content))
18029
+ if (!isRecord8(content))
17601
18030
  return null;
17602
18031
  const type = String(content.type ?? "");
17603
18032
  if (type === "thinking" || type === "reasoning" || type === "thought") {
@@ -17607,12 +18036,12 @@ function extractReasoningText(content) {
17607
18036
  return null;
17608
18037
  }
17609
18038
  function assistantReasoning(value) {
17610
- if (!isRecord7(value) || value.role !== "assistant" || !Array.isArray(value.content))
18039
+ if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
17611
18040
  return null;
17612
18041
  return value.content.map(extractReasoningText).filter((text) => typeof text === "string").join("");
17613
18042
  }
17614
18043
  function assistantFailure(value) {
17615
- if (!isRecord7(value) || value.role !== "assistant")
18044
+ if (!isRecord8(value) || value.role !== "assistant")
17616
18045
  return void 0;
17617
18046
  if (value.stopReason !== "error" && value.stopReason !== "aborted")
17618
18047
  return null;
@@ -17632,7 +18061,7 @@ function signalProcessTree(child, signal) {
17632
18061
  try {
17633
18062
  process.kill(-child.pid, signal);
17634
18063
  } catch (error51) {
17635
- if (!isRecord7(error51) || error51.code !== "ESRCH")
18064
+ if (!isRecord8(error51) || error51.code !== "ESRCH")
17636
18065
  throw error51;
17637
18066
  }
17638
18067
  }
@@ -17671,7 +18100,7 @@ function ompRpcProcessCommand(options, dependencies = {}) {
17671
18100
  ...modelArguments,
17672
18101
  ...sessionArguments
17673
18102
  ];
17674
- const extension = path4.win32.extname(command).toLowerCase();
18103
+ const extension = path5.win32.extname(command).toLowerCase();
17675
18104
  if (platform !== "win32" || ![".cmd", ".bat"].includes(extension)) {
17676
18105
  return { command, arguments: arguments_, windowsVerbatimArguments: false };
17677
18106
  }
@@ -17714,6 +18143,10 @@ var OmpRpcSession = class {
17714
18143
  #manualCompaction = null;
17715
18144
  #stderrTail = "";
17716
18145
  #frameDecoder = new OmpFrameDecoder();
18146
+ #startupFaultResolve = null;
18147
+ #startupFault = new Promise((resolve) => {
18148
+ this.#startupFaultResolve = resolve;
18149
+ });
17717
18150
  #readyResolve = null;
17718
18151
  #ready = new Promise((resolve) => {
17719
18152
  this.#readyResolve = resolve;
@@ -17770,7 +18203,7 @@ var OmpRpcSession = class {
17770
18203
  this.#stderrTail = sanitizeDiagnosticTail(`${this.#stderrTail}${chunk.toString()}`);
17771
18204
  });
17772
18205
  child.once("error", (error51) => {
17773
- const kind = isRecord7(error51) && error51.code === "ENOENT" ? "notInstalled" : "unavailable";
18206
+ const kind = isRecord8(error51) && error51.code === "ENOENT" ? "notInstalled" : "unavailable";
17774
18207
  this.#fail(new OmpRpcFaultError(kind, `Omp RPC failed to start: ${error51.message}`, this.stderrTail));
17775
18208
  });
17776
18209
  child.once("exit", (code, signal) => {
@@ -17785,10 +18218,20 @@ var OmpRpcSession = class {
17785
18218
  }),
17786
18219
  new Promise((_resolve, reject) => setTimeout(() => reject(new Error("Omp RPC start timed out")), this.#options.commandTimeoutMs))
17787
18220
  ]);
17788
- await Promise.race([
17789
- this.#ready,
17790
- new Promise((_resolve, reject) => setTimeout(() => reject(new Error("Omp RPC ready signal timed out")), this.#options.commandTimeoutMs))
17791
- ]);
18221
+ let readyTimeout;
18222
+ try {
18223
+ await Promise.race([
18224
+ this.#ready,
18225
+ this.#startupFault.then((error51) => {
18226
+ throw error51;
18227
+ }),
18228
+ new Promise((_resolve, reject) => {
18229
+ readyTimeout = setTimeout(() => reject(new Error("Omp RPC ready signal timed out")), this.#options.commandTimeoutMs);
18230
+ })
18231
+ ]);
18232
+ } finally {
18233
+ clearTimeout(readyTimeout);
18234
+ }
17792
18235
  await this.#send("negotiate_protocol", { protocolVersion: 2 }).catch(() => void 0);
17793
18236
  if (this.#options.subscribeSubagentEvents !== false) {
17794
18237
  await this.#send("set_subagent_subscription", { level: "events" }).catch(() => void 0);
@@ -17808,17 +18251,17 @@ var OmpRpcSession = class {
17808
18251
  return await readOmpSessionHistory(this.state.sessionFile);
17809
18252
  }
17810
18253
  const response = await this.#send("get_messages", {});
17811
- const data = isRecord7(response.data) ? response.data : null;
18254
+ const data = isRecord8(response.data) ? response.data : null;
17812
18255
  if (!data || !Array.isArray(data.messages)) {
17813
18256
  throw new OmpRpcFaultError("protocolError", "Omp RPC messages response has no messages");
17814
18257
  }
17815
18258
  let branchEntryIds = null;
17816
18259
  try {
17817
18260
  const branchResponse = await this.#send("get_branch_messages", {});
17818
- const branchData = isRecord7(branchResponse.data) ? branchResponse.data : null;
18261
+ const branchData = isRecord8(branchResponse.data) ? branchResponse.data : null;
17819
18262
  if (branchData && Array.isArray(branchData.messages)) {
17820
18263
  const parsed = branchData.messages.flatMap((entry) => {
17821
- if (!isRecord7(entry) || !nonBlankString(entry.entryId))
18264
+ if (!isRecord8(entry) || !nonBlankString(entry.entryId))
17822
18265
  return [];
17823
18266
  return [entry.entryId];
17824
18267
  });
@@ -17832,11 +18275,11 @@ var OmpRpcSession = class {
17832
18275
  let userIndex = 0;
17833
18276
  let parentId = null;
17834
18277
  const entries = data.messages.map((nativeMessage, index) => {
17835
- const role = isRecord7(nativeMessage) ? nativeMessage.role : void 0;
18278
+ const role = isRecord8(nativeMessage) ? nativeMessage.role : void 0;
17836
18279
  const branchId = role === "user" ? branchEntryIds?.[userIndex] : void 0;
17837
18280
  if (branchId)
17838
18281
  userIndex += 1;
17839
- const id = branchId ?? (isRecord7(nativeMessage) && nonBlankString(nativeMessage.id) ? nativeMessage.id : `omp-message-${index}`);
18282
+ const id = branchId ?? (isRecord8(nativeMessage) && nonBlankString(nativeMessage.id) ? nativeMessage.id : `omp-message-${index}`);
17840
18283
  const entry = { id, parentId, type: "message", message: nativeMessage };
17841
18284
  parentId = id;
17842
18285
  return entry;
@@ -17916,7 +18359,7 @@ var OmpRpcSession = class {
17916
18359
  throw new Error("Omp RPC Fork requires an Entry identity");
17917
18360
  try {
17918
18361
  const response = await this.#send("branch", { entryId });
17919
- const data = isRecord7(response.data) ? response.data : null;
18362
+ const data = isRecord8(response.data) ? response.data : null;
17920
18363
  if (!data || data.cancelled === true) {
17921
18364
  throw new OmpRpcFaultError("unavailable", "Omp RPC Fork was cancelled");
17922
18365
  }
@@ -18109,7 +18552,7 @@ var OmpRpcSession = class {
18109
18552
  continue;
18110
18553
  }
18111
18554
  const value = decoded;
18112
- if (!isRecord7(value) || typeof value.type !== "string") {
18555
+ if (!isRecord8(value) || typeof value.type !== "string") {
18113
18556
  throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid envelope");
18114
18557
  }
18115
18558
  this.#handle(value);
@@ -18168,7 +18611,7 @@ var OmpRpcSession = class {
18168
18611
  this.#armCommandTimeout(id, pending);
18169
18612
  }
18170
18613
  }
18171
- const outcome = value.aborted === true ? "cancelled" : isRecord7(value.result) ? "succeeded" : "failed";
18614
+ const outcome = value.aborted === true ? "cancelled" : isRecord8(value.result) ? "succeeded" : "failed";
18172
18615
  const event = {
18173
18616
  type: "compaction.completed",
18174
18617
  outcome,
@@ -18201,12 +18644,12 @@ var OmpRpcSession = class {
18201
18644
  this.#startAssistantMessage(active, value.message);
18202
18645
  return;
18203
18646
  }
18204
- if (value.type === "message_update" && isRecord7(value.assistantMessageEvent)) {
18647
+ if (value.type === "message_update" && isRecord8(value.assistantMessageEvent)) {
18205
18648
  const event = value.assistantMessageEvent;
18206
18649
  const eventType = String(event.type ?? "");
18207
18650
  const isThinking = eventType === "thinking_delta" || eventType === "reasoning_delta" || eventType === "thought_delta" || eventType === "thinking" || eventType === "reasoning";
18208
18651
  const isText = eventType === "text_delta" || eventType === "text" || eventType === "content_block_delta";
18209
- const delta = typeof event.delta === "string" ? event.delta : typeof event.thinking === "string" ? event.thinking : typeof event.reasoning === "string" ? event.reasoning : typeof event.text === "string" ? event.text : isRecord7(event.delta) && typeof event.delta.thinking === "string" ? event.delta.thinking : isRecord7(event.delta) && typeof event.delta.text === "string" ? event.delta.text : null;
18652
+ const delta = typeof event.delta === "string" ? event.delta : typeof event.thinking === "string" ? event.thinking : typeof event.reasoning === "string" ? event.reasoning : typeof event.text === "string" ? event.text : isRecord8(event.delta) && typeof event.delta.thinking === "string" ? event.delta.thinking : isRecord8(event.delta) && typeof event.delta.text === "string" ? event.delta.text : null;
18210
18653
  if (isThinking && delta !== null && delta.length > 0) {
18211
18654
  const messageId = this.#ensureAssistantMessage(active, value.message);
18212
18655
  active.reasoningMessageOpen = true;
@@ -18246,7 +18689,7 @@ var OmpRpcSession = class {
18246
18689
  if (value.type === "agent_end" && Array.isArray(value.messages)) {
18247
18690
  for (let index = value.messages.length - 1; index >= 0; index -= 1) {
18248
18691
  const message3 = value.messages[index];
18249
- if (!isRecord7(message3) || message3.role !== "assistant")
18692
+ if (!isRecord8(message3) || message3.role !== "assistant")
18250
18693
  continue;
18251
18694
  this.#finalizeAssistantMessage(active, message3);
18252
18695
  break;
@@ -18263,8 +18706,8 @@ var OmpRpcSession = class {
18263
18706
  if (value.type !== "subagent_lifecycle" && value.type !== "subagent_progress" && value.type !== "subagent_event") {
18264
18707
  return false;
18265
18708
  }
18266
- const payload = isRecord7(value.payload) ? value.payload : null;
18267
- const progressPayload = value.type === "subagent_progress" && payload && isRecord7(payload.progress) ? payload.progress : null;
18709
+ const payload = isRecord8(value.payload) ? value.payload : null;
18710
+ const progressPayload = value.type === "subagent_progress" && payload && isRecord8(payload.progress) ? payload.progress : null;
18268
18711
  const nativeIdValue = progressPayload?.id ?? payload?.id;
18269
18712
  if (!payload || !nonBlankString(nativeIdValue)) {
18270
18713
  throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent frame has no stable ID");
@@ -18681,6 +19124,8 @@ var OmpRpcSession = class {
18681
19124
  if (this.#closed || this.#failed)
18682
19125
  return;
18683
19126
  this.#failed = true;
19127
+ this.#startupFaultResolve?.(error51);
19128
+ this.#startupFaultResolve = null;
18684
19129
  this.#rejectAll(error51);
18685
19130
  this.#options.onFault?.(error51);
18686
19131
  }
@@ -18841,7 +19286,7 @@ var ompCommandCatalog = harnessCommandCatalogSchema.parse({
18841
19286
  ]
18842
19287
  });
18843
19288
  var DEFAULT_TOOL_OUTPUT_LIMIT = 64e3;
18844
- function isRecord8(value) {
19289
+ function isRecord9(value) {
18845
19290
  return typeof value === "object" && value !== null && !Array.isArray(value);
18846
19291
  }
18847
19292
  function errorMessage(error51) {
@@ -18856,7 +19301,7 @@ var OmpAdapterFaultError = class extends Error {
18856
19301
  }
18857
19302
  };
18858
19303
  function normalizedError(error51, fallbackCode) {
18859
- if (isRecord8(error51) && error51.code === "ENOENT") {
19304
+ if (isRecord9(error51) && error51.code === "ENOENT") {
18860
19305
  return { code: "notInstalled", message: errorMessage(error51), retryable: false };
18861
19306
  }
18862
19307
  if (error51 instanceof OmpAdapterFaultError)
@@ -18933,7 +19378,7 @@ function nativeModelForHistory(state) {
18933
19378
  return nativeModelFromState(state);
18934
19379
  }
18935
19380
  function sessionFileFromRef(ref) {
18936
- if (ref.harnessId !== ompHarnessId2 || !isRecord8(ref.locator) || typeof ref.locator.sessionFile !== "string" || ref.locator.sessionFile.length === 0) {
19381
+ if (ref.harnessId !== ompHarnessId2 || !isRecord9(ref.locator) || typeof ref.locator.sessionFile !== "string" || ref.locator.sessionFile.length === 0) {
18937
19382
  throw new Error("Omp Native Session Ref has no resumable Session file");
18938
19383
  }
18939
19384
  return ref.locator.sessionFile;
@@ -18943,7 +19388,7 @@ function subagentTranscriptPath(parentSessionFile, nativeSubagentId) {
18943
19388
  if (nativeSubagentId.includes("/") || nativeSubagentId.includes("\\") || nativeSubagentId.includes("\0") || nativeSubagentId === "." || nativeSubagentId === "..") {
18944
19389
  throw new Error("Omp Subagent ID is not a plain transcript file name");
18945
19390
  }
18946
- if (!parentSessionFile.endsWith(".jsonl") || !path5.isAbsolute(parentSessionFile)) {
19391
+ if (!parentSessionFile.endsWith(".jsonl") || !path6.isAbsolute(parentSessionFile)) {
18947
19392
  return null;
18948
19393
  }
18949
19394
  return `${parentSessionFile.slice(0, -".jsonl".length)}/${nativeSubagentId}.jsonl`;
@@ -18952,10 +19397,10 @@ async function readSubagentTranscriptFile(parentSessionFile, nativeSubagentId) {
18952
19397
  const transcriptFile = subagentTranscriptPath(parentSessionFile, nativeSubagentId);
18953
19398
  if (transcriptFile === null)
18954
19399
  return { status: "missing" };
18955
- const parentDirectory = path5.dirname(transcriptFile);
19400
+ const parentDirectory = path6.dirname(transcriptFile);
18956
19401
  let metadata;
18957
19402
  try {
18958
- metadata = await stat(transcriptFile);
19403
+ metadata = await stat2(transcriptFile);
18959
19404
  } catch (error51) {
18960
19405
  if (error51.code === "ENOENT")
18961
19406
  return { status: "missing" };
@@ -18967,10 +19412,10 @@ async function readSubagentTranscriptFile(parentSessionFile, nativeSubagentId) {
18967
19412
  throw new Error("Omp Subagent transcript exceeds the supported size");
18968
19413
  }
18969
19414
  const [resolvedTranscript, resolvedParentDirectory] = await Promise.all([
18970
- realpath2(transcriptFile),
18971
- realpath2(parentDirectory)
19415
+ realpath3(transcriptFile),
19416
+ realpath3(parentDirectory)
18972
19417
  ]);
18973
- if (path5.dirname(resolvedTranscript) !== resolvedParentDirectory) {
19418
+ if (path6.dirname(resolvedTranscript) !== resolvedParentDirectory) {
18974
19419
  throw new Error("Omp Subagent transcript resolves outside its parent Session directory");
18975
19420
  }
18976
19421
  return {
@@ -18988,9 +19433,9 @@ function toolFailure(toolName) {
18988
19433
  function nativeText(value) {
18989
19434
  if (typeof value === "string")
18990
19435
  return value;
18991
- if (!isRecord8(value) || !Array.isArray(value.content))
19436
+ if (!isRecord9(value) || !Array.isArray(value.content))
18992
19437
  return "";
18993
- return value.content.filter((content) => isRecord8(content) && content.type === "text" && typeof content.text === "string").map(({ text }) => text).join("");
19438
+ return value.content.filter((content) => isRecord9(content) && content.type === "text" && typeof content.text === "string").map(({ text }) => text).join("");
18994
19439
  }
18995
19440
  function boundedOutput(value, limit) {
18996
19441
  const text = nativeText(value);
@@ -19006,17 +19451,17 @@ function outputText(output) {
19006
19451
  return output?.content.filter((content) => content.type === "text").map(({ text }) => text).join("") ?? "";
19007
19452
  }
19008
19453
  function stringField2(value, key) {
19009
- if (!isRecord8(value))
19454
+ if (!isRecord9(value))
19010
19455
  return void 0;
19011
19456
  const field = value[key];
19012
19457
  if (typeof field === "string" && field.length > 0)
19013
19458
  return field;
19014
- if (isRecord8(value.input)) {
19459
+ if (isRecord9(value.input)) {
19015
19460
  const nested = value.input[key];
19016
19461
  if (typeof nested === "string" && nested.length > 0)
19017
19462
  return nested;
19018
19463
  }
19019
- if (isRecord8(value.arguments)) {
19464
+ if (isRecord9(value.arguments)) {
19020
19465
  const nested = value.arguments[key];
19021
19466
  if (typeof nested === "string" && nested.length > 0)
19022
19467
  return nested;
@@ -19024,7 +19469,7 @@ function stringField2(value, key) {
19024
19469
  return void 0;
19025
19470
  }
19026
19471
  function numberField(value, key) {
19027
- if (!isRecord8(value))
19472
+ if (!isRecord9(value))
19028
19473
  return void 0;
19029
19474
  const field = value[key];
19030
19475
  return typeof field === "number" || field === null ? field : void 0;
@@ -19035,10 +19480,10 @@ function stripDiffPrefix(pathString) {
19035
19480
  return pathString.startsWith("a/") || pathString.startsWith("b/") ? pathString.slice(2) : pathString;
19036
19481
  }
19037
19482
  function displayPath(nativePath, cwd) {
19038
- const resolvedCwd = path5.resolve(cwd);
19039
- const resolvedPath = path5.isAbsolute(nativePath) ? path5.resolve(nativePath) : path5.resolve(cwd, nativePath);
19040
- const relative = path5.relative(resolvedCwd, resolvedPath);
19041
- const inside = relative.length > 0 && relative !== ".." && !relative.startsWith(`..${path5.sep}`);
19483
+ const resolvedCwd = path6.resolve(cwd);
19484
+ const resolvedPath = path6.isAbsolute(nativePath) ? path6.resolve(nativePath) : path6.resolve(cwd, nativePath);
19485
+ const relative = path6.relative(resolvedCwd, resolvedPath);
19486
+ const inside = relative.length > 0 && relative !== ".." && !relative.startsWith(`..${path6.sep}`);
19042
19487
  const selected = inside ? relative : resolvedPath;
19043
19488
  const normalized = selected.replaceAll("\\", "/");
19044
19489
  if (normalized.length === 0 || normalized === ".")
@@ -19063,7 +19508,7 @@ function fileMutatingKind(toolName) {
19063
19508
  return null;
19064
19509
  }
19065
19510
  function nestedToolString(value, keys) {
19066
- if (!isRecord8(value))
19511
+ if (!isRecord9(value))
19067
19512
  return void 0;
19068
19513
  for (const key of keys) {
19069
19514
  const field = value[key];
@@ -19078,14 +19523,14 @@ function nestedToolString(value, keys) {
19078
19523
  return void 0;
19079
19524
  }
19080
19525
  function patchFromResult(result) {
19081
- if (!isRecord8(result))
19526
+ if (!isRecord9(result))
19082
19527
  return void 0;
19083
19528
  for (const key of ["patch", "diff", "unifiedDiff"]) {
19084
19529
  const field = result[key];
19085
19530
  if (typeof field === "string" && field.length > 0)
19086
19531
  return field;
19087
19532
  }
19088
- if (isRecord8(result.details)) {
19533
+ if (isRecord9(result.details)) {
19089
19534
  for (const key of ["patch", "diff", "unifiedDiff"]) {
19090
19535
  const field = result.details[key];
19091
19536
  if (typeof field === "string" && field.length > 0)
@@ -20567,6 +21012,7 @@ var OmpAdapter = class {
20567
21012
  commandCatalog = ompCommandCatalog;
20568
21013
  liveCommandCatalog = true;
20569
21014
  harnessId = ompHarnessId2;
21015
+ sessionImport;
20570
21016
  subagents = {
20571
21017
  readSnapshot: async (input) => {
20572
21018
  if (input.parent.harnessId !== this.harnessId || input.nativeSubagentId.trim().length === 0 || input.cwd.length === 0) {
@@ -20626,6 +21072,7 @@ var OmpAdapter = class {
20626
21072
  createTransport: (sessionOptions) => new OmpRpcSession({ ...options, ...sessionOptions })
20627
21073
  }) {
20628
21074
  this.#createTransport = dependencies.createTransport;
21075
+ this.sessionImport = new OmpSessionImport({ ...process.env, ...options.environment });
20629
21076
  this.#closeTimeoutMs = options.closeTimeoutMs ?? 2e3;
20630
21077
  this.#toolOutputLimit = options.toolOutputLimit ?? DEFAULT_TOOL_OUTPUT_LIMIT;
20631
21078
  }
@@ -20697,7 +21144,11 @@ var OmpAdapter = class {
20697
21144
  };
20698
21145
  } catch (error51) {
20699
21146
  await transport.close().catch(() => void 0);
20700
- const normalized = normalizedError(error51, "unavailable");
21147
+ const normalized = stage === "startup" && /No models available\.\s*Use \/login or set an API key environment variable\./i.test(transport.stderrTail ?? "") ? {
21148
+ code: "configurationRequired",
21149
+ message: "No models are configured for use in Oh My Pi. Configure a Provider and its API key or models.yml, then run `omp` and use `/model` to select a model. For Providers that support it, `/login` configures Provider authentication, not an Oh My Pi account.",
21150
+ retryable: false
21151
+ } : normalizedError(error51, "unavailable");
20701
21152
  return {
20702
21153
  status: normalized.code === "notInstalled" ? "notInstalled" : "error",
20703
21154
  error: {
@@ -20887,6 +21338,7 @@ var OmpAdapter = class {
20887
21338
  close() {
20888
21339
  if (!this.#closePromise) {
20889
21340
  this.#closePromise = Promise.all([
21341
+ this.sessionImport.close(),
20890
21342
  ...[...this.#inspections].map((transport) => transport.close()),
20891
21343
  ...[...this.#sessions].map((session) => session.close())
20892
21344
  ]).then(() => void 0);
@@ -20896,22 +21348,22 @@ var OmpAdapter = class {
20896
21348
  };
20897
21349
 
20898
21350
  // dist/installation.js
20899
- import { realpath as realpath3 } from "node:fs/promises";
20900
- import path6 from "node:path";
21351
+ import { realpath as realpath4 } from "node:fs/promises";
21352
+ import path7 from "node:path";
20901
21353
  function createOmpInstallation(environment, command) {
20902
21354
  let executable;
20903
21355
  let env;
20904
21356
  return createInstallationManager({
20905
21357
  async check() {
20906
21358
  executable = resolveOmpExecutable({ environment, ...command ? { command } : {} });
20907
- const selected = await realpath3(executable);
21359
+ const selected = await realpath4(executable);
20908
21360
  const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
20909
21361
  env = {
20910
21362
  ...environment,
20911
- [pathKey]: `${path6.dirname(executable)}${path6.delimiter}${environment[pathKey] ?? ""}`
21363
+ [pathKey]: `${path7.dirname(executable)}${path7.delimiter}${environment[pathKey] ?? ""}`
20912
21364
  };
20913
21365
  const target = resolveHarnessExecutable({ id: "omp", command: "omp", installRoots: { posix: [], windows: [] } }, { environment: env });
20914
- const canUpdate = !selected.includes("/nix/store/") && !!target && await realpath3(target.executable) === selected;
21366
+ const canUpdate = !selected.includes("/nix/store/") && !!target && await realpath4(target.executable) === selected;
20915
21367
  const output = await runInstallationCommand(executable, ["update", "--check"], env);
20916
21368
  const currentVersion = versionFromOutput(output);
20917
21369
  const available = output.match(/New version available:\s*(\S+)/i);
@@ -20923,7 +21375,10 @@ function createOmpInstallation(environment, command) {
20923
21375
  latestVersion,
20924
21376
  updateAvailable: newerInstallationVersion(currentVersion, latestVersion),
20925
21377
  canUpdate,
20926
- ...!canUpdate ? { message: "Update this installation with its original package manager or launcher." } : {}
21378
+ ...!canUpdate ? {
21379
+ messageCode: "original-installer",
21380
+ message: "Update this installation with its original package manager or launcher."
21381
+ } : {}
20927
21382
  };
20928
21383
  },
20929
21384
  async update() {
@@ -20939,7 +21394,14 @@ function createHarnessAdapter(context) {
20939
21394
  return Object.assign(new OmpAdapter({
20940
21395
  ...environment[OMP_COMMAND_ENV] ? { command: environment[OMP_COMMAND_ENV] } : {},
20941
21396
  environment
20942
- }), { installation: createOmpInstallation(environment, environment[OMP_COMMAND_ENV]) });
21397
+ }), {
21398
+ installation: createOmpInstallation(environment, environment[OMP_COMMAND_ENV]),
21399
+ install: createHarnessInstaller(environment, {
21400
+ posix: "https://omp.sh/install",
21401
+ windows: "https://omp.sh/install.ps1",
21402
+ shell: "sh"
21403
+ })
21404
+ });
20943
21405
  }
20944
21406
  export {
20945
21407
  OMP_COMMAND_ENV,