@karlorz/codexhost-cli-win32-x64 0.13.3-6 → 0.13.3-7

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.
@@ -424,10 +424,13 @@ function usageSessionTitle(value) {
424
424
  return void 0;
425
425
  if (normalized.length <= MAX_SESSION_TITLE_LENGTH)
426
426
  return normalized;
427
- const characters = [...normalized];
428
- if (characters.length <= MAX_SESSION_TITLE_LENGTH)
429
- return normalized;
430
- return `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("").trimEnd()}…`;
427
+ let title = "";
428
+ for (const character of normalized) {
429
+ if (title.length + character.length >= MAX_SESSION_TITLE_LENGTH)
430
+ break;
431
+ title += character;
432
+ }
433
+ return `${title.trimEnd()}…`;
431
434
  }
432
435
  function sessionIdOk(value) {
433
436
  return typeof value === "string" && value.length > 0 && value.length <= MAX_SESSION_ID_LENGTH;
@@ -15374,6 +15377,8 @@ var CONSOLE_HOST_METHODS = Object.freeze([
15374
15377
  "codexhost/harness/installation",
15375
15378
  "codexhost/harness/launch-settings/get",
15376
15379
  "codexhost/harness/launch-settings/set",
15380
+ "codexhost/harness/grok-computer-use/get",
15381
+ "codexhost/harness/grok-computer-use/set",
15377
15382
  "codexhost/harness/web-ui/open",
15378
15383
  "codexhost/harness/accounts/sources",
15379
15384
  "codexhost/harness/accounts/list",
@@ -15902,7 +15907,9 @@ var harnessInspectionSchema = external_exports.union([
15902
15907
  var harnessInspectParamsSchema = external_exports.object({
15903
15908
  harnessId: harnessIdSchema,
15904
15909
  cwd: nonBlankTextSchema2.max(16384).optional(),
15905
- refresh: external_exports.boolean().optional()
15910
+ refresh: external_exports.boolean().optional(),
15911
+ /** `availability` asks only whether the Harness can run; see `InspectHarnessInput.purpose`. */
15912
+ purpose: external_exports.literal("availability").optional()
15906
15913
  }).strict();
15907
15914
  var harnessWebUiOpenParamsSchema = external_exports.object({
15908
15915
  harnessId: harnessIdSchema
@@ -16076,6 +16083,26 @@ var harnessLaunchSettingsSchema = external_exports.object({
16076
16083
  restartRequired: external_exports.boolean()
16077
16084
  }).strict();
16078
16085
 
16086
+ // ../../shared-contracts/dist/grok-computer-use.js
16087
+ var grokComputerUseGetSchema = external_exports.object({}).strict();
16088
+ var grokComputerUseSetSchema = external_exports.object({
16089
+ enabled: external_exports.boolean().optional(),
16090
+ browser: external_exports.boolean().optional()
16091
+ }).strict().refine((value) => value.enabled !== void 0 || value.browser !== void 0, {
16092
+ message: "Expected enabled or browser"
16093
+ });
16094
+ var grokComputerUseSettingsSchema = external_exports.object({
16095
+ enabled: external_exports.boolean(),
16096
+ available: external_exports.boolean(),
16097
+ unavailableReason: external_exports.string().min(1).max(500).optional(),
16098
+ browser: external_exports.boolean().optional(),
16099
+ browserFilesPresent: external_exports.boolean().optional(),
16100
+ browserAvailable: external_exports.boolean().optional(),
16101
+ browserUnavailableReason: external_exports.string().min(1).max(500).optional(),
16102
+ applyFailed: external_exports.boolean().optional(),
16103
+ applyError: external_exports.string().min(1).max(500).optional()
16104
+ }).strict();
16105
+
16079
16106
  // ../../shared-contracts/dist/harness-installation.js
16080
16107
  var harnessInstallationParamsSchema = external_exports.object({
16081
16108
  harnessId: harnessIdSchema,
@@ -17788,2256 +17815,2348 @@ function createOmpUsageStatistics(environment) {
17788
17815
  });
17789
17816
  }
17790
17817
 
17791
- // ../../../node_modules/diff/libesm/diff/base.js
17792
- var Diff = class {
17793
- diff(oldStr, newStr, options = {}) {
17794
- let callback;
17795
- if (typeof options === "function") {
17796
- callback = options;
17797
- options = {};
17798
- } else if ("callback" in options) {
17799
- callback = options.callback;
17800
- }
17801
- const oldString = this.castInput(oldStr, options);
17802
- const newString = this.castInput(newStr, options);
17803
- const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
17804
- const newTokens = this.removeEmpty(this.tokenize(newString, options));
17805
- return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
17818
+ // dist/omp-model-list.js
17819
+ import { spawn as spawn2 } from "node:child_process";
17820
+
17821
+ // dist/command.js
17822
+ var ompDiscoverySpec = {
17823
+ id: "omp",
17824
+ command: "omp",
17825
+ commandEnvironmentVariable: "OMP_COMMAND",
17826
+ installRoots: {
17827
+ posix: [
17828
+ "~/.npm-global/bin",
17829
+ "~/.local/bin",
17830
+ VERSION_MANAGER_ROOTS,
17831
+ "/opt/homebrew/bin",
17832
+ "/usr/local/bin"
17833
+ ],
17834
+ windows: ["${APPDATA}/npm", "~/.local/bin", VERSION_MANAGER_ROOTS]
17806
17835
  }
17807
- diffWithOptionsObj(oldTokens, newTokens, options, callback) {
17808
- var _a3;
17809
- const done = (value) => {
17810
- value = this.postProcess(value, options);
17811
- if (callback) {
17812
- setTimeout(function() {
17813
- callback(value);
17814
- }, 0);
17815
- return void 0;
17816
- } else {
17817
- return value;
17836
+ };
17837
+ function resolveOmpExecutable(input, dependencies = {}) {
17838
+ const resolution = resolveHarnessExecutable(ompDiscoverySpec, {
17839
+ ...input.command ? { command: input.command } : {},
17840
+ environment: input.environment,
17841
+ ...dependencies.platform ? { platform: dependencies.platform } : {},
17842
+ ...dependencies.homeDirectory ? { homeDirectory: dependencies.homeDirectory } : {}
17843
+ }, { ...dependencies.isExecutable ? { isExecutable: dependencies.isExecutable } : {} });
17844
+ return resolution?.executable ?? input.command ?? input.environment.OMP_COMMAND ?? "omp";
17845
+ }
17846
+
17847
+ // dist/omp-rpc-session.js
17848
+ import { spawn, spawnSync } from "node:child_process";
17849
+ import { randomUUID } from "node:crypto";
17850
+ import path6 from "node:path";
17851
+
17852
+ // dist/omp-slash-commands.js
17853
+ var OMP_LIVE_COMMAND_ID_PREFIX = "omp.slash.";
17854
+ function isRecord6(value) {
17855
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17856
+ }
17857
+ function parseOmpAvailableCommands(value) {
17858
+ if (!Array.isArray(value))
17859
+ return [];
17860
+ return value.flatMap((entry) => {
17861
+ if (!isRecord6(entry))
17862
+ return [];
17863
+ const name = typeof entry.name === "string" ? entry.name.trim().replace(/^\//u, "") : "";
17864
+ if (!name)
17865
+ return [];
17866
+ return [
17867
+ {
17868
+ name,
17869
+ description: typeof entry.description === "string" ? entry.description : "",
17870
+ source: typeof entry.source === "string" ? entry.source : null
17818
17871
  }
17819
- };
17820
- const newLen = newTokens.length, oldLen = oldTokens.length;
17821
- let editLength = 1;
17822
- let maxEditLength = newLen + oldLen;
17823
- if (options.maxEditLength != null) {
17824
- maxEditLength = Math.min(maxEditLength, options.maxEditLength);
17825
- }
17826
- const maxExecutionTime = (_a3 = options.timeout) !== null && _a3 !== void 0 ? _a3 : Infinity;
17827
- const abortAfterTimestamp = Date.now() + maxExecutionTime;
17828
- const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
17829
- let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
17830
- if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
17831
- return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
17832
- }
17833
- let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
17834
- const execEditLength = () => {
17835
- for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
17836
- let basePath;
17837
- const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
17838
- if (removePath) {
17839
- bestPath[diagonalPath - 1] = void 0;
17840
- }
17841
- let canAdd = false;
17842
- if (addPath) {
17843
- const addPathNewPos = addPath.oldPos - diagonalPath;
17844
- canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
17845
- }
17846
- const canRemove = removePath && removePath.oldPos + 1 < oldLen;
17847
- if (!canAdd && !canRemove) {
17848
- bestPath[diagonalPath] = void 0;
17849
- continue;
17850
- }
17851
- if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
17852
- basePath = this.addToPath(addPath, true, false, 0, options);
17853
- } else {
17854
- basePath = this.addToPath(removePath, false, true, 1, options);
17855
- }
17856
- newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
17857
- if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
17858
- return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
17859
- } else {
17860
- bestPath[diagonalPath] = basePath;
17861
- if (basePath.oldPos + 1 >= oldLen) {
17862
- maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
17863
- }
17864
- if (newPos + 1 >= newLen) {
17865
- minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
17866
- }
17867
- }
17872
+ ];
17873
+ });
17874
+ }
17875
+ function ompLiveCommands(native) {
17876
+ return native.filter(({ source, name }) => source !== "builtin" || name === "context").map((command) => ({
17877
+ name: command.name,
17878
+ description: command.description,
17879
+ kind: command.source === "skill" ? "skill" : "command"
17880
+ }));
17881
+ }
17882
+
17883
+ // dist/omp-session-file.js
17884
+ import { createReadStream as createReadStream3 } from "node:fs";
17885
+ import { open, realpath as realpath2 } from "node:fs/promises";
17886
+ import readline from "node:readline";
17887
+ var MAX_SESSION_HEADER_BYTES = 64 * 1024;
17888
+ var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
17889
+ function isRecord7(value) {
17890
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17891
+ }
17892
+ function historyEntry(value) {
17893
+ if (!isRecord7(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") {
17894
+ return null;
17895
+ }
17896
+ return value;
17897
+ }
17898
+ async function readOmpSessionHistory(sessionFile, maxBytes) {
17899
+ const entries = [];
17900
+ let leafId = null;
17901
+ let incomplete = false;
17902
+ const lines = readline.createInterface({
17903
+ input: createReadStream3(sessionFile, {
17904
+ encoding: "utf8",
17905
+ ...maxBytes === void 0 ? {} : { end: maxBytes - 1 }
17906
+ }),
17907
+ crlfDelay: Number.POSITIVE_INFINITY
17908
+ });
17909
+ try {
17910
+ for await (const line of lines) {
17911
+ if (line.length === 0)
17912
+ continue;
17913
+ let parsed;
17914
+ try {
17915
+ parsed = JSON.parse(line);
17916
+ } catch {
17917
+ incomplete = true;
17918
+ continue;
17868
17919
  }
17869
- editLength++;
17870
- };
17871
- if (callback) {
17872
- (function exec() {
17873
- setTimeout(function() {
17874
- if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
17875
- return callback(void 0);
17876
- }
17877
- if (!execEditLength()) {
17878
- exec();
17879
- }
17880
- }, 0);
17881
- })();
17882
- } else {
17883
- while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
17884
- const ret = execEditLength();
17885
- if (ret) {
17886
- return ret;
17920
+ const entry = historyEntry(parsed);
17921
+ if (!entry) {
17922
+ if (!isRecord7(parsed) || parsed.type !== "title" && parsed.type !== "session") {
17923
+ incomplete = true;
17887
17924
  }
17925
+ continue;
17888
17926
  }
17927
+ entries.push(entry);
17928
+ leafId = entry.id;
17889
17929
  }
17930
+ } finally {
17931
+ lines.close();
17890
17932
  }
17891
- addToPath(path9, added, removed, oldPosInc, options) {
17892
- const last = path9.lastComponent;
17893
- if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
17894
- return {
17895
- oldPos: path9.oldPos + oldPosInc,
17896
- lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
17897
- };
17898
- } else {
17899
- return {
17900
- oldPos: path9.oldPos + oldPosInc,
17901
- lastComponent: { count: 1, added, removed, previousComponent: last }
17902
- };
17933
+ return { entries, leafId, ...incomplete ? { incomplete: true } : {} };
17934
+ }
17935
+ async function readOmpSessionHeader(sessionFile) {
17936
+ const handle = await open(sessionFile, "r");
17937
+ try {
17938
+ const buffer = Buffer.allocUnsafe(MAX_SESSION_HEADER_BYTES);
17939
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
17940
+ const contents = buffer.subarray(0, bytesRead);
17941
+ const newline = contents.indexOf(10);
17942
+ if (newline < 0 && bytesRead === buffer.length) {
17943
+ throw new Error("Omp Session header exceeds the supported size");
17903
17944
  }
17904
- }
17905
- extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
17906
- const newLen = newTokens.length, oldLen = oldTokens.length;
17907
- let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
17908
- while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
17909
- newPos++;
17910
- oldPos++;
17911
- commonCount++;
17912
- if (options.oneChangePerToken) {
17913
- basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
17945
+ const contentsText = utf8Decoder.decode(contents);
17946
+ for (const line of contentsText.split("\n")) {
17947
+ if (line.length === 0)
17948
+ continue;
17949
+ let parsed;
17950
+ try {
17951
+ parsed = JSON.parse(line);
17952
+ } catch {
17953
+ continue;
17954
+ }
17955
+ if (!isRecord7(parsed) || parsed.type !== "session")
17956
+ continue;
17957
+ if (typeof parsed.id !== "string" || parsed.id.length === 0 || typeof parsed.cwd !== "string" || parsed.cwd.length === 0) {
17958
+ throw new Error("Omp Session header is invalid");
17914
17959
  }
17960
+ return { type: "session", id: parsed.id, cwd: parsed.cwd };
17915
17961
  }
17916
- if (commonCount && !options.oneChangePerToken) {
17917
- basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
17918
- }
17919
- basePath.oldPos = oldPos;
17920
- return newPos;
17921
- }
17922
- equals(left, right, options) {
17923
- if (options.comparator) {
17924
- return options.comparator(left, right);
17925
- } else {
17926
- return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
17927
- }
17928
- }
17929
- removeEmpty(array2) {
17930
- const ret = [];
17931
- for (let i = 0; i < array2.length; i++) {
17932
- if (array2[i]) {
17933
- ret.push(array2[i]);
17934
- }
17935
- }
17936
- return ret;
17937
- }
17938
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
17939
- castInput(value, options) {
17940
- return value;
17941
- }
17942
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
17943
- tokenize(value, options) {
17944
- return Array.from(value);
17945
- }
17946
- join(chars) {
17947
- return chars.join("");
17962
+ throw new Error("Omp Session header is invalid");
17963
+ } finally {
17964
+ await handle.close();
17948
17965
  }
17949
- postProcess(changeObjects, options) {
17950
- return changeObjects;
17966
+ }
17967
+ async function verifyOmpSessionCwd(input) {
17968
+ if (!input.sessionFile)
17969
+ throw new Error("Omp Fork Session has no persisted Session file");
17970
+ const header = await readOmpSessionHeader(input.sessionFile);
17971
+ if (header.id !== input.sessionId) {
17972
+ throw new Error("Omp Fork Session header identity does not match RPC state");
17951
17973
  }
17952
- get useLongestToken() {
17953
- return false;
17974
+ const [actualCwd, expectedCwd] = await Promise.all([
17975
+ realpath2(header.cwd),
17976
+ realpath2(input.expectedCwd)
17977
+ ]);
17978
+ if (actualCwd !== expectedCwd) {
17979
+ throw new Error("Omp Fork Session did not bind the requested cwd");
17954
17980
  }
17955
- buildValues(lastComponent, newTokens, oldTokens) {
17956
- const components = [];
17957
- let nextComponent;
17958
- while (lastComponent) {
17959
- components.push(lastComponent);
17960
- nextComponent = lastComponent.previousComponent;
17961
- delete lastComponent.previousComponent;
17962
- lastComponent = nextComponent;
17981
+ }
17982
+
17983
+ // dist/omp-protocol.js
17984
+ var MAX_FRAME_BYTES = 1024 * 1024;
17985
+ var MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;
17986
+ var MAX_CHUNK_BYTES = 256 * 1024;
17987
+ function isObject2(value) {
17988
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17989
+ }
17990
+ var OmpFrameDecoder = class {
17991
+ #pending = null;
17992
+ push(value) {
17993
+ if (!isObject2(value))
17994
+ throw new Error("OMP RPC frame must be a JSON object");
17995
+ if (value.type !== "rpc_chunk") {
17996
+ if (this.#pending)
17997
+ throw new Error("OMP RPC chunk sequence was interrupted");
17998
+ return value;
17963
17999
  }
17964
- components.reverse();
17965
- const componentLen = components.length;
17966
- let componentPos = 0, newPos = 0, oldPos = 0;
17967
- for (; componentPos < componentLen; componentPos++) {
17968
- const component = components[componentPos];
17969
- if (!component.removed) {
17970
- if (!component.added && this.useLongestToken) {
17971
- let value = newTokens.slice(newPos, newPos + component.count);
17972
- value = value.map(function(value2, i) {
17973
- const oldValue = oldTokens[oldPos + i];
17974
- return oldValue.length > value2.length ? oldValue : value2;
17975
- });
17976
- component.value = this.join(value);
17977
- } else {
17978
- component.value = this.join(newTokens.slice(newPos, newPos + component.count));
17979
- }
17980
- newPos += component.count;
17981
- if (!component.added) {
17982
- oldPos += component.count;
17983
- }
17984
- } else {
17985
- component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
17986
- oldPos += component.count;
17987
- }
18000
+ const chunkId = value.chunkId;
18001
+ const index = value.index;
18002
+ const count2 = value.count;
18003
+ const byteLength = value.byteLength;
18004
+ const data = value.data;
18005
+ if (typeof chunkId !== "string" || chunkId.length === 0 || chunkId.length > 128 || !Number.isInteger(index) || !Number.isInteger(count2) || !Number.isInteger(byteLength) || index < 0 || count2 < 2 || index >= count2 || byteLength < MAX_FRAME_BYTES || byteLength > MAX_REASSEMBLED_BYTES || typeof data !== "string" || data.length === 0)
18006
+ throw new Error("Invalid OMP RPC chunk metadata");
18007
+ let bytes;
18008
+ try {
18009
+ bytes = Buffer.from(data, "base64");
18010
+ } catch {
18011
+ throw new Error("Invalid OMP RPC chunk data");
17988
18012
  }
17989
- return components;
18013
+ if (bytes.length === 0 || bytes.length > MAX_CHUNK_BYTES || bytes.toString("base64") !== data) {
18014
+ throw new Error("Invalid OMP RPC chunk data");
18015
+ }
18016
+ const chunkIndex = index;
18017
+ const chunkCount = count2;
18018
+ const declaredByteLength = byteLength;
18019
+ if (!this.#pending) {
18020
+ if (chunkIndex !== 0)
18021
+ throw new Error("OMP RPC chunk sequence must start at index 0");
18022
+ this.#pending = {
18023
+ chunkId,
18024
+ count: chunkCount,
18025
+ byteLength: declaredByteLength,
18026
+ nextIndex: 0,
18027
+ chunks: [],
18028
+ received: 0
18029
+ };
18030
+ }
18031
+ const pending = this.#pending;
18032
+ if (!pending)
18033
+ throw new Error("OMP RPC chunk state was lost");
18034
+ if (pending.chunkId !== chunkId || pending.count !== chunkCount || pending.byteLength !== declaredByteLength || pending.nextIndex !== chunkIndex) {
18035
+ throw new Error("OMP RPC chunk sequence mismatch");
18036
+ }
18037
+ pending.chunks.push(bytes);
18038
+ pending.received += bytes.length;
18039
+ pending.nextIndex += 1;
18040
+ if (pending.received > pending.byteLength)
18041
+ throw new Error("OMP RPC chunk sequence exceeds its declared length");
18042
+ if (pending.nextIndex < pending.count)
18043
+ return null;
18044
+ if (pending.received !== pending.byteLength)
18045
+ throw new Error("OMP RPC chunk sequence length mismatch");
18046
+ this.#pending = null;
18047
+ let decoded;
18048
+ try {
18049
+ decoded = JSON.parse(Buffer.concat(pending.chunks).toString("utf8"));
18050
+ } catch {
18051
+ throw new Error("Failed to decode reassembled OMP RPC frame");
18052
+ }
18053
+ if (!isObject2(decoded))
18054
+ throw new Error("Reassembled OMP RPC frame must be an object");
18055
+ return decoded;
17990
18056
  }
17991
18057
  };
17992
18058
 
17993
- // ../../../node_modules/diff/libesm/diff/line.js
17994
- var LineDiff = class extends Diff {
17995
- constructor() {
17996
- super(...arguments);
17997
- this.tokenize = tokenize;
18059
+ // dist/omp-rpc-session.js
18060
+ var OmpRpcFaultError = class extends Error {
18061
+ kind;
18062
+ diagnostic;
18063
+ constructor(kind, message3, diagnostic) {
18064
+ super(message3);
18065
+ this.kind = kind;
18066
+ this.diagnostic = diagnostic;
18067
+ this.name = "OmpRpcFaultError";
17998
18068
  }
17999
- equals(left, right, options) {
18000
- if (options.ignoreWhitespace) {
18001
- if (!options.newlineIsToken || !left.includes("\n")) {
18002
- left = left.trim();
18003
- }
18004
- if (!options.newlineIsToken || !right.includes("\n")) {
18005
- right = right.trim();
18006
- }
18007
- } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
18008
- if (left.endsWith("\n")) {
18009
- left = left.slice(0, -1);
18010
- }
18011
- if (right.endsWith("\n")) {
18012
- right = right.slice(0, -1);
18013
- }
18014
- }
18015
- return super.equals(left, right, options);
18069
+ };
18070
+ var OmpRpcUnsupportedCommandError = class extends Error {
18071
+ command;
18072
+ constructor(command) {
18073
+ super(`Omp RPC does not support '${command}'`);
18074
+ this.command = command;
18075
+ this.name = "OmpRpcUnsupportedCommandError";
18016
18076
  }
18017
18077
  };
18018
- var lineDiff = new LineDiff();
18019
- function diffLines(oldStr, newStr, options) {
18020
- return lineDiff.diff(oldStr, newStr, options);
18078
+ var textDecoder = new TextDecoder("utf-8", { fatal: true });
18079
+ function isRecord8(value) {
18080
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18021
18081
  }
18022
- function tokenize(value, options) {
18023
- if (options.stripTrailingCr) {
18024
- value = value.replace(/\r\n/g, "\n");
18082
+ function message2(value) {
18083
+ return value instanceof Error ? value.message : String(value);
18084
+ }
18085
+ function nonBlankString(value) {
18086
+ return typeof value === "string" && value.trim().length > 0;
18087
+ }
18088
+ function parseNativeModel(value, context) {
18089
+ if (value === null || value === void 0)
18090
+ return null;
18091
+ if (!isRecord8(value) || !nonBlankString(value.provider) || !nonBlankString(value.id)) {
18092
+ throw new OmpRpcFaultError("protocolError", `Omp RPC returned an invalid ${context} Model`);
18025
18093
  }
18026
- const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
18027
- if (!linesAndNewlines[linesAndNewlines.length - 1]) {
18028
- linesAndNewlines.pop();
18094
+ return { provider: value.provider, id: value.id };
18095
+ }
18096
+ function sessionStateData(response) {
18097
+ const data = isRecord8(response.data) ? response.data : null;
18098
+ if (!data)
18099
+ throw new OmpRpcFaultError("protocolError", "Omp RPC state response has no data");
18100
+ return data;
18101
+ }
18102
+ function parseSessionState(response) {
18103
+ const data = sessionStateData(response);
18104
+ const model2 = parseNativeModel(data.model, "state");
18105
+ if (!nonBlankString(data.sessionId)) {
18106
+ throw new OmpRpcFaultError("protocolError", "Omp RPC state has no stable Session identity");
18029
18107
  }
18030
- for (let i = 0; i < linesAndNewlines.length; i++) {
18031
- const line = linesAndNewlines[i];
18032
- if (i % 2 && !options.newlineIsToken) {
18033
- retLines[retLines.length - 1] += line;
18034
- } else {
18035
- retLines.push(line);
18036
- }
18108
+ const thinkingLevel = data.thinkingLevel === void 0 ? null : harnessThinkingOptionIdSchema.safeParse(data.thinkingLevel);
18109
+ if (thinkingLevel !== null && !thinkingLevel.success) {
18110
+ throw new OmpRpcFaultError("protocolError", "Omp RPC state has an invalid Thinking level");
18037
18111
  }
18038
- return retLines;
18112
+ const thinking = isRecord8(data.model) && isRecord8(data.model.thinking) ? data.model.thinking : null;
18113
+ const availableThinkingLevels = thinking && Array.isArray(thinking.efforts) ? thinking.efforts.flatMap((level) => {
18114
+ const parsed = harnessThinkingOptionIdSchema.safeParse(level);
18115
+ return parsed.success ? [parsed.data] : [];
18116
+ }) : null;
18117
+ return {
18118
+ sessionId: data.sessionId,
18119
+ sessionFile: typeof data.sessionFile === "string" ? data.sessionFile : null,
18120
+ provider: model2?.provider ?? null,
18121
+ modelId: model2?.id ?? null,
18122
+ thinkingLevel: thinkingLevel?.data ?? null,
18123
+ contextUsage: optionalOmpStateContextUsage(data.contextUsage),
18124
+ availableThinkingLevels
18125
+ };
18039
18126
  }
18040
-
18041
- // ../../../node_modules/diff/libesm/patch/parse.js
18042
- function parsePatch(uniDiff) {
18043
- const diffstr = uniDiff.split(/\n/), list = [];
18044
- let i = 0;
18045
- function parseIndex() {
18046
- const index = {};
18047
- list.push(index);
18048
- while (i < diffstr.length) {
18049
- const line = diffstr[i];
18050
- if (/^(---|\+\+\+|@@)\s/.test(line)) {
18051
- break;
18052
- }
18053
- const header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line);
18054
- if (header) {
18055
- index.index = header[1];
18056
- }
18057
- i++;
18058
- }
18059
- parseFileHeader(index);
18060
- parseFileHeader(index);
18061
- index.hunks = [];
18062
- while (i < diffstr.length) {
18063
- const line = diffstr[i];
18064
- if (/^(Index:\s|diff\s|---\s|\+\+\+\s|===================================================================)/.test(line)) {
18065
- break;
18066
- } else if (/^@@/.test(line)) {
18067
- index.hunks.push(parseHunk());
18068
- } else if (line) {
18069
- throw new Error("Unknown line " + (i + 1) + " " + JSON.stringify(line));
18070
- } else {
18071
- i++;
18072
- }
18073
- }
18127
+ function parseSessionStreaming(response) {
18128
+ const isStreaming = sessionStateData(response).isStreaming;
18129
+ if (typeof isStreaming !== "boolean") {
18130
+ throw new OmpRpcFaultError("protocolError", "Omp RPC state has no Streaming status");
18074
18131
  }
18075
- function parseFileHeader(index) {
18076
- const fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]);
18077
- if (fileHeader) {
18078
- const data = fileHeader[2].split(" ", 2), header = (data[1] || "").trim();
18079
- let fileName = data[0].replace(/\\\\/g, "\\");
18080
- if (/^".*"$/.test(fileName)) {
18081
- fileName = fileName.substr(1, fileName.length - 2);
18082
- }
18083
- if (fileHeader[1] === "---") {
18084
- index.oldFileName = fileName;
18085
- index.oldHeader = header;
18086
- } else {
18087
- index.newFileName = fileName;
18088
- index.newHeader = header;
18089
- }
18090
- i++;
18091
- }
18132
+ return isStreaming;
18133
+ }
18134
+ function cancellationSettled(response, terminalEnd) {
18135
+ const data = sessionStateData(response);
18136
+ return !parseSessionStreaming(response) && (data.isSettled === true || data.isSettled === void 0 && terminalEnd) && data.isCompacting !== true && data.hasPendingAsyncWork !== true && !(typeof data.queuedMessageCount === "number" && data.queuedMessageCount > 0);
18137
+ }
18138
+ function parseAvailableModels(response) {
18139
+ const data = isRecord8(response.data) ? response.data : null;
18140
+ if (!data || !Array.isArray(data.models)) {
18141
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Model catalog response has no models");
18092
18142
  }
18093
- function parseHunk() {
18094
- var _a3;
18095
- const chunkHeaderIndex = i, chunkHeaderLine = diffstr[i++], chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
18096
- const hunk = {
18097
- oldStart: +chunkHeader[1],
18098
- oldLines: typeof chunkHeader[2] === "undefined" ? 1 : +chunkHeader[2],
18099
- newStart: +chunkHeader[3],
18100
- newLines: typeof chunkHeader[4] === "undefined" ? 1 : +chunkHeader[4],
18101
- lines: []
18102
- };
18103
- if (hunk.oldLines === 0) {
18104
- hunk.oldStart += 1;
18105
- }
18106
- if (hunk.newLines === 0) {
18107
- hunk.newStart += 1;
18108
- }
18109
- let addCount = 0, removeCount = 0;
18110
- for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || ((_a3 = diffstr[i]) === null || _a3 === void 0 ? void 0 : _a3.startsWith("\\"))); i++) {
18111
- const operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? " " : diffstr[i][0];
18112
- if (operation === "+" || operation === "-" || operation === " " || operation === "\\") {
18113
- hunk.lines.push(diffstr[i]);
18114
- if (operation === "+") {
18115
- addCount++;
18116
- } else if (operation === "-") {
18117
- removeCount++;
18118
- } else if (operation === " ") {
18119
- addCount++;
18120
- removeCount++;
18121
- }
18122
- } else {
18123
- throw new Error(`Hunk at line ${chunkHeaderIndex + 1} contained invalid line ${diffstr[i]}`);
18124
- }
18125
- }
18126
- if (!addCount && hunk.newLines === 1) {
18127
- hunk.newLines = 0;
18128
- }
18129
- if (!removeCount && hunk.oldLines === 1) {
18130
- hunk.oldLines = 0;
18131
- }
18132
- if (addCount !== hunk.newLines) {
18133
- throw new Error("Added line count did not match for hunk at line " + (chunkHeaderIndex + 1));
18134
- }
18135
- if (removeCount !== hunk.oldLines) {
18136
- throw new Error("Removed line count did not match for hunk at line " + (chunkHeaderIndex + 1));
18143
+ return data.models.map((model2) => {
18144
+ const parsed = parseNativeModel(model2, "catalog");
18145
+ if (!parsed || !isRecord8(model2) || typeof model2.reasoning !== "boolean") {
18146
+ throw new OmpRpcFaultError("protocolError", "Omp RPC catalog contains a Model without reasoning capability");
18137
18147
  }
18138
- return hunk;
18148
+ return { ...parsed, reasoning: model2.reasoning };
18149
+ });
18150
+ }
18151
+ function parseSubagentMessages(response) {
18152
+ const data = isRecord8(response.data) ? response.data : null;
18153
+ 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)) {
18154
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript response is invalid");
18139
18155
  }
18140
- while (i < diffstr.length) {
18141
- parseIndex();
18156
+ const entries = data.entries.flatMap((value) => isRecord8(value) ? [value] : []);
18157
+ const messages = data.messages.flatMap((value) => isRecord8(value) ? [value] : []);
18158
+ if (entries.length !== data.entries.length || messages.length !== data.messages.length) {
18159
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript contains invalid data");
18142
18160
  }
18143
- return list;
18161
+ return {
18162
+ sessionFile: data.sessionFile,
18163
+ fromByte: data.fromByte,
18164
+ nextByte: data.nextByte,
18165
+ reset: data.reset,
18166
+ entries,
18167
+ messages
18168
+ };
18144
18169
  }
18145
-
18146
- // ../../../node_modules/diff/libesm/patch/create.js
18147
- function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
18148
- let optionsObj;
18149
- if (!options) {
18150
- optionsObj = {};
18151
- } else if (typeof options === "function") {
18152
- optionsObj = { callback: options };
18153
- } else {
18154
- optionsObj = options;
18170
+ function subagentStatus(value) {
18171
+ if (value === "pending" || value === "running" || value === "completed")
18172
+ return value;
18173
+ if (value === "failed" || value === "aborted" || value === "interrupted")
18174
+ return "failed";
18175
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent status is invalid");
18176
+ }
18177
+ function assistantText(value) {
18178
+ if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
18179
+ return null;
18180
+ return value.content.filter((content) => isRecord8(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
18181
+ }
18182
+ function assistantMessageId(value) {
18183
+ if (!isRecord8(value) || value.role !== "assistant")
18184
+ return null;
18185
+ return nonBlankString(value.responseId) ? value.responseId : null;
18186
+ }
18187
+ function extractReasoningText(content) {
18188
+ if (!isRecord8(content))
18189
+ return null;
18190
+ const type = String(content.type ?? "");
18191
+ if (type === "thinking" || type === "reasoning" || type === "thought") {
18192
+ const text = content.thinking ?? content.reasoning ?? content.text ?? content.delta;
18193
+ return typeof text === "string" ? text : null;
18155
18194
  }
18156
- if (typeof optionsObj.context === "undefined") {
18157
- optionsObj.context = 4;
18195
+ return null;
18196
+ }
18197
+ function assistantReasoning(value) {
18198
+ if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
18199
+ return null;
18200
+ return value.content.map(extractReasoningText).filter((text) => typeof text === "string").join("");
18201
+ }
18202
+ function assistantFailure(value) {
18203
+ if (!isRecord8(value) || value.role !== "assistant")
18204
+ return void 0;
18205
+ if (value.stopReason !== "error" && value.stopReason !== "aborted")
18206
+ return null;
18207
+ const fallback = value.stopReason === "aborted" ? "Omp assistant message was aborted" : "Omp assistant message failed";
18208
+ return new Error(nonBlankString(value.errorMessage) ? value.errorMessage : fallback);
18209
+ }
18210
+ function signalProcessTree(child, signal) {
18211
+ if (!child.pid)
18212
+ return;
18213
+ if (process.platform === "win32") {
18214
+ spawnSync("taskkill.exe", ["/pid", String(child.pid), "/t", "/f"], {
18215
+ stdio: "ignore",
18216
+ windowsHide: true
18217
+ });
18218
+ return;
18158
18219
  }
18159
- const context = optionsObj.context;
18160
- if (optionsObj.newlineIsToken) {
18161
- throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
18220
+ try {
18221
+ process.kill(-child.pid, signal);
18222
+ } catch (error51) {
18223
+ if (!isRecord8(error51) || error51.code !== "ESRCH")
18224
+ throw error51;
18162
18225
  }
18163
- if (!optionsObj.callback) {
18164
- return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
18165
- } else {
18166
- const { callback } = optionsObj;
18167
- diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
18168
- const patch = diffLinesResultToPatch(diff);
18169
- callback(patch);
18170
- } }));
18226
+ }
18227
+ function waitForExit(child, timeoutMs) {
18228
+ if (child.exitCode !== null || child.signalCode !== null)
18229
+ return Promise.resolve(true);
18230
+ return Promise.race([
18231
+ new Promise((resolve) => child.once("exit", () => resolve(true))),
18232
+ new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
18233
+ ]);
18234
+ }
18235
+ function ompRpcProcessCommand(options, dependencies = {}) {
18236
+ if (options.sessionFile && options.forkSessionFile) {
18237
+ throw new Error("Omp RPC cannot combine Session resume and Fork startup");
18171
18238
  }
18172
- function diffLinesResultToPatch(diff) {
18173
- if (!diff) {
18174
- return;
18175
- }
18176
- diff.push({ value: "", lines: [] });
18177
- function contextLines(lines) {
18178
- return lines.map(function(entry) {
18179
- return " " + entry;
18180
- });
18181
- }
18182
- const hunks = [];
18183
- let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
18184
- for (let i = 0; i < diff.length; i++) {
18185
- const current = diff[i], lines = current.lines || splitLines(current.value);
18186
- current.lines = lines;
18187
- if (current.added || current.removed) {
18188
- if (!oldRangeStart) {
18189
- const prev = diff[i - 1];
18190
- oldRangeStart = oldLine;
18191
- newRangeStart = newLine;
18192
- if (prev) {
18193
- curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
18194
- oldRangeStart -= curRange.length;
18195
- newRangeStart -= curRange.length;
18196
- }
18197
- }
18198
- for (const line of lines) {
18199
- curRange.push((current.added ? "+" : "-") + line);
18200
- }
18201
- if (current.added) {
18202
- newLine += lines.length;
18203
- } else {
18204
- oldLine += lines.length;
18205
- }
18206
- } else {
18207
- if (oldRangeStart) {
18208
- if (lines.length <= context * 2 && i < diff.length - 2) {
18209
- for (const line of contextLines(lines)) {
18210
- curRange.push(line);
18211
- }
18212
- } else {
18213
- const contextSize = Math.min(lines.length, context);
18214
- for (const line of contextLines(lines.slice(0, contextSize))) {
18215
- curRange.push(line);
18216
- }
18217
- const hunk = {
18218
- oldStart: oldRangeStart,
18219
- oldLines: oldLine - oldRangeStart + contextSize,
18220
- newStart: newRangeStart,
18221
- newLines: newLine - newRangeStart + contextSize,
18222
- lines: curRange
18223
- };
18224
- hunks.push(hunk);
18225
- oldRangeStart = 0;
18226
- newRangeStart = 0;
18227
- curRange = [];
18228
- }
18229
- }
18230
- oldLine += lines.length;
18231
- newLine += lines.length;
18232
- }
18239
+ if (options.model && (options.sessionFile || options.forkSessionFile)) {
18240
+ throw new Error("Omp RPC cannot combine a startup Model with Session restore or Fork");
18241
+ }
18242
+ const platform = dependencies.platform ?? process.platform;
18243
+ const command = resolveOmpExecutable({
18244
+ ...options.command ? { command: options.command } : {},
18245
+ environment: options.environment
18246
+ }, {
18247
+ platform,
18248
+ ...dependencies.homeDirectory ? { homeDirectory: dependencies.homeDirectory } : {},
18249
+ ...dependencies.isExecutable ? { isExecutable: dependencies.isExecutable } : {}
18250
+ });
18251
+ const sessionArguments = options.forkSessionFile ? ["--fork", options.forkSessionFile] : options.sessionFile ? ["--resume", options.sessionFile] : [];
18252
+ const modelArguments = options.model ? ["--provider", options.model.provider, "--model", options.model.id] : [];
18253
+ const permissionArguments = options.permissionMode ? ["--approval-mode", options.permissionMode] : [];
18254
+ const arguments_ = [
18255
+ "--mode",
18256
+ // OMP only creates ask and connects its tool UI in rpc-ui mode.
18257
+ "rpc-ui",
18258
+ ...permissionArguments,
18259
+ ...modelArguments,
18260
+ ...sessionArguments
18261
+ ];
18262
+ const extension = path6.win32.extname(command).toLowerCase();
18263
+ if (platform !== "win32" || ![".cmd", ".bat"].includes(extension)) {
18264
+ return { command, arguments: arguments_, windowsVerbatimArguments: false };
18265
+ }
18266
+ const quote = (value) => `"${value.replaceAll("%", "%%").replaceAll('"', '""')}"`;
18267
+ const commandLine = [command, ...arguments_].map(quote).join(" ");
18268
+ return {
18269
+ command: options.environment?.ComSpec ?? options.environment?.COMSPEC ?? "cmd.exe",
18270
+ arguments: ["/d", "/v:off", "/s", "/c", `"${commandLine}"`],
18271
+ windowsVerbatimArguments: true
18272
+ };
18273
+ }
18274
+ var nodeProcessAdapter = {
18275
+ spawn(options) {
18276
+ const invocation = ompRpcProcessCommand(options);
18277
+ return spawn(invocation.command, invocation.arguments, {
18278
+ cwd: options.cwd,
18279
+ env: options.environment,
18280
+ detached: process.platform !== "win32",
18281
+ stdio: ["pipe", "pipe", "pipe"],
18282
+ windowsHide: true,
18283
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments
18284
+ });
18285
+ }
18286
+ };
18287
+ var OmpRpcSession = class {
18288
+ #options;
18289
+ #processAdapter;
18290
+ #activeTurn = null;
18291
+ #buffer = Buffer.alloc(0);
18292
+ #child = null;
18293
+ #closed = false;
18294
+ #closePromise = null;
18295
+ #compactionActive = false;
18296
+ #compactionTurn = null;
18297
+ #compactionTimeout = null;
18298
+ #failed = false;
18299
+ #pending = /* @__PURE__ */ new Map();
18300
+ #state = null;
18301
+ #availableCommands = null;
18302
+ #latestCacheHitRatePercent;
18303
+ #manualCompaction = null;
18304
+ #usageRequestStartedAtMs = null;
18305
+ #stderrTail = "";
18306
+ #frameDecoder = new OmpFrameDecoder();
18307
+ #startupFaultResolve = null;
18308
+ #startupFault = new Promise((resolve) => {
18309
+ this.#startupFaultResolve = resolve;
18310
+ });
18311
+ #readyResolve = null;
18312
+ #ready = new Promise((resolve) => {
18313
+ this.#readyResolve = resolve;
18314
+ });
18315
+ constructor(options, processAdapter = nodeProcessAdapter) {
18316
+ if (options.sessionFile && options.forkSessionFile) {
18317
+ throw new Error("Omp RPC cannot combine Session resume and Fork startup");
18233
18318
  }
18234
- for (const hunk of hunks) {
18235
- for (let i = 0; i < hunk.lines.length; i++) {
18236
- if (hunk.lines[i].endsWith("\n")) {
18237
- hunk.lines[i] = hunk.lines[i].slice(0, -1);
18238
- } else {
18239
- hunk.lines.splice(i + 1, 0, "\");
18240
- i++;
18241
- }
18242
- }
18319
+ if (options.model && (options.sessionFile || options.forkSessionFile)) {
18320
+ throw new Error("Omp RPC cannot combine a startup Model with Session restore or Fork");
18243
18321
  }
18244
- return {
18245
- oldFileName,
18246
- newFileName,
18247
- oldHeader,
18248
- newHeader,
18249
- hunks
18322
+ this.#options = {
18323
+ commandTimeoutMs: 3e4,
18324
+ compactionTimeoutMs: 3e5,
18325
+ // Native Abort waits for tools, persistence and deferred work to unwind.
18326
+ // Stable cancellation must outlast the Host's 20s steering wait.
18327
+ cancelTimeoutMs: 3e4,
18328
+ closeTimeoutMs: 2e3,
18329
+ ...options
18250
18330
  };
18331
+ this.#processAdapter = processAdapter;
18251
18332
  }
18252
- }
18253
- function formatPatch(patch) {
18254
- if (Array.isArray(patch)) {
18255
- return patch.map(formatPatch).join("\n");
18333
+ get state() {
18334
+ if (!this.#state)
18335
+ throw new Error("Omp RPC Session has not started");
18336
+ return this.#state;
18256
18337
  }
18257
- const ret = [];
18258
- if (patch.oldFileName == patch.newFileName) {
18259
- ret.push("Index: " + patch.oldFileName);
18338
+ get stderrTail() {
18339
+ return this.#stderrTail;
18260
18340
  }
18261
- ret.push("===================================================================");
18262
- ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : " " + patch.oldHeader));
18263
- ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : " " + patch.newHeader));
18264
- for (let i = 0; i < patch.hunks.length; i++) {
18265
- const hunk = patch.hunks[i];
18266
- if (hunk.oldLines === 0) {
18267
- hunk.oldStart -= 1;
18268
- }
18269
- if (hunk.newLines === 0) {
18270
- hunk.newStart -= 1;
18341
+ async start() {
18342
+ if (this.#child || this.#closed)
18343
+ throw new Error("Omp RPC Session cannot be started twice");
18344
+ const child = this.#processAdapter.spawn({
18345
+ cwd: this.#options.cwd,
18346
+ ...this.#options.command ? { command: this.#options.command } : {},
18347
+ environment: withNodeRuntimeOnPath({
18348
+ ...process.env,
18349
+ ...this.#options.environment,
18350
+ OMP_SKIP_VERSION_CHECK: "1",
18351
+ OMP_TELEMETRY: "0"
18352
+ }),
18353
+ ...this.#options.sessionFile ? { sessionFile: this.#options.sessionFile } : {},
18354
+ ...this.#options.forkSessionFile ? { forkSessionFile: this.#options.forkSessionFile } : {},
18355
+ ...this.#options.model ? { model: this.#options.model } : {},
18356
+ ...this.#options.permissionMode ? { permissionMode: this.#options.permissionMode } : {}
18357
+ });
18358
+ this.#child = child;
18359
+ child.stdout.on("data", (chunk) => this.#push(chunk));
18360
+ child.stdout.on("end", () => {
18361
+ if (this.#buffer.length !== 0) {
18362
+ this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC stdout ended mid-frame"));
18363
+ }
18364
+ });
18365
+ child.stderr.on("data", (chunk) => {
18366
+ this.#stderrTail = sanitizeDiagnosticTail(`${this.#stderrTail}${chunk.toString()}`);
18367
+ });
18368
+ child.once("error", (error51) => {
18369
+ const kind = isRecord8(error51) && error51.code === "ENOENT" ? "notInstalled" : "unavailable";
18370
+ this.#fail(new OmpRpcFaultError(kind, `Omp RPC failed to start: ${error51.message}`, this.stderrTail));
18371
+ });
18372
+ child.once("exit", (code, signal) => {
18373
+ if (!this.#closed) {
18374
+ this.#fail(new OmpRpcFaultError("processExited", `Omp RPC exited (code=${code}, signal=${signal})`, this.stderrTail));
18375
+ }
18376
+ });
18377
+ await Promise.race([
18378
+ new Promise((resolve, reject) => {
18379
+ child.once("spawn", resolve);
18380
+ child.once("error", reject);
18381
+ }),
18382
+ new Promise((_resolve, reject) => setTimeout(() => reject(new Error("Omp RPC start timed out")), this.#options.commandTimeoutMs))
18383
+ ]);
18384
+ let readyTimeout;
18385
+ try {
18386
+ await Promise.race([
18387
+ this.#ready,
18388
+ this.#startupFault.then((error51) => {
18389
+ throw error51;
18390
+ }),
18391
+ new Promise((_resolve, reject) => {
18392
+ readyTimeout = setTimeout(() => reject(new Error("Omp RPC ready signal timed out")), this.#options.commandTimeoutMs);
18393
+ })
18394
+ ]);
18395
+ } finally {
18396
+ clearTimeout(readyTimeout);
18271
18397
  }
18272
- ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
18273
- for (const line of hunk.lines) {
18274
- ret.push(line);
18398
+ await this.#send("negotiate_protocol", { protocolVersion: 2 }).catch(() => void 0);
18399
+ if (this.#options.subscribeSubagentEvents !== false) {
18400
+ await this.#send("set_subagent_subscription", { level: "events" }).catch(() => void 0);
18275
18401
  }
18276
- }
18277
- return ret.join("\n") + "\n";
18278
- }
18279
- function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
18280
- if (typeof options === "function") {
18281
- options = { callback: options };
18282
- }
18283
- if (!(options === null || options === void 0 ? void 0 : options.callback)) {
18284
- const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
18285
- if (!patchObj) {
18286
- return;
18402
+ try {
18403
+ this.#state = parseSessionState(await this.#send("get_state", {}));
18404
+ } catch (error51) {
18405
+ const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("unavailable", `Omp RPC state unavailable: ${message2(error51)}`);
18406
+ this.#fail(fault);
18407
+ throw fault;
18287
18408
  }
18288
- return formatPatch(patchObj);
18289
- } else {
18290
- const { callback } = options;
18291
- structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
18292
- if (!patchObj) {
18293
- callback(void 0);
18294
- } else {
18295
- callback(formatPatch(patchObj));
18296
- }
18297
- } }));
18298
- }
18299
- }
18300
- function splitLines(text) {
18301
- const hasTrailingNl = text.endsWith("\n");
18302
- const result = text.split("\n").map((line) => line + "\n");
18303
- if (hasTrailingNl) {
18304
- result.pop();
18305
- } else {
18306
- result.push(result.pop().slice(0, -1));
18409
+ return this;
18307
18410
  }
18308
- return result;
18309
- }
18310
-
18311
- // dist/omp-adapter.js
18312
- import { randomUUID as randomUUID2 } from "node:crypto";
18313
- import { realpath as realpath3, stat as stat2 } from "node:fs/promises";
18314
- import path7 from "node:path";
18315
-
18316
- // dist/omp-slash-commands.js
18317
- var OMP_LIVE_COMMAND_ID_PREFIX = "omp.slash.";
18318
- function isRecord6(value) {
18319
- return typeof value === "object" && value !== null && !Array.isArray(value);
18320
- }
18321
- function parseOmpAvailableCommands(value) {
18322
- if (!Array.isArray(value))
18323
- return [];
18324
- return value.flatMap((entry) => {
18325
- if (!isRecord6(entry))
18326
- return [];
18327
- const name = typeof entry.name === "string" ? entry.name.trim().replace(/^\//u, "") : "";
18328
- if (!name)
18329
- return [];
18330
- return [
18331
- {
18332
- name,
18333
- description: typeof entry.description === "string" ? entry.description : "",
18334
- source: typeof entry.source === "string" ? entry.source : null
18411
+ async getEntries() {
18412
+ try {
18413
+ if (this.state.sessionFile) {
18414
+ try {
18415
+ return await readOmpSessionHistory(this.state.sessionFile);
18416
+ } catch (error51) {
18417
+ if (!isRecord8(error51) || error51.code !== "ENOENT" || this.#options.sessionFile || this.#options.forkSessionFile)
18418
+ throw error51;
18419
+ const response2 = await this.#send("get_messages", {});
18420
+ const data2 = isRecord8(response2.data) ? response2.data : null;
18421
+ if (data2 && Array.isArray(data2.messages) && data2.messages.length === 0) {
18422
+ return { entries: [], leafId: null };
18423
+ }
18424
+ throw error51;
18425
+ }
18335
18426
  }
18336
- ];
18337
- });
18338
- }
18339
- function ompLiveCommands(native) {
18340
- return native.filter(({ source, name }) => source !== "builtin" || name === "context").map((command) => ({
18341
- name: command.name,
18342
- description: command.description,
18343
- kind: command.source === "skill" ? "skill" : "command"
18344
- }));
18345
- }
18346
-
18347
- // dist/omp-session-file.js
18348
- import { createReadStream as createReadStream3 } from "node:fs";
18349
- import { open, realpath as realpath2 } from "node:fs/promises";
18350
- import readline from "node:readline";
18351
- var MAX_SESSION_HEADER_BYTES = 64 * 1024;
18352
- var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
18353
- function isRecord7(value) {
18354
- return typeof value === "object" && value !== null && !Array.isArray(value);
18355
- }
18356
- function historyEntry(value) {
18357
- if (!isRecord7(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") {
18358
- return null;
18359
- }
18360
- return value;
18361
- }
18362
- async function readOmpSessionHistory(sessionFile, maxBytes) {
18363
- const entries = [];
18364
- let leafId = null;
18365
- let incomplete = false;
18366
- const lines = readline.createInterface({
18367
- input: createReadStream3(sessionFile, {
18368
- encoding: "utf8",
18369
- ...maxBytes === void 0 ? {} : { end: maxBytes - 1 }
18370
- }),
18371
- crlfDelay: Number.POSITIVE_INFINITY
18372
- });
18373
- try {
18374
- for await (const line of lines) {
18375
- if (line.length === 0)
18376
- continue;
18377
- let parsed;
18378
- try {
18379
- parsed = JSON.parse(line);
18380
- } catch {
18381
- incomplete = true;
18382
- continue;
18427
+ const response = await this.#send("get_messages", {});
18428
+ const data = isRecord8(response.data) ? response.data : null;
18429
+ if (!data || !Array.isArray(data.messages)) {
18430
+ throw new OmpRpcFaultError("protocolError", "Omp RPC messages response has no messages");
18383
18431
  }
18384
- const entry = historyEntry(parsed);
18385
- if (!entry) {
18386
- if (!isRecord7(parsed) || parsed.type !== "title" && parsed.type !== "session") {
18387
- incomplete = true;
18432
+ let branchEntryIds = null;
18433
+ try {
18434
+ const branchResponse = await this.#send("get_branch_messages", {});
18435
+ const branchData = isRecord8(branchResponse.data) ? branchResponse.data : null;
18436
+ if (branchData && Array.isArray(branchData.messages)) {
18437
+ const parsed = branchData.messages.flatMap((entry) => {
18438
+ if (!isRecord8(entry) || !nonBlankString(entry.entryId))
18439
+ return [];
18440
+ return [entry.entryId];
18441
+ });
18442
+ if (parsed.length === branchData.messages.length)
18443
+ branchEntryIds = parsed;
18388
18444
  }
18389
- continue;
18445
+ } catch (error51) {
18446
+ if (!(error51 instanceof OmpRpcUnsupportedCommandError))
18447
+ throw error51;
18390
18448
  }
18391
- entries.push(entry);
18392
- leafId = entry.id;
18449
+ let userIndex = 0;
18450
+ let parentId = null;
18451
+ const entries = data.messages.map((nativeMessage, index) => {
18452
+ const role = isRecord8(nativeMessage) ? nativeMessage.role : void 0;
18453
+ const branchId = role === "user" ? branchEntryIds?.[userIndex] : void 0;
18454
+ if (branchId)
18455
+ userIndex += 1;
18456
+ const id = branchId ?? (isRecord8(nativeMessage) && nonBlankString(nativeMessage.id) ? nativeMessage.id : `omp-message-${index}`);
18457
+ const entry = { id, parentId, type: "message", message: nativeMessage };
18458
+ parentId = id;
18459
+ return entry;
18460
+ });
18461
+ return { entries, leafId: parentId };
18462
+ } catch (error51) {
18463
+ if (error51 instanceof OmpRpcFaultError)
18464
+ this.#fail(error51);
18465
+ throw error51;
18393
18466
  }
18394
- } finally {
18395
- lines.close();
18396
18467
  }
18397
- return { entries, leafId, ...incomplete ? { incomplete: true } : {} };
18398
- }
18399
- async function readOmpSessionHeader(sessionFile) {
18400
- const handle = await open(sessionFile, "r");
18401
- try {
18402
- const buffer = Buffer.allocUnsafe(MAX_SESSION_HEADER_BYTES);
18403
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
18404
- const contents = buffer.subarray(0, bytesRead);
18405
- const newline = contents.indexOf(10);
18406
- if (newline < 0 && bytesRead === buffer.length) {
18407
- throw new Error("Omp Session header exceeds the supported size");
18468
+ async getSubagentMessages(input) {
18469
+ if ((input.subagentId === void 0 || !nonBlankString(input.subagentId)) && (input.sessionFile === void 0 || !nonBlankString(input.sessionFile))) {
18470
+ throw new Error("Omp Subagent transcript requires a Subagent ID or Session file");
18408
18471
  }
18409
- const contentsText = utf8Decoder.decode(contents);
18410
- for (const line of contentsText.split("\n")) {
18411
- if (line.length === 0)
18412
- continue;
18413
- let parsed;
18414
- try {
18415
- parsed = JSON.parse(line);
18416
- } catch {
18417
- continue;
18418
- }
18419
- if (!isRecord7(parsed) || parsed.type !== "session")
18420
- continue;
18421
- if (typeof parsed.id !== "string" || parsed.id.length === 0 || typeof parsed.cwd !== "string" || parsed.cwd.length === 0) {
18422
- throw new Error("Omp Session header is invalid");
18423
- }
18424
- return { type: "session", id: parsed.id, cwd: parsed.cwd };
18472
+ if (input.fromByte !== void 0 && (!Number.isSafeInteger(input.fromByte) || input.fromByte < 0)) {
18473
+ throw new Error("Omp Subagent transcript offset is invalid");
18425
18474
  }
18426
- throw new Error("Omp Session header is invalid");
18427
- } finally {
18428
- await handle.close();
18475
+ const response = await this.#send("get_subagent_messages", {
18476
+ ...input.subagentId ? { subagentId: input.subagentId } : {},
18477
+ ...input.sessionFile ? { sessionFile: input.sessionFile } : {},
18478
+ ...input.fromByte !== void 0 ? { fromByte: input.fromByte } : {}
18479
+ });
18480
+ return parseSubagentMessages(response);
18429
18481
  }
18430
- }
18431
- async function verifyOmpSessionCwd(input) {
18432
- if (!input.sessionFile)
18433
- throw new Error("Omp Fork Session has no persisted Session file");
18434
- const header = await readOmpSessionHeader(input.sessionFile);
18435
- if (header.id !== input.sessionId) {
18436
- throw new Error("Omp Fork Session header identity does not match RPC state");
18482
+ async compact(customInstructions, onEvent) {
18483
+ if (!this.#child || !this.#state || this.#closed || this.#failed) {
18484
+ throw new Error("Omp RPC Session is unavailable");
18485
+ }
18486
+ if (this.#activeTurn || this.#manualCompaction || this.#compactionActive) {
18487
+ throw new Error("Omp RPC Session already has an active operation");
18488
+ }
18489
+ const result = new Promise((resolve, reject) => {
18490
+ this.#manualCompaction = { onEvent, resolve, reject };
18491
+ });
18492
+ try {
18493
+ await this.#send("compact", customInstructions ? { customInstructions } : {});
18494
+ } catch (error51) {
18495
+ const pending = this.#manualCompaction;
18496
+ this.#manualCompaction = null;
18497
+ pending?.reject(error51 instanceof Error ? error51 : new Error(message2(error51)));
18498
+ }
18499
+ return result;
18437
18500
  }
18438
- const [actualCwd, expectedCwd] = await Promise.all([
18439
- realpath2(header.cwd),
18440
- realpath2(input.expectedCwd)
18441
- ]);
18442
- if (actualCwd !== expectedCwd) {
18443
- throw new Error("Omp Fork Session did not bind the requested cwd");
18501
+ async getSessionUsage() {
18502
+ try {
18503
+ const usage = parseOmpSessionUsage(await this.#send("get_session_stats", {}));
18504
+ await this.#ensureLatestCacheHitRate();
18505
+ return this.#withLatestCacheHitRate(usage);
18506
+ } catch (error51) {
18507
+ if (!(error51 instanceof OmpRpcUnsupportedCommandError))
18508
+ throw error51;
18509
+ const response = await this.#send("get_state", {});
18510
+ const observedState = parseSessionState(response);
18511
+ if (!this.#state || observedState.sessionId !== this.#state.sessionId || observedState.provider !== this.#state.provider || observedState.modelId !== this.#state.modelId) {
18512
+ throw new Error("Omp RPC Usage fallback does not match the confirmed Session state");
18513
+ }
18514
+ await this.#ensureLatestCacheHitRate();
18515
+ const usage = parseOmpStateContextUsage(response);
18516
+ return usage ? this.#withLatestCacheHitRate(usage) : null;
18517
+ }
18444
18518
  }
18445
- }
18446
-
18447
- // dist/omp-last-turn-rollback.js
18448
- function modelFromState(state) {
18449
- if (state.provider === null && state.modelId === null)
18450
- return null;
18451
- if (state.provider === null || state.modelId === null) {
18452
- throw new Error("Omp state contains a partial Model identity");
18519
+ async #ensureLatestCacheHitRate() {
18520
+ if (this.#latestCacheHitRatePercent !== void 0)
18521
+ return;
18522
+ try {
18523
+ this.#latestCacheHitRatePercent = latestOmpCacheHitRatePercent(await this.getEntries());
18524
+ } catch {
18525
+ this.#latestCacheHitRatePercent = null;
18526
+ }
18453
18527
  }
18454
- return { provider: state.provider, id: state.modelId };
18455
- }
18456
- async function rollbackOmpLastTurn(transport, sourceSessionId, cwd) {
18457
- const startupState = transport.state;
18458
- const startupSessionId = startupState.sessionId;
18459
- const currentModel = modelFromState(startupState);
18460
- const currentThinking = startupState.thinkingLevel;
18461
- const copiedHistory = await transport.getEntries();
18462
- const boundary = resolveOmpLastTurnBoundary(copiedHistory);
18463
- if (!boundary)
18464
- return { ok: false, reason: "empty" };
18465
- const sourceSnapshot = mapOmpSnapshot(copiedHistory, {
18466
- sessionId: startupSessionId,
18467
- model: currentModel
18468
- });
18469
- let state = await transport.fork(boundary.lastUserEntryId);
18470
- if (state.sessionId === sourceSessionId || state.sessionId === startupSessionId) {
18471
- throw new Error("Omp last-Turn rollback did not create a distinct Native Session");
18528
+ #withLatestCacheHitRate(usage) {
18529
+ return this.#latestCacheHitRatePercent === null || this.#latestCacheHitRatePercent === void 0 ? usage : parseHostUsage({ ...usage, cacheHitRatePercent: this.#latestCacheHitRatePercent });
18472
18530
  }
18473
- if (!sameOmpModel(modelFromState(state), currentModel)) {
18474
- if (!currentModel)
18475
- throw new Error("Omp last-Turn rollback changed the current Model");
18476
- state = await transport.selectModel(currentModel);
18531
+ async fork(entryId) {
18532
+ if (!nonBlankString(entryId))
18533
+ throw new Error("Omp RPC Fork requires an Entry identity");
18534
+ try {
18535
+ const response = await this.#send("branch", { entryId });
18536
+ const data = isRecord8(response.data) ? response.data : null;
18537
+ if (!data || data.cancelled === true) {
18538
+ throw new OmpRpcFaultError("unavailable", "Omp RPC Fork was cancelled");
18539
+ }
18540
+ this.#state = parseSessionState(await this.#send("get_state", {}));
18541
+ return this.#state;
18542
+ } catch (error51) {
18543
+ if (error51 instanceof OmpRpcFaultError)
18544
+ this.#fail(error51);
18545
+ throw error51;
18546
+ }
18477
18547
  }
18478
- if (state.thinkingLevel !== currentThinking) {
18479
- if (!currentThinking)
18480
- throw new Error("Omp last-Turn rollback changed current Thinking");
18481
- state = await transport.selectThinkingOption(currentThinking);
18548
+ verifySessionCwd(expectedCwd) {
18549
+ return verifyOmpSessionCwd({
18550
+ sessionFile: this.state.sessionFile,
18551
+ sessionId: this.state.sessionId,
18552
+ expectedCwd
18553
+ });
18482
18554
  }
18483
- if (!sameOmpModel(modelFromState(state), currentModel) || state.thinkingLevel !== currentThinking) {
18484
- throw new Error("Omp last-Turn rollback could not restore current Model and Thinking");
18555
+ async getAvailableModels() {
18556
+ try {
18557
+ return parseAvailableModels(await this.#send("get_available_models", {}));
18558
+ } catch (error51) {
18559
+ if (error51 instanceof OmpRpcFaultError)
18560
+ this.#fail(error51);
18561
+ throw error51;
18562
+ }
18485
18563
  }
18486
- const snapshot = mapOmpSnapshot(await transport.getEntries(), {
18487
- sessionId: state.sessionId,
18488
- model: modelFromState(state)
18489
- });
18490
- const expectedTurnKeys = sourceSnapshot.turns.slice(0, -1).map((turn) => turn.nativeTurnRef.nativeTurnKey);
18491
- const actualTurnKeys = snapshot.turns.map((turn) => turn.nativeTurnRef.nativeTurnKey);
18492
- if (snapshot.turns.length !== boundary.sourceTurnCount - 1 || actualTurnKeys.some((key, index) => key !== expectedTurnKeys[index])) {
18493
- throw new Error("Omp last-Turn rollback did not produce the exact retained history prefix");
18564
+ async getAvailableThinkingLevels() {
18565
+ return this.state.availableThinkingLevels;
18494
18566
  }
18495
- await transport.verifySessionCwd(cwd);
18496
- return { ok: true };
18497
- }
18498
-
18499
- // dist/omp-rpc-session.js
18500
- import { spawn, spawnSync } from "node:child_process";
18501
- import { randomUUID } from "node:crypto";
18502
- import path6 from "node:path";
18503
-
18504
- // dist/command.js
18505
- var ompDiscoverySpec = {
18506
- id: "omp",
18507
- command: "omp",
18508
- commandEnvironmentVariable: "OMP_COMMAND",
18509
- installRoots: {
18510
- posix: [
18511
- "~/.npm-global/bin",
18512
- "~/.local/bin",
18513
- VERSION_MANAGER_ROOTS,
18514
- "/opt/homebrew/bin",
18515
- "/usr/local/bin"
18516
- ],
18517
- windows: ["${APPDATA}/npm", "~/.local/bin", VERSION_MANAGER_ROOTS]
18567
+ async selectModel(model2) {
18568
+ await this.#send("set_model", { provider: model2.provider, modelId: model2.id });
18569
+ return this.#refreshState("Model");
18518
18570
  }
18519
- };
18520
- function resolveOmpExecutable(input, dependencies = {}) {
18521
- const resolution = resolveHarnessExecutable(ompDiscoverySpec, {
18522
- ...input.command ? { command: input.command } : {},
18523
- environment: input.environment,
18524
- ...dependencies.platform ? { platform: dependencies.platform } : {},
18525
- ...dependencies.homeDirectory ? { homeDirectory: dependencies.homeDirectory } : {}
18526
- }, { ...dependencies.isExecutable ? { isExecutable: dependencies.isExecutable } : {} });
18527
- return resolution?.executable ?? input.command ?? input.environment.OMP_COMMAND ?? "omp";
18528
- }
18529
-
18530
- // dist/omp-protocol.js
18531
- var MAX_FRAME_BYTES = 1024 * 1024;
18532
- var MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;
18533
- var MAX_CHUNK_BYTES = 256 * 1024;
18534
- function isObject2(value) {
18535
- return typeof value === "object" && value !== null && !Array.isArray(value);
18536
- }
18537
- var OmpFrameDecoder = class {
18538
- #pending = null;
18539
- push(value) {
18540
- if (!isObject2(value))
18541
- throw new Error("OMP RPC frame must be a JSON object");
18542
- if (value.type !== "rpc_chunk") {
18543
- if (this.#pending)
18544
- throw new Error("OMP RPC chunk sequence was interrupted");
18545
- return value;
18546
- }
18547
- const chunkId = value.chunkId;
18548
- const index = value.index;
18549
- const count2 = value.count;
18550
- const byteLength = value.byteLength;
18551
- const data = value.data;
18552
- if (typeof chunkId !== "string" || chunkId.length === 0 || chunkId.length > 128 || !Number.isInteger(index) || !Number.isInteger(count2) || !Number.isInteger(byteLength) || index < 0 || count2 < 2 || index >= count2 || byteLength < MAX_FRAME_BYTES || byteLength > MAX_REASSEMBLED_BYTES || typeof data !== "string" || data.length === 0)
18553
- throw new Error("Invalid OMP RPC chunk metadata");
18554
- let bytes;
18571
+ async selectThinkingOption(thinkingOptionId) {
18572
+ const level = harnessThinkingOptionIdSchema.parse(thinkingOptionId);
18573
+ await this.#send("set_thinking_level", { level });
18574
+ return this.#refreshState("Thinking");
18575
+ }
18576
+ async #refreshState(operation) {
18555
18577
  try {
18556
- bytes = Buffer.from(data, "base64");
18557
- } catch {
18558
- throw new Error("Invalid OMP RPC chunk data");
18578
+ this.#state = parseSessionState(await this.#send("get_state", {}));
18579
+ return this.#state;
18580
+ } catch (error51) {
18581
+ const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC ${operation} state could not be confirmed: ${message2(error51)}`);
18582
+ this.#fail(fault);
18583
+ throw fault;
18559
18584
  }
18560
- if (bytes.length === 0 || bytes.length > MAX_CHUNK_BYTES || bytes.toString("base64") !== data) {
18561
- throw new Error("Invalid OMP RPC chunk data");
18585
+ }
18586
+ async runTurn(text, onEvent) {
18587
+ if (!this.#child || !this.#state || this.#closed || this.#failed) {
18588
+ throw new Error("Omp RPC Session is unavailable");
18562
18589
  }
18563
- const chunkIndex = index;
18564
- const chunkCount = count2;
18565
- const declaredByteLength = byteLength;
18566
- if (!this.#pending) {
18567
- if (chunkIndex !== 0)
18568
- throw new Error("OMP RPC chunk sequence must start at index 0");
18569
- this.#pending = {
18570
- chunkId,
18571
- count: chunkCount,
18572
- byteLength: declaredByteLength,
18573
- nextIndex: 0,
18574
- chunks: [],
18575
- received: 0
18590
+ if (this.#activeTurn)
18591
+ throw new Error("Omp RPC Session already has an active Turn");
18592
+ if (text.length === 0)
18593
+ throw new Error("Omp text Turn must not be empty");
18594
+ const promptId = `codexhost-${randomUUID()}`;
18595
+ const settled = new Promise((resolve, reject) => {
18596
+ this.#activeTurn = {
18597
+ promptId,
18598
+ localResult: null,
18599
+ text: "",
18600
+ assistantMessageId: null,
18601
+ sawStreamedMessageText: false,
18602
+ sawStreamedMessageReasoning: false,
18603
+ lastFinalizedMessageText: null,
18604
+ lastFinalizedMessageReasoning: null,
18605
+ reasoningMessageOpen: false,
18606
+ onEvent,
18607
+ resolve,
18608
+ reject,
18609
+ failure: null,
18610
+ sawTool: false,
18611
+ tools: /* @__PURE__ */ new Map(),
18612
+ interactions: /* @__PURE__ */ new Map(),
18613
+ settlement: "pending",
18614
+ terminalEnd: false,
18615
+ cancellation: "none",
18616
+ cancellationTimeout: null,
18617
+ cancellationRecheck: null,
18618
+ cancellationChecking: false,
18619
+ cancellationReloaded: false,
18620
+ abortPromise: null
18576
18621
  };
18622
+ });
18623
+ try {
18624
+ await this.#send("prompt", { message: text }, promptId);
18625
+ } catch (error51) {
18626
+ this.#rejectActiveTurn(error51 instanceof Error ? error51 : new Error(message2(error51)));
18577
18627
  }
18578
- const pending = this.#pending;
18579
- if (!pending)
18580
- throw new Error("OMP RPC chunk state was lost");
18581
- if (pending.chunkId !== chunkId || pending.count !== chunkCount || pending.byteLength !== declaredByteLength || pending.nextIndex !== chunkIndex) {
18582
- throw new Error("OMP RPC chunk sequence mismatch");
18628
+ return settled;
18629
+ }
18630
+ respondToInteraction(response) {
18631
+ return this.#resolveInteraction(response, "cancelled" in response ? "cancelled" : "responded");
18632
+ }
18633
+ async #resolveInteraction(response, reason) {
18634
+ const active = this.#activeTurn;
18635
+ const pending = active?.interactions.get(response.requestId);
18636
+ if (!active || !pending || this.#closed || this.#failed) {
18637
+ throw new Error("Omp RPC interaction is not pending");
18583
18638
  }
18584
- pending.chunks.push(bytes);
18585
- pending.received += bytes.length;
18586
- pending.nextIndex += 1;
18587
- if (pending.received > pending.byteLength)
18588
- throw new Error("OMP RPC chunk sequence exceeds its declared length");
18589
- if (pending.nextIndex < pending.count)
18590
- return null;
18591
- if (pending.received !== pending.byteLength)
18592
- throw new Error("OMP RPC chunk sequence length mismatch");
18593
- this.#pending = null;
18594
- let decoded;
18639
+ if ("value" in response && !["select", "input", "editor"].includes(pending.request.method)) {
18640
+ throw new Error("Omp RPC interaction response type does not match the request");
18641
+ }
18642
+ if ("confirmed" in response && pending.request.method !== "confirm") {
18643
+ throw new Error("Omp RPC confirmation does not match the request");
18644
+ }
18645
+ const frame = "cancelled" in response ? {
18646
+ type: "extension_ui_response",
18647
+ id: response.requestId,
18648
+ cancelled: true,
18649
+ ...reason === "expired" ? { timedOut: true } : {}
18650
+ } : "confirmed" in response ? { type: "extension_ui_response", id: response.requestId, confirmed: response.confirmed } : { type: "extension_ui_response", id: response.requestId, value: response.value };
18651
+ if (!active.interactions.delete(response.requestId)) {
18652
+ throw new Error("Omp RPC interaction is not pending");
18653
+ }
18654
+ if (pending.timeout)
18655
+ clearTimeout(pending.timeout);
18656
+ active.onEvent({ type: "interaction.closed", requestId: response.requestId, reason });
18595
18657
  try {
18596
- decoded = JSON.parse(Buffer.concat(pending.chunks).toString("utf8"));
18597
- } catch {
18598
- throw new Error("Failed to decode reassembled OMP RPC frame");
18658
+ await this.#write(frame);
18659
+ } catch (error51) {
18660
+ const fault = new OmpRpcFaultError("protocolError", `Omp RPC interaction response failed: ${message2(error51)}`);
18661
+ this.#fail(fault);
18662
+ throw fault;
18599
18663
  }
18600
- if (!isObject2(decoded))
18601
- throw new Error("Reassembled OMP RPC frame must be an object");
18602
- return decoded;
18603
18664
  }
18604
- };
18605
-
18606
- // dist/omp-rpc-session.js
18607
- var OmpRpcFaultError = class extends Error {
18608
- kind;
18609
- diagnostic;
18610
- constructor(kind, message3, diagnostic) {
18611
- super(message3);
18612
- this.kind = kind;
18613
- this.diagnostic = diagnostic;
18614
- this.name = "OmpRpcFaultError";
18615
- }
18616
- };
18617
- var OmpRpcUnsupportedCommandError = class extends Error {
18618
- command;
18619
- constructor(command) {
18620
- super(`Omp RPC does not support '${command}'`);
18621
- this.command = command;
18622
- this.name = "OmpRpcUnsupportedCommandError";
18623
- }
18624
- };
18625
- var textDecoder = new TextDecoder("utf-8", { fatal: true });
18626
- function isRecord8(value) {
18627
- return typeof value === "object" && value !== null && !Array.isArray(value);
18628
- }
18629
- function message2(value) {
18630
- return value instanceof Error ? value.message : String(value);
18631
- }
18632
- function nonBlankString(value) {
18633
- return typeof value === "string" && value.trim().length > 0;
18634
- }
18635
- function parseNativeModel(value, context) {
18636
- if (value === null || value === void 0)
18637
- return null;
18638
- if (!isRecord8(value) || !nonBlankString(value.provider) || !nonBlankString(value.id)) {
18639
- throw new OmpRpcFaultError("protocolError", `Omp RPC returned an invalid ${context} Model`);
18640
- }
18641
- return { provider: value.provider, id: value.id };
18642
- }
18643
- function sessionStateData(response) {
18644
- const data = isRecord8(response.data) ? response.data : null;
18645
- if (!data)
18646
- throw new OmpRpcFaultError("protocolError", "Omp RPC state response has no data");
18647
- return data;
18648
- }
18649
- function parseSessionState(response) {
18650
- const data = sessionStateData(response);
18651
- const model2 = parseNativeModel(data.model, "state");
18652
- if (!nonBlankString(data.sessionId)) {
18653
- throw new OmpRpcFaultError("protocolError", "Omp RPC state has no stable Session identity");
18654
- }
18655
- const thinkingLevel = data.thinkingLevel === void 0 ? null : harnessThinkingOptionIdSchema.safeParse(data.thinkingLevel);
18656
- if (thinkingLevel !== null && !thinkingLevel.success) {
18657
- throw new OmpRpcFaultError("protocolError", "Omp RPC state has an invalid Thinking level");
18658
- }
18659
- const thinking = isRecord8(data.model) && isRecord8(data.model.thinking) ? data.model.thinking : null;
18660
- const availableThinkingLevels = thinking && Array.isArray(thinking.efforts) ? thinking.efforts.flatMap((level) => {
18661
- const parsed = harnessThinkingOptionIdSchema.safeParse(level);
18662
- return parsed.success ? [parsed.data] : [];
18663
- }) : null;
18664
- return {
18665
- sessionId: data.sessionId,
18666
- sessionFile: typeof data.sessionFile === "string" ? data.sessionFile : null,
18667
- provider: model2?.provider ?? null,
18668
- modelId: model2?.id ?? null,
18669
- thinkingLevel: thinkingLevel?.data ?? null,
18670
- contextUsage: optionalOmpStateContextUsage(data.contextUsage),
18671
- availableThinkingLevels
18672
- };
18673
- }
18674
- function parseSessionStreaming(response) {
18675
- const isStreaming = sessionStateData(response).isStreaming;
18676
- if (typeof isStreaming !== "boolean") {
18677
- throw new OmpRpcFaultError("protocolError", "Omp RPC state has no Streaming status");
18678
- }
18679
- return isStreaming;
18680
- }
18681
- function cancellationSettled(response, terminalEnd) {
18682
- const data = sessionStateData(response);
18683
- return !parseSessionStreaming(response) && (data.isSettled === true || data.isSettled === void 0 && terminalEnd) && data.isCompacting !== true && data.hasPendingAsyncWork !== true && !(typeof data.queuedMessageCount === "number" && data.queuedMessageCount > 0);
18684
- }
18685
- function parseAvailableModels(response) {
18686
- const data = isRecord8(response.data) ? response.data : null;
18687
- if (!data || !Array.isArray(data.models)) {
18688
- throw new OmpRpcFaultError("protocolError", "Omp RPC Model catalog response has no models");
18689
- }
18690
- return data.models.map((model2) => {
18691
- const parsed = parseNativeModel(model2, "catalog");
18692
- if (!parsed || !isRecord8(model2) || typeof model2.reasoning !== "boolean") {
18693
- throw new OmpRpcFaultError("protocolError", "Omp RPC catalog contains a Model without reasoning capability");
18665
+ abort() {
18666
+ const active = this.#activeTurn;
18667
+ if (!active || this.#closed || this.#failed) {
18668
+ return Promise.reject(new Error("Omp RPC Session has no cancellable Turn"));
18694
18669
  }
18695
- return { ...parsed, reasoning: model2.reasoning };
18696
- });
18697
- }
18698
- function parseSubagentMessages(response) {
18699
- const data = isRecord8(response.data) ? response.data : null;
18700
- 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)) {
18701
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript response is invalid");
18702
- }
18703
- const entries = data.entries.flatMap((value) => isRecord8(value) ? [value] : []);
18704
- const messages = data.messages.flatMap((value) => isRecord8(value) ? [value] : []);
18705
- if (entries.length !== data.entries.length || messages.length !== data.messages.length) {
18706
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent transcript contains invalid data");
18670
+ if (active.abortPromise)
18671
+ return active.abortPromise;
18672
+ active.cancellation = "requesting";
18673
+ active.cancellationTimeout = setTimeout(() => {
18674
+ this.#terminateFailedCancellation(active, new OmpRpcFaultError("protocolError", "Omp Turn cancellation did not settle within its bound"));
18675
+ }, this.#options.cancelTimeoutMs);
18676
+ const aborting = this.#send("abort", {}).then(async () => {
18677
+ if (this.#activeTurn !== active)
18678
+ return;
18679
+ active.cancellation = "accepted";
18680
+ await this.#confirmCancelledTurn(active);
18681
+ }).catch((error51) => {
18682
+ if (this.#activeTurn !== active)
18683
+ throw error51;
18684
+ const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC Abort failed: ${message2(error51)}`);
18685
+ this.#terminateFailedCancellation(active, fault);
18686
+ throw fault;
18687
+ });
18688
+ active.abortPromise = aborting;
18689
+ return aborting;
18707
18690
  }
18708
- return {
18709
- sessionFile: data.sessionFile,
18710
- fromByte: data.fromByte,
18711
- nextByte: data.nextByte,
18712
- reset: data.reset,
18713
- entries,
18714
- messages
18715
- };
18716
- }
18717
- function subagentStatus(value) {
18718
- if (value === "pending" || value === "running" || value === "completed")
18719
- return value;
18720
- if (value === "failed" || value === "aborted" || value === "interrupted")
18721
- return "failed";
18722
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent status is invalid");
18723
- }
18724
- function assistantText(value) {
18725
- if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
18726
- return null;
18727
- return value.content.filter((content) => isRecord8(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
18728
- }
18729
- function assistantMessageId(value) {
18730
- if (!isRecord8(value) || value.role !== "assistant")
18731
- return null;
18732
- return nonBlankString(value.responseId) ? value.responseId : null;
18733
- }
18734
- function extractReasoningText(content) {
18735
- if (!isRecord8(content))
18736
- return null;
18737
- const type = String(content.type ?? "");
18738
- if (type === "thinking" || type === "reasoning" || type === "thought") {
18739
- const text = content.thinking ?? content.reasoning ?? content.text ?? content.delta;
18740
- return typeof text === "string" ? text : null;
18691
+ #terminateFailedCancellation(active, fault) {
18692
+ if (this.#activeTurn !== active)
18693
+ return;
18694
+ if (active.cancellationTimeout)
18695
+ clearTimeout(active.cancellationTimeout);
18696
+ active.cancellationTimeout = null;
18697
+ this.#fail(fault);
18698
+ void this.close().catch(() => void 0);
18741
18699
  }
18742
- return null;
18743
- }
18744
- function assistantReasoning(value) {
18745
- if (!isRecord8(value) || value.role !== "assistant" || !Array.isArray(value.content))
18746
- return null;
18747
- return value.content.map(extractReasoningText).filter((text) => typeof text === "string").join("");
18748
- }
18749
- function assistantFailure(value) {
18750
- if (!isRecord8(value) || value.role !== "assistant")
18751
- return void 0;
18752
- if (value.stopReason !== "error" && value.stopReason !== "aborted")
18753
- return null;
18754
- const fallback = value.stopReason === "aborted" ? "Omp assistant message was aborted" : "Omp assistant message failed";
18755
- return new Error(nonBlankString(value.errorMessage) ? value.errorMessage : fallback);
18756
- }
18757
- function signalProcessTree(child, signal) {
18758
- if (!child.pid)
18759
- return;
18760
- if (process.platform === "win32") {
18761
- spawnSync("taskkill.exe", ["/pid", String(child.pid), "/t", "/f"], {
18762
- stdio: "ignore",
18763
- windowsHide: true
18764
- });
18765
- return;
18700
+ close() {
18701
+ if (!this.#closed) {
18702
+ this.#closed = true;
18703
+ this.#rejectAll(new Error("Omp RPC Session closed"));
18704
+ }
18705
+ this.#closePromise ??= this.#stopProcess();
18706
+ return this.#closePromise;
18766
18707
  }
18767
- try {
18768
- process.kill(-child.pid, signal);
18769
- } catch (error51) {
18770
- if (!isRecord8(error51) || error51.code !== "ESRCH")
18771
- throw error51;
18708
+ async #stopProcess() {
18709
+ const child = this.#child;
18710
+ if (!child)
18711
+ return;
18712
+ if (child.stdin.writable)
18713
+ child.stdin.end();
18714
+ if (await waitForExit(child, this.#options.closeTimeoutMs))
18715
+ return;
18716
+ signalProcessTree(child, "SIGTERM");
18717
+ if (await waitForExit(child, this.#options.closeTimeoutMs))
18718
+ return;
18719
+ signalProcessTree(child, "SIGKILL");
18720
+ if (!await waitForExit(child, this.#options.closeTimeoutMs)) {
18721
+ throw new Error("Omp RPC process tree did not exit within cleanup bounds");
18722
+ }
18772
18723
  }
18773
- }
18774
- function waitForExit(child, timeoutMs) {
18775
- if (child.exitCode !== null || child.signalCode !== null)
18776
- return Promise.resolve(true);
18777
- return Promise.race([
18778
- new Promise((resolve) => child.once("exit", () => resolve(true))),
18779
- new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
18780
- ]);
18781
- }
18782
- function ompRpcProcessCommand(options, dependencies = {}) {
18783
- if (options.sessionFile && options.forkSessionFile) {
18784
- throw new Error("Omp RPC cannot combine Session resume and Fork startup");
18724
+ #push(chunk) {
18725
+ this.#buffer = this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);
18726
+ let newline = this.#buffer.indexOf(10);
18727
+ while (newline >= 0) {
18728
+ const frame = this.#buffer.subarray(0, newline);
18729
+ this.#buffer = this.#buffer.subarray(newline + 1);
18730
+ let parsed;
18731
+ try {
18732
+ parsed = JSON.parse(textDecoder.decode(frame));
18733
+ } catch (error51) {
18734
+ this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC returned invalid JSONL: ${message2(error51)}`));
18735
+ newline = this.#buffer.indexOf(10);
18736
+ continue;
18737
+ }
18738
+ try {
18739
+ const decoded = this.#frameDecoder.push(parsed);
18740
+ if (decoded === null) {
18741
+ newline = this.#buffer.indexOf(10);
18742
+ continue;
18743
+ }
18744
+ const value = decoded;
18745
+ if (!isRecord8(value) || typeof value.type !== "string") {
18746
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid envelope");
18747
+ }
18748
+ this.#handle(value);
18749
+ } catch (error51) {
18750
+ this.#fail(error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC frame handling failed: ${message2(error51)}`));
18751
+ }
18752
+ newline = this.#buffer.indexOf(10);
18753
+ }
18785
18754
  }
18786
- if (options.model && (options.sessionFile || options.forkSessionFile)) {
18787
- throw new Error("Omp RPC cannot combine a startup Model with Session restore or Fork");
18755
+ /** Latest `available_commands_update` of the running process, if any. */
18756
+ get availableCommands() {
18757
+ return this.#availableCommands;
18788
18758
  }
18789
- const platform = dependencies.platform ?? process.platform;
18790
- const command = resolveOmpExecutable({
18791
- ...options.command ? { command: options.command } : {},
18792
- environment: options.environment
18793
- }, {
18794
- platform,
18795
- ...dependencies.homeDirectory ? { homeDirectory: dependencies.homeDirectory } : {},
18796
- ...dependencies.isExecutable ? { isExecutable: dependencies.isExecutable } : {}
18797
- });
18798
- const sessionArguments = options.forkSessionFile ? ["--fork", options.forkSessionFile] : options.sessionFile ? ["--resume", options.sessionFile] : [];
18799
- const modelArguments = options.model ? ["--provider", options.model.provider, "--model", options.model.id] : [];
18800
- const permissionArguments = options.permissionMode ? ["--approval-mode", options.permissionMode] : [];
18801
- const arguments_ = [
18802
- "--mode",
18803
- // OMP only creates ask and connects its tool UI in rpc-ui mode.
18804
- "rpc-ui",
18805
- ...permissionArguments,
18806
- ...modelArguments,
18807
- ...sessionArguments
18808
- ];
18809
- const extension = path6.win32.extname(command).toLowerCase();
18810
- if (platform !== "win32" || ![".cmd", ".bat"].includes(extension)) {
18811
- return { command, arguments: arguments_, windowsVerbatimArguments: false };
18812
- }
18813
- const quote = (value) => `"${value.replaceAll("%", "%%").replaceAll('"', '""')}"`;
18814
- const commandLine = [command, ...arguments_].map(quote).join(" ");
18815
- return {
18816
- command: options.environment?.ComSpec ?? options.environment?.COMSPEC ?? "cmd.exe",
18817
- arguments: ["/d", "/v:off", "/s", "/c", `"${commandLine}"`],
18818
- windowsVerbatimArguments: true
18819
- };
18820
- }
18821
- var nodeProcessAdapter = {
18822
- spawn(options) {
18823
- const invocation = ompRpcProcessCommand(options);
18824
- return spawn(invocation.command, invocation.arguments, {
18825
- cwd: options.cwd,
18826
- env: options.environment,
18827
- detached: process.platform !== "win32",
18828
- stdio: ["pipe", "pipe", "pipe"],
18829
- windowsHide: true,
18830
- windowsVerbatimArguments: invocation.windowsVerbatimArguments
18831
- });
18832
- }
18833
- };
18834
- var OmpRpcSession = class {
18835
- #options;
18836
- #processAdapter;
18837
- #activeTurn = null;
18838
- #buffer = Buffer.alloc(0);
18839
- #child = null;
18840
- #closed = false;
18841
- #closePromise = null;
18842
- #compactionActive = false;
18843
- #compactionTurn = null;
18844
- #compactionTimeout = null;
18845
- #failed = false;
18846
- #pending = /* @__PURE__ */ new Map();
18847
- #state = null;
18848
- #availableCommands = null;
18849
- #latestCacheHitRatePercent;
18850
- #manualCompaction = null;
18851
- #usageRequestStartedAtMs = null;
18852
- #stderrTail = "";
18853
- #frameDecoder = new OmpFrameDecoder();
18854
- #startupFaultResolve = null;
18855
- #startupFault = new Promise((resolve) => {
18856
- this.#startupFaultResolve = resolve;
18857
- });
18858
- #readyResolve = null;
18859
- #ready = new Promise((resolve) => {
18860
- this.#readyResolve = resolve;
18861
- });
18862
- constructor(options, processAdapter = nodeProcessAdapter) {
18863
- if (options.sessionFile && options.forkSessionFile) {
18864
- throw new Error("Omp RPC cannot combine Session resume and Fork startup");
18759
+ #handle(value) {
18760
+ if (this.#closed || this.#failed)
18761
+ return;
18762
+ if (value.type === "available_commands_update") {
18763
+ this.#availableCommands = parseOmpAvailableCommands(value.commands);
18764
+ return;
18865
18765
  }
18866
- if (options.model && (options.sessionFile || options.forkSessionFile)) {
18867
- throw new Error("Omp RPC cannot combine a startup Model with Session restore or Fork");
18766
+ if (value.type === "ready") {
18767
+ this.#readyResolve?.();
18768
+ this.#readyResolve = null;
18769
+ return;
18868
18770
  }
18869
- this.#options = {
18870
- commandTimeoutMs: 3e4,
18871
- compactionTimeoutMs: 3e5,
18872
- // Native Abort waits for tools, persistence and deferred work to unwind.
18873
- // Stable cancellation must outlast the Host's 20s steering wait.
18874
- cancelTimeoutMs: 3e4,
18875
- closeTimeoutMs: 2e3,
18876
- ...options
18877
- };
18878
- this.#processAdapter = processAdapter;
18879
- }
18880
- get state() {
18881
- if (!this.#state)
18882
- throw new Error("Omp RPC Session has not started");
18883
- return this.#state;
18884
- }
18885
- get stderrTail() {
18886
- return this.#stderrTail;
18887
- }
18888
- async start() {
18889
- if (this.#child || this.#closed)
18890
- throw new Error("Omp RPC Session cannot be started twice");
18891
- const child = this.#processAdapter.spawn({
18892
- cwd: this.#options.cwd,
18893
- ...this.#options.command ? { command: this.#options.command } : {},
18894
- environment: withNodeRuntimeOnPath({
18895
- ...process.env,
18896
- ...this.#options.environment,
18897
- OMP_SKIP_VERSION_CHECK: "1",
18898
- OMP_TELEMETRY: "0"
18899
- }),
18900
- ...this.#options.sessionFile ? { sessionFile: this.#options.sessionFile } : {},
18901
- ...this.#options.forkSessionFile ? { forkSessionFile: this.#options.forkSessionFile } : {},
18902
- ...this.#options.model ? { model: this.#options.model } : {},
18903
- ...this.#options.permissionMode ? { permissionMode: this.#options.permissionMode } : {}
18904
- });
18905
- this.#child = child;
18906
- child.stdout.on("data", (chunk) => this.#push(chunk));
18907
- child.stdout.on("end", () => {
18908
- if (this.#buffer.length !== 0) {
18909
- this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC stdout ended mid-frame"));
18771
+ if (value.type === "response") {
18772
+ this.#handleResponse(value);
18773
+ return;
18774
+ }
18775
+ if (value.type === "auto_compaction_start" || value.type === "compaction_start") {
18776
+ this.#compactionActive = true;
18777
+ this.#compactionTurn = this.#activeTurn;
18778
+ this.#compactionTimeout ??= setTimeout(() => {
18779
+ this.#compactionTimeout = null;
18780
+ this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC compaction timed out after ${this.#options.compactionTimeoutMs}ms`));
18781
+ }, this.#options.compactionTimeoutMs);
18782
+ const onEvent = this.#activeTurn?.onEvent ?? this.#manualCompaction?.onEvent;
18783
+ onEvent?.({ type: "compaction.started" });
18784
+ for (const pending of this.#pending.values()) {
18785
+ if (pending.command !== "prompt" && pending.command !== "compact" || !pending.timeout)
18786
+ continue;
18787
+ clearTimeout(pending.timeout);
18788
+ pending.timeout = null;
18910
18789
  }
18911
- });
18912
- child.stderr.on("data", (chunk) => {
18913
- this.#stderrTail = sanitizeDiagnosticTail(`${this.#stderrTail}${chunk.toString()}`);
18914
- });
18915
- child.once("error", (error51) => {
18916
- const kind = isRecord8(error51) && error51.code === "ENOENT" ? "notInstalled" : "unavailable";
18917
- this.#fail(new OmpRpcFaultError(kind, `Omp RPC failed to start: ${error51.message}`, this.stderrTail));
18918
- });
18919
- child.once("exit", (code, signal) => {
18920
- if (!this.#closed) {
18921
- this.#fail(new OmpRpcFaultError("processExited", `Omp RPC exited (code=${code}, signal=${signal})`, this.stderrTail));
18790
+ return;
18791
+ }
18792
+ if (value.type === "auto_compaction_end" || value.type === "compaction_end") {
18793
+ this.#compactionActive = false;
18794
+ if (this.#compactionTimeout)
18795
+ clearTimeout(this.#compactionTimeout);
18796
+ this.#compactionTimeout = null;
18797
+ const compactionTurn = this.#compactionTurn;
18798
+ this.#compactionTurn = null;
18799
+ for (const [id, pending] of this.#pending) {
18800
+ if (pending.command === "prompt" || pending.command === "compact") {
18801
+ this.#armCommandTimeout(id, pending);
18802
+ }
18922
18803
  }
18923
- });
18924
- await Promise.race([
18925
- new Promise((resolve, reject) => {
18926
- child.once("spawn", resolve);
18927
- child.once("error", reject);
18928
- }),
18929
- new Promise((_resolve, reject) => setTimeout(() => reject(new Error("Omp RPC start timed out")), this.#options.commandTimeoutMs))
18930
- ]);
18931
- let readyTimeout;
18932
- try {
18933
- await Promise.race([
18934
- this.#ready,
18935
- this.#startupFault.then((error51) => {
18936
- throw error51;
18937
- }),
18938
- new Promise((_resolve, reject) => {
18939
- readyTimeout = setTimeout(() => reject(new Error("Omp RPC ready signal timed out")), this.#options.commandTimeoutMs);
18940
- })
18941
- ]);
18942
- } finally {
18943
- clearTimeout(readyTimeout);
18804
+ const outcome = value.aborted === true ? "cancelled" : isRecord8(value.result) ? "succeeded" : "failed";
18805
+ const event = {
18806
+ type: "compaction.completed",
18807
+ outcome,
18808
+ ...nonBlankString(value.errorMessage) ? { errorMessage: value.errorMessage } : {}
18809
+ };
18810
+ compactionTurn?.onEvent(event);
18811
+ const manual = this.#manualCompaction;
18812
+ this.#manualCompaction = null;
18813
+ manual?.onEvent(event);
18814
+ manual?.resolve({
18815
+ outcome,
18816
+ ...event.errorMessage ? { errorMessage: event.errorMessage } : {}
18817
+ });
18818
+ return;
18944
18819
  }
18945
- await this.#send("negotiate_protocol", { protocolVersion: 2 }).catch(() => void 0);
18946
- if (this.#options.subscribeSubagentEvents !== false) {
18947
- await this.#send("set_subagent_subscription", { level: "events" }).catch(() => void 0);
18820
+ const active = this.#activeTurn;
18821
+ if (this.#handleSubagentFrame(active, value))
18822
+ return;
18823
+ this.#observeUsage(value);
18824
+ if (value.type === "agent_start" && active)
18825
+ active.terminalEnd = false;
18826
+ if ((value.type === "session_settled" || value.type === "agent_start") && active?.cancellation === "accepted") {
18827
+ void this.#confirmCancelledTurn(active);
18828
+ return;
18829
+ }
18830
+ if (!active) {
18831
+ if (value.type === "extension_ui_request" && this.#isBlockingInteraction(value)) {
18832
+ this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC requested blocking Extension UI outside an active Turn"));
18833
+ }
18834
+ return;
18835
+ }
18836
+ if (value.type === "command_output") {
18837
+ if (active.settlement !== "pending")
18838
+ return;
18839
+ if (typeof value.text !== "string") {
18840
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned invalid command output");
18841
+ }
18842
+ active.text += value.text;
18843
+ active.onEvent({ type: "text.delta", messageId: active.promptId, delta: value.text });
18844
+ return;
18845
+ }
18846
+ if (value.type === "prompt_result") {
18847
+ if (value.id !== active.promptId || value.agentInvoked !== false)
18848
+ return;
18849
+ if (value.status === "completed" || value.status === "aborted") {
18850
+ this.#settleLocalTurn(active, { cancelled: value.status === "aborted" });
18851
+ } else if (value.status === "error" && isRecord8(value.error) && typeof value.error.message === "string") {
18852
+ this.#settleLocalTurn(active, { cancelled: false, error: value.error.message });
18853
+ } else {
18854
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid local prompt result");
18855
+ }
18856
+ return;
18857
+ }
18858
+ if (value.type === "extension_ui_request") {
18859
+ this.#startInteraction(active, value);
18860
+ return;
18861
+ }
18862
+ if (value.type === "message_start" && assistantText(value.message) !== null) {
18863
+ this.#startAssistantMessage(active, value.message);
18864
+ return;
18865
+ }
18866
+ if (value.type === "message_update" && isRecord8(value.assistantMessageEvent)) {
18867
+ const event = value.assistantMessageEvent;
18868
+ const eventType = String(event.type ?? "");
18869
+ const isThinking = eventType === "thinking_delta" || eventType === "reasoning_delta" || eventType === "thought_delta" || eventType === "thinking" || eventType === "reasoning";
18870
+ const isText = eventType === "text_delta" || eventType === "text" || eventType === "content_block_delta";
18871
+ 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;
18872
+ if (isThinking && delta !== null && delta.length > 0) {
18873
+ const messageId = this.#ensureAssistantMessage(active, value.message);
18874
+ active.reasoningMessageOpen = true;
18875
+ active.sawStreamedMessageReasoning = true;
18876
+ active.onEvent({ type: "reasoning.delta", messageId, delta });
18877
+ } else if (isText && delta !== null && delta.length > 0) {
18878
+ const messageId = this.#ensureAssistantMessage(active, value.message);
18879
+ active.text += delta;
18880
+ active.sawStreamedMessageText = true;
18881
+ active.onEvent({ type: "text.delta", messageId, delta });
18882
+ } else if (event.type === "error") {
18883
+ this.#ensureAssistantMessage(active, value.message);
18884
+ active.failure = assistantFailure(event.error) ?? assistantFailure(value.message) ?? new Error("Omp assistant message failed");
18885
+ }
18886
+ return;
18887
+ }
18888
+ if (value.type === "message_end") {
18889
+ this.#finalizeAssistantMessage(active, value.message);
18890
+ return;
18891
+ }
18892
+ if (value.type === "turn_end") {
18893
+ this.#finalizeAssistantMessage(active, value.message);
18894
+ return;
18895
+ }
18896
+ if (value.type === "tool_execution_start") {
18897
+ this.#startTool(active, value);
18898
+ return;
18899
+ }
18900
+ if (value.type === "tool_execution_update") {
18901
+ this.#updateTool(active, value);
18902
+ return;
18903
+ }
18904
+ if (value.type === "tool_execution_end") {
18905
+ this.#completeTool(active, value);
18906
+ return;
18907
+ }
18908
+ if (value.type === "agent_end" && Array.isArray(value.messages)) {
18909
+ for (let index = value.messages.length - 1; index >= 0; index -= 1) {
18910
+ const message3 = value.messages[index];
18911
+ if (!isRecord8(message3) || message3.role !== "assistant")
18912
+ continue;
18913
+ this.#finalizeAssistantMessage(active, message3);
18914
+ break;
18915
+ }
18916
+ }
18917
+ if (value.type === "agent_end" && value.isTerminal !== false) {
18918
+ active.terminalEnd = true;
18919
+ if (active.cancellation !== "none") {
18920
+ if (active.cancellation === "accepted")
18921
+ void this.#confirmCancelledTurn(active);
18922
+ return;
18923
+ }
18924
+ if (active.settlement !== "pending")
18925
+ return;
18926
+ active.settlement = "confirming";
18927
+ void this.#confirmSettledTurn(active);
18928
+ }
18929
+ }
18930
+ /** Every finished assistant message is one model request, with or without an active Turn. */
18931
+ #observeUsage(value) {
18932
+ if (value.type === "message_start") {
18933
+ this.#usageRequestStartedAtMs = null;
18934
+ return;
18935
+ }
18936
+ if (value.type === "message_update") {
18937
+ const event = value.assistantMessageEvent;
18938
+ if (this.#usageRequestStartedAtMs === null && isRecord8(event) && typeof event.type === "string" && /^(text|thinking|reasoning|thought|toolcall)_(start|delta)$/u.test(event.type)) {
18939
+ this.#usageRequestStartedAtMs = Date.now();
18940
+ }
18941
+ return;
18948
18942
  }
18949
- try {
18950
- this.#state = parseSessionState(await this.#send("get_state", {}));
18951
- } catch (error51) {
18952
- const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("unavailable", `Omp RPC state unavailable: ${message2(error51)}`);
18953
- this.#fail(fault);
18954
- throw fault;
18943
+ if (value.type === "message_end" && isRecord8(value.message)) {
18944
+ if (value.message.role !== "assistant")
18945
+ return;
18946
+ const startedAtMs = this.#usageRequestStartedAtMs;
18947
+ this.#usageRequestStartedAtMs = null;
18948
+ this.#options.onUsage?.({
18949
+ message: value.message,
18950
+ startedAtMs,
18951
+ completedAtMs: Date.now()
18952
+ });
18955
18953
  }
18956
- return this;
18957
18954
  }
18958
- async getEntries() {
18959
- try {
18960
- if (this.state.sessionFile) {
18961
- try {
18962
- return await readOmpSessionHistory(this.state.sessionFile);
18963
- } catch (error51) {
18964
- if (!isRecord8(error51) || error51.code !== "ENOENT" || this.#options.sessionFile || this.#options.forkSessionFile)
18965
- throw error51;
18966
- const response2 = await this.#send("get_messages", {});
18967
- const data2 = isRecord8(response2.data) ? response2.data : null;
18968
- if (data2 && Array.isArray(data2.messages) && data2.messages.length === 0) {
18969
- return { entries: [], leafId: null };
18970
- }
18971
- throw error51;
18972
- }
18973
- }
18974
- const response = await this.#send("get_messages", {});
18975
- const data = isRecord8(response.data) ? response.data : null;
18976
- if (!data || !Array.isArray(data.messages)) {
18977
- throw new OmpRpcFaultError("protocolError", "Omp RPC messages response has no messages");
18955
+ #handleSubagentFrame(active, value) {
18956
+ if (value.type !== "subagent_lifecycle" && value.type !== "subagent_progress" && value.type !== "subagent_event") {
18957
+ return false;
18958
+ }
18959
+ const payload = isRecord8(value.payload) ? value.payload : null;
18960
+ const progressPayload = value.type === "subagent_progress" && payload && isRecord8(payload.progress) ? payload.progress : null;
18961
+ const nativeIdValue = progressPayload?.id ?? payload?.id;
18962
+ if (!payload || !nonBlankString(nativeIdValue)) {
18963
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent frame has no stable ID");
18964
+ }
18965
+ const nativeSubagentId = nativeIdValue;
18966
+ const callId = nativeSubagentId;
18967
+ const emit = active?.onEvent ?? this.#options.onSubagentEvent;
18968
+ if (value.type === "subagent_event") {
18969
+ emit?.({ type: "subagent.transcript.changed", callId, nativeSubagentId });
18970
+ return true;
18971
+ }
18972
+ if (value.type === "subagent_lifecycle") {
18973
+ const status2 = payload.status;
18974
+ if (status2 === "started") {
18975
+ const description = nonBlankString(payload.description) ? payload.description : nonBlankString(payload.agent) ? `${payload.agent} delegation` : "Subagent delegation";
18976
+ emit?.({
18977
+ type: "subagent.started",
18978
+ callId,
18979
+ nativeSubagentId,
18980
+ description,
18981
+ ...nonBlankString(payload.agent) ? { role: payload.agent } : {},
18982
+ ...nonBlankString(payload.task) ? { prompt: payload.task } : {},
18983
+ background: payload.detached === true,
18984
+ ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
18985
+ });
18986
+ return true;
18978
18987
  }
18979
- let branchEntryIds = null;
18980
- try {
18981
- const branchResponse = await this.#send("get_branch_messages", {});
18982
- const branchData = isRecord8(branchResponse.data) ? branchResponse.data : null;
18983
- if (branchData && Array.isArray(branchData.messages)) {
18984
- const parsed = branchData.messages.flatMap((entry) => {
18985
- if (!isRecord8(entry) || !nonBlankString(entry.entryId))
18986
- return [];
18987
- return [entry.entryId];
18988
- });
18989
- if (parsed.length === branchData.messages.length)
18990
- branchEntryIds = parsed;
18991
- }
18992
- } catch (error51) {
18993
- if (!(error51 instanceof OmpRpcUnsupportedCommandError))
18994
- throw error51;
18988
+ if (status2 !== "completed" && status2 !== "failed" && status2 !== "aborted") {
18989
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent lifecycle status is invalid");
18995
18990
  }
18996
- let userIndex = 0;
18997
- let parentId = null;
18998
- const entries = data.messages.map((nativeMessage, index) => {
18999
- const role = isRecord8(nativeMessage) ? nativeMessage.role : void 0;
19000
- const branchId = role === "user" ? branchEntryIds?.[userIndex] : void 0;
19001
- if (branchId)
19002
- userIndex += 1;
19003
- const id = branchId ?? (isRecord8(nativeMessage) && nonBlankString(nativeMessage.id) ? nativeMessage.id : `omp-message-${index}`);
19004
- const entry = { id, parentId, type: "message", message: nativeMessage };
19005
- parentId = id;
19006
- return entry;
18991
+ emit?.({
18992
+ type: "subagent.completed",
18993
+ callId,
18994
+ nativeSubagentId,
18995
+ isError: status2 !== "completed",
18996
+ ...nonBlankString(payload.resultSummary) ? { resultSummary: payload.resultSummary } : {},
18997
+ ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
19007
18998
  });
19008
- return { entries, leafId: parentId };
19009
- } catch (error51) {
19010
- if (error51 instanceof OmpRpcFaultError)
19011
- this.#fail(error51);
19012
- throw error51;
19013
- }
19014
- }
19015
- async getSubagentMessages(input) {
19016
- if ((input.subagentId === void 0 || !nonBlankString(input.subagentId)) && (input.sessionFile === void 0 || !nonBlankString(input.sessionFile))) {
19017
- throw new Error("Omp Subagent transcript requires a Subagent ID or Session file");
18999
+ return true;
19018
19000
  }
19019
- if (input.fromByte !== void 0 && (!Number.isSafeInteger(input.fromByte) || input.fromByte < 0)) {
19020
- throw new Error("Omp Subagent transcript offset is invalid");
19001
+ const progress = progressPayload;
19002
+ if (!progress || !nonBlankString(progress.id) || progress.id !== nativeSubagentId) {
19003
+ throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent progress is invalid");
19021
19004
  }
19022
- const response = await this.#send("get_subagent_messages", {
19023
- ...input.subagentId ? { subagentId: input.subagentId } : {},
19024
- ...input.sessionFile ? { sessionFile: input.sessionFile } : {},
19025
- ...input.fromByte !== void 0 ? { fromByte: input.fromByte } : {}
19005
+ const status = subagentStatus(progress.status);
19006
+ const recentOutput = Array.isArray(progress.recentOutput) ? progress.recentOutput.filter((item) => typeof item === "string") : [];
19007
+ const resultSummary = recentOutput.at(-1);
19008
+ emit?.({
19009
+ type: "subagent.updated",
19010
+ callId,
19011
+ nativeSubagentId,
19012
+ status,
19013
+ ...nonBlankString(progress.description) ? { description: progress.description } : nonBlankString(payload.task) ? { description: payload.task } : {},
19014
+ ...resultSummary !== void 0 ? { resultSummary } : {},
19015
+ ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
19026
19016
  });
19027
- return parseSubagentMessages(response);
19017
+ return true;
19028
19018
  }
19029
- async compact(customInstructions, onEvent) {
19030
- if (!this.#child || !this.#state || this.#closed || this.#failed) {
19031
- throw new Error("Omp RPC Session is unavailable");
19019
+ #isBlockingInteraction(value) {
19020
+ return ["select", "confirm", "input", "editor"].includes(String(value.method));
19021
+ }
19022
+ #startInteraction(active, value) {
19023
+ if (!this.#isBlockingInteraction(value))
19024
+ return;
19025
+ const requestId2 = value.id;
19026
+ const method = value.method;
19027
+ const title = value.title;
19028
+ const timeoutMs = value.timeout;
19029
+ if (typeof requestId2 !== "string" || requestId2.length === 0 || typeof method !== "string" || typeof title !== "string" || title.length === 0 || timeoutMs !== void 0 && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs < 0) || active.interactions.has(requestId2)) {
19030
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Interaction request");
19032
19031
  }
19033
- if (this.#activeTurn || this.#manualCompaction || this.#compactionActive) {
19034
- throw new Error("Omp RPC Session already has an active operation");
19032
+ let request;
19033
+ if (method === "select") {
19034
+ if (!Array.isArray(value.options) || value.options.length === 0 || !value.options.every((option) => typeof option === "string" && option.length > 0)) {
19035
+ throw new OmpRpcFaultError("protocolError", "Omp RPC select request has invalid options");
19036
+ }
19037
+ let optionDetails;
19038
+ if (value.optionDetails !== void 0) {
19039
+ if (!Array.isArray(value.optionDetails) || value.optionDetails.length !== value.options.length || !value.optionDetails.every((detail) => typeof detail === "object" && detail !== null && !Array.isArray(detail) && (detail.description === void 0 || typeof detail.description === "string"))) {
19040
+ throw new OmpRpcFaultError("protocolError", "Omp RPC select option details are invalid");
19041
+ }
19042
+ optionDetails = value.optionDetails.map(({ description }) => description === void 0 ? {} : { description });
19043
+ }
19044
+ request = {
19045
+ requestId: requestId2,
19046
+ method,
19047
+ title,
19048
+ options: [...value.options],
19049
+ ...optionDetails ? { optionDetails } : {},
19050
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19051
+ };
19052
+ } else if (method === "confirm") {
19053
+ if (typeof value.message !== "string") {
19054
+ throw new OmpRpcFaultError("protocolError", "Omp RPC confirm request has no message");
19055
+ }
19056
+ request = {
19057
+ requestId: requestId2,
19058
+ method,
19059
+ title,
19060
+ message: value.message,
19061
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19062
+ };
19063
+ } else if (method === "input") {
19064
+ if (value.placeholder !== void 0 && typeof value.placeholder !== "string") {
19065
+ throw new OmpRpcFaultError("protocolError", "Omp RPC input placeholder is invalid");
19066
+ }
19067
+ request = {
19068
+ requestId: requestId2,
19069
+ method,
19070
+ title,
19071
+ ...typeof value.placeholder === "string" ? { placeholder: value.placeholder } : {},
19072
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19073
+ };
19074
+ } else {
19075
+ if (value.prefill !== void 0 && typeof value.prefill !== "string") {
19076
+ throw new OmpRpcFaultError("protocolError", "Omp RPC editor prefill is invalid");
19077
+ }
19078
+ request = {
19079
+ requestId: requestId2,
19080
+ method: "editor",
19081
+ title,
19082
+ ...typeof value.prefill === "string" ? { prefill: value.prefill } : {},
19083
+ ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19084
+ };
19035
19085
  }
19036
- const result = new Promise((resolve, reject) => {
19037
- this.#manualCompaction = { onEvent, resolve, reject };
19038
- });
19039
- try {
19040
- await this.#send("compact", customInstructions ? { customInstructions } : {});
19041
- } catch (error51) {
19042
- const pending = this.#manualCompaction;
19043
- this.#manualCompaction = null;
19044
- pending?.reject(error51 instanceof Error ? error51 : new Error(message2(error51)));
19086
+ const pending = { request, timeout: null };
19087
+ if (request.timeoutMs !== void 0) {
19088
+ pending.timeout = setTimeout(() => {
19089
+ if (this.#activeTurn !== active || !active.interactions.has(requestId2))
19090
+ return;
19091
+ void this.#resolveInteraction({ requestId: requestId2, cancelled: true }, "expired").catch((error51) => {
19092
+ if (this.#closed || this.#failed)
19093
+ return;
19094
+ this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC Interaction timeout handling failed: ${message2(error51)}`));
19095
+ });
19096
+ }, request.timeoutMs);
19045
19097
  }
19046
- return result;
19098
+ active.interactions.set(requestId2, pending);
19099
+ active.onEvent({ type: "interaction.requested", request });
19047
19100
  }
19048
- async getSessionUsage() {
19049
- try {
19050
- const usage = parseOmpSessionUsage(await this.#send("get_session_stats", {}));
19051
- await this.#ensureLatestCacheHitRate();
19052
- return this.#withLatestCacheHitRate(usage);
19053
- } catch (error51) {
19054
- if (!(error51 instanceof OmpRpcUnsupportedCommandError))
19055
- throw error51;
19056
- const response = await this.#send("get_state", {});
19057
- const observedState = parseSessionState(response);
19058
- if (!this.#state || observedState.sessionId !== this.#state.sessionId || observedState.provider !== this.#state.provider || observedState.modelId !== this.#state.modelId) {
19059
- throw new Error("Omp RPC Usage fallback does not match the confirmed Session state");
19060
- }
19061
- await this.#ensureLatestCacheHitRate();
19062
- const usage = parseOmpStateContextUsage(response);
19063
- return usage ? this.#withLatestCacheHitRate(usage) : null;
19101
+ #handleResponse(value) {
19102
+ const id = value.id;
19103
+ if (typeof id !== "string") {
19104
+ this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC response has no id"));
19105
+ return;
19064
19106
  }
19065
- }
19066
- async #ensureLatestCacheHitRate() {
19067
- if (this.#latestCacheHitRatePercent !== void 0)
19107
+ const pending = this.#pending.get(id);
19108
+ const active = this.#activeTurn;
19109
+ if (!pending) {
19110
+ if (active?.promptId === id && value.command === "prompt" && value.success === false) {
19111
+ active.failure = new Error(typeof value.error === "string" ? value.error : "Omp RPC command failed");
19112
+ return;
19113
+ }
19114
+ this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC response id is not pending"));
19068
19115
  return;
19069
- try {
19070
- this.#latestCacheHitRatePercent = latestOmpCacheHitRatePercent(await this.getEntries());
19071
- } catch {
19072
- this.#latestCacheHitRatePercent = null;
19073
19116
  }
19074
- }
19075
- #withLatestCacheHitRate(usage) {
19076
- return this.#latestCacheHitRatePercent === null || this.#latestCacheHitRatePercent === void 0 ? usage : parseHostUsage({ ...usage, cacheHitRatePercent: this.#latestCacheHitRatePercent });
19077
- }
19078
- async fork(entryId) {
19079
- if (!nonBlankString(entryId))
19080
- throw new Error("Omp RPC Fork requires an Entry identity");
19081
- try {
19082
- const response = await this.#send("branch", { entryId });
19083
- const data = isRecord8(response.data) ? response.data : null;
19084
- if (!data || data.cancelled === true) {
19085
- throw new OmpRpcFaultError("unavailable", "Omp RPC Fork was cancelled");
19117
+ if (pending.timeout)
19118
+ clearTimeout(pending.timeout);
19119
+ this.#pending.delete(id);
19120
+ if (value.success === true) {
19121
+ if (pending.command === "prompt" && active?.promptId === id && isRecord8(value.data) && value.data.agentInvoked === false) {
19122
+ this.#settleLocalTurn(active, { cancelled: false });
19123
+ }
19124
+ pending.resolve(value);
19125
+ } else {
19126
+ const error51 = typeof value.error === "string" ? value.error : "Omp RPC command failed";
19127
+ if (value.command === pending.command && error51 === `Unknown command: ${pending.command}`) {
19128
+ pending.reject(new OmpRpcUnsupportedCommandError(pending.command));
19129
+ } else {
19130
+ pending.reject(new Error(error51));
19086
19131
  }
19087
- this.#state = parseSessionState(await this.#send("get_state", {}));
19088
- return this.#state;
19089
- } catch (error51) {
19090
- if (error51 instanceof OmpRpcFaultError)
19091
- this.#fail(error51);
19092
- throw error51;
19093
19132
  }
19094
19133
  }
19095
- verifySessionCwd(expectedCwd) {
19096
- return verifyOmpSessionCwd({
19097
- sessionFile: this.state.sessionFile,
19098
- sessionId: this.state.sessionId,
19099
- expectedCwd
19134
+ #startTool(active, value) {
19135
+ const callId = value.toolCallId;
19136
+ const toolName = value.toolName;
19137
+ const argumentsResult = jsonValueSchema.safeParse(value.args);
19138
+ if (typeof callId !== "string" || callId.length === 0 || typeof toolName !== "string" || toolName.length === 0 || !argumentsResult.success || active.tools.has(callId)) {
19139
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool start");
19140
+ }
19141
+ active.sawTool = true;
19142
+ active.tools.set(callId, toolName);
19143
+ active.onEvent({
19144
+ type: "tool.started",
19145
+ callId,
19146
+ toolName,
19147
+ arguments: argumentsResult.data
19100
19148
  });
19101
19149
  }
19102
- async getAvailableModels() {
19103
- try {
19104
- return parseAvailableModels(await this.#send("get_available_models", {}));
19105
- } catch (error51) {
19106
- if (error51 instanceof OmpRpcFaultError)
19107
- this.#fail(error51);
19108
- throw error51;
19150
+ #updateTool(active, value) {
19151
+ const callId = value.toolCallId;
19152
+ if (typeof callId !== "string" || callId.length === 0) {
19153
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool update");
19109
19154
  }
19110
- }
19111
- async getAvailableThinkingLevels() {
19112
- return this.state.availableThinkingLevels;
19113
- }
19114
- async selectModel(model2) {
19115
- await this.#send("set_model", { provider: model2.provider, modelId: model2.id });
19116
- return this.#refreshState("Model");
19117
- }
19118
- async selectThinkingOption(thinkingOptionId) {
19119
- const level = harnessThinkingOptionIdSchema.parse(thinkingOptionId);
19120
- await this.#send("set_thinking_level", { level });
19121
- return this.#refreshState("Thinking");
19122
- }
19123
- async #refreshState(operation) {
19124
- try {
19125
- this.#state = parseSessionState(await this.#send("get_state", {}));
19126
- return this.#state;
19127
- } catch (error51) {
19128
- const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC ${operation} state could not be confirmed: ${message2(error51)}`);
19129
- this.#fail(fault);
19130
- throw fault;
19155
+ if (!active.tools.has(callId))
19156
+ return;
19157
+ const outputResult = jsonValueSchema.safeParse(value.partialResult ?? null);
19158
+ if (!outputResult.success) {
19159
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool update");
19131
19160
  }
19161
+ active.onEvent({ type: "tool.updated", callId, output: outputResult.data });
19132
19162
  }
19133
- async runTurn(text, onEvent) {
19134
- if (!this.#child || !this.#state || this.#closed || this.#failed) {
19135
- throw new Error("Omp RPC Session is unavailable");
19163
+ #completeTool(active, value) {
19164
+ const callId = value.toolCallId;
19165
+ const toolName = value.toolName;
19166
+ if (typeof callId !== "string" || callId.length === 0) {
19167
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool end");
19136
19168
  }
19137
- if (this.#activeTurn)
19138
- throw new Error("Omp RPC Session already has an active Turn");
19139
- if (text.length === 0)
19140
- throw new Error("Omp text Turn must not be empty");
19141
- const promptId = `codexhost-${randomUUID()}`;
19142
- const settled = new Promise((resolve, reject) => {
19143
- this.#activeTurn = {
19144
- promptId,
19145
- localResult: null,
19146
- text: "",
19147
- assistantMessageId: null,
19148
- sawStreamedMessageText: false,
19149
- sawStreamedMessageReasoning: false,
19150
- lastFinalizedMessageText: null,
19151
- lastFinalizedMessageReasoning: null,
19152
- reasoningMessageOpen: false,
19153
- onEvent,
19154
- resolve,
19155
- reject,
19156
- failure: null,
19157
- sawTool: false,
19158
- tools: /* @__PURE__ */ new Map(),
19159
- interactions: /* @__PURE__ */ new Map(),
19160
- settlement: "pending",
19161
- terminalEnd: false,
19162
- cancellation: "none",
19163
- cancellationTimeout: null,
19164
- cancellationRecheck: null,
19165
- cancellationChecking: false,
19166
- cancellationReloaded: false,
19167
- abortPromise: null
19168
- };
19169
- });
19170
- try {
19171
- await this.#send("prompt", { message: text }, promptId);
19172
- } catch (error51) {
19173
- this.#rejectActiveTurn(error51 instanceof Error ? error51 : new Error(message2(error51)));
19169
+ const expectedName = active.tools.get(callId);
19170
+ if (expectedName === void 0)
19171
+ return;
19172
+ const result = jsonValueSchema.safeParse(value.result);
19173
+ if (typeof toolName !== "string" || expectedName !== toolName || value.isError !== void 0 && typeof value.isError !== "boolean" || !result.success) {
19174
+ throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool end");
19174
19175
  }
19175
- return settled;
19176
- }
19177
- respondToInteraction(response) {
19178
- return this.#resolveInteraction(response, "cancelled" in response ? "cancelled" : "responded");
19176
+ active.tools.delete(callId);
19177
+ active.onEvent({
19178
+ type: "tool.completed",
19179
+ callId,
19180
+ toolName,
19181
+ result: result.data,
19182
+ isError: value.isError === true
19183
+ });
19179
19184
  }
19180
- async #resolveInteraction(response, reason) {
19181
- const active = this.#activeTurn;
19182
- const pending = active?.interactions.get(response.requestId);
19183
- if (!active || !pending || this.#closed || this.#failed) {
19184
- throw new Error("Omp RPC interaction is not pending");
19185
- }
19186
- if ("value" in response && !["select", "input", "editor"].includes(pending.request.method)) {
19187
- throw new Error("Omp RPC interaction response type does not match the request");
19188
- }
19189
- if ("confirmed" in response && pending.request.method !== "confirm") {
19190
- throw new Error("Omp RPC confirmation does not match the request");
19191
- }
19192
- const frame = "cancelled" in response ? {
19193
- type: "extension_ui_response",
19194
- id: response.requestId,
19195
- cancelled: true,
19196
- ...reason === "expired" ? { timedOut: true } : {}
19197
- } : "confirmed" in response ? { type: "extension_ui_response", id: response.requestId, confirmed: response.confirmed } : { type: "extension_ui_response", id: response.requestId, value: response.value };
19198
- if (!active.interactions.delete(response.requestId)) {
19199
- throw new Error("Omp RPC interaction is not pending");
19200
- }
19201
- if (pending.timeout)
19202
- clearTimeout(pending.timeout);
19203
- active.onEvent({ type: "interaction.closed", requestId: response.requestId, reason });
19185
+ async #confirmCancelledTurn(active) {
19186
+ if (this.#activeTurn !== active || active.cancellation !== "accepted" || active.settlement === "confirming" || active.cancellationChecking)
19187
+ return;
19188
+ active.cancellationChecking = true;
19189
+ if (active.cancellationRecheck)
19190
+ clearTimeout(active.cancellationRecheck);
19191
+ active.cancellationRecheck = null;
19204
19192
  try {
19205
- await this.#write(frame);
19193
+ let response = await this.#send("get_state", {});
19194
+ if (this.#activeTurn !== active)
19195
+ return;
19196
+ if (parseSessionStreaming(response)) {
19197
+ await this.#send("abort", {});
19198
+ if (this.#activeTurn !== active)
19199
+ return;
19200
+ response = await this.#send("get_state", {});
19201
+ }
19202
+ let state = parseSessionState(response);
19203
+ if (this.#activeTurn !== active || active.cancellation !== "accepted")
19204
+ return;
19205
+ const data = sessionStateData(response);
19206
+ if (!active.cancellationReloaded && state.sessionFile && state.sessionId === this.state.sessionId && state.sessionFile === this.state.sessionFile && data.isStreaming === false && data.isCompacting === false && data.queuedMessageCount === 0 && data.hasPendingAsyncWork === true && active.tools.size === 0) {
19207
+ await this.verifySessionCwd(this.#options.cwd);
19208
+ if (this.#activeTurn !== active)
19209
+ return;
19210
+ active.cancellationReloaded = true;
19211
+ const reset = await this.#send("new_session", {});
19212
+ if (this.#activeTurn !== active)
19213
+ return;
19214
+ if (!isRecord8(reset.data) || reset.data.cancelled !== false) {
19215
+ throw new OmpRpcFaultError("unavailable", "Omp cancelled Session reset was refused");
19216
+ }
19217
+ const reloaded = await this.#send("switch_session", { sessionPath: state.sessionFile });
19218
+ if (this.#activeTurn !== active)
19219
+ return;
19220
+ if (!isRecord8(reloaded.data) || reloaded.data.cancelled !== false) {
19221
+ throw new OmpRpcFaultError("unavailable", "Omp cancelled Session reload was refused");
19222
+ }
19223
+ const previous = state;
19224
+ response = await this.#send("get_state", {});
19225
+ state = parseSessionState(response);
19226
+ if (state.sessionId !== previous.sessionId || state.sessionFile !== previous.sessionFile) {
19227
+ throw new OmpRpcFaultError("protocolError", "Omp cancelled Session reload changed its identity");
19228
+ }
19229
+ if (state.provider !== previous.provider || state.modelId !== previous.modelId) {
19230
+ if (!previous.provider || !previous.modelId) {
19231
+ throw new OmpRpcFaultError("protocolError", "Omp cancelled Session has no restorable Model");
19232
+ }
19233
+ await this.#send("set_model", { provider: previous.provider, modelId: previous.modelId });
19234
+ }
19235
+ if (previous.thinkingLevel && state.thinkingLevel !== previous.thinkingLevel) {
19236
+ await this.#send("set_thinking_level", { level: previous.thinkingLevel });
19237
+ }
19238
+ response = await this.#send("get_state", {});
19239
+ state = parseSessionState(response);
19240
+ if (state.sessionId !== previous.sessionId || state.sessionFile !== previous.sessionFile || state.provider !== previous.provider || state.modelId !== previous.modelId || previous.thinkingLevel !== null && state.thinkingLevel !== previous.thinkingLevel) {
19241
+ throw new OmpRpcFaultError("protocolError", "Omp cancelled Session reload changed its identity or configuration");
19242
+ }
19243
+ }
19244
+ if (!cancellationSettled(response, active.terminalEnd)) {
19245
+ active.settlement = "pending";
19246
+ active.cancellationRecheck = setTimeout(() => {
19247
+ active.cancellationRecheck = null;
19248
+ void this.#confirmCancelledTurn(active);
19249
+ }, 250);
19250
+ return;
19251
+ }
19252
+ this.#state = state;
19253
+ active.settlement = "confirmed";
19254
+ this.#finishSettledTurn(active);
19206
19255
  } catch (error51) {
19207
- const fault = new OmpRpcFaultError("protocolError", `Omp RPC interaction response failed: ${message2(error51)}`);
19208
- this.#fail(fault);
19209
- throw fault;
19256
+ if (this.#activeTurn !== active)
19257
+ return;
19258
+ const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC cancelled Turn state could not be confirmed: ${message2(error51)}`);
19259
+ this.#terminateFailedCancellation(active, fault);
19260
+ } finally {
19261
+ active.cancellationChecking = false;
19210
19262
  }
19211
19263
  }
19212
- abort() {
19213
- const active = this.#activeTurn;
19214
- if (!active || this.#closed || this.#failed) {
19215
- return Promise.reject(new Error("Omp RPC Session has no cancellable Turn"));
19216
- }
19217
- if (active.abortPromise)
19218
- return active.abortPromise;
19219
- active.cancellation = "requesting";
19220
- active.cancellationTimeout = setTimeout(() => {
19221
- this.#terminateFailedCancellation(active, new OmpRpcFaultError("protocolError", "Omp Turn cancellation did not settle within its bound"));
19222
- }, this.#options.cancelTimeoutMs);
19223
- const aborting = this.#send("abort", {}).then(async () => {
19264
+ #settleLocalTurn(active, result) {
19265
+ if (this.#activeTurn !== active || active.settlement !== "pending")
19266
+ return;
19267
+ active.localResult = result;
19268
+ active.settlement = "confirming";
19269
+ void this.#confirmSettledTurn(active);
19270
+ }
19271
+ async #confirmSettledTurn(active) {
19272
+ try {
19273
+ const response = await this.#send("get_state", {});
19274
+ const state = parseSessionState(response);
19224
19275
  if (this.#activeTurn !== active)
19225
19276
  return;
19226
- active.cancellation = "accepted";
19227
- await this.#confirmCancelledTurn(active);
19228
- }).catch((error51) => {
19277
+ if (active.cancellation !== "none") {
19278
+ active.settlement = "pending";
19279
+ if (active.cancellation === "accepted")
19280
+ void this.#confirmCancelledTurn(active);
19281
+ return;
19282
+ }
19283
+ if (parseSessionStreaming(response)) {
19284
+ throw new OmpRpcFaultError("protocolError", "Omp RPC completed Turn state is still Streaming");
19285
+ }
19286
+ this.#state = state;
19287
+ active.settlement = "confirmed";
19288
+ this.#finishSettledTurn(active);
19289
+ } catch (error51) {
19229
19290
  if (this.#activeTurn !== active)
19230
- throw error51;
19231
- const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC Abort failed: ${message2(error51)}`);
19232
- this.#terminateFailedCancellation(active, fault);
19233
- throw fault;
19234
- });
19235
- active.abortPromise = aborting;
19236
- return aborting;
19291
+ return;
19292
+ const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC stable Turn state could not be confirmed: ${message2(error51)}`);
19293
+ this.#fail(fault);
19294
+ }
19237
19295
  }
19238
- #terminateFailedCancellation(active, fault) {
19239
- if (this.#activeTurn !== active)
19296
+ #finishSettledTurn(active) {
19297
+ if (this.#activeTurn !== active || active.settlement !== "confirmed" || active.cancellation === "requesting") {
19240
19298
  return;
19299
+ }
19241
19300
  if (active.cancellationTimeout)
19242
19301
  clearTimeout(active.cancellationTimeout);
19243
19302
  active.cancellationTimeout = null;
19244
- this.#fail(fault);
19245
- void this.close().catch(() => void 0);
19246
- }
19247
- close() {
19248
- if (!this.#closed) {
19249
- this.#closed = true;
19250
- this.#rejectAll(new Error("Omp RPC Session closed"));
19251
- }
19252
- this.#closePromise ??= this.#stopProcess();
19253
- return this.#closePromise;
19254
- }
19255
- async #stopProcess() {
19256
- const child = this.#child;
19257
- if (!child)
19258
- return;
19259
- if (child.stdin.writable)
19260
- child.stdin.end();
19261
- if (await waitForExit(child, this.#options.closeTimeoutMs))
19262
- return;
19263
- signalProcessTree(child, "SIGTERM");
19264
- if (await waitForExit(child, this.#options.closeTimeoutMs))
19303
+ if (active.cancellationRecheck)
19304
+ clearTimeout(active.cancellationRecheck);
19305
+ active.cancellationRecheck = null;
19306
+ this.#closeInteractions(active, active.cancellation === "accepted" ? "cancelled" : "superseded");
19307
+ if (active.tools.size > 0) {
19308
+ this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC settled with active Tool executions"));
19265
19309
  return;
19266
- signalProcessTree(child, "SIGKILL");
19267
- if (!await waitForExit(child, this.#options.closeTimeoutMs)) {
19268
- throw new Error("Omp RPC process tree did not exit within cleanup bounds");
19310
+ }
19311
+ this.#activeTurn = null;
19312
+ if (active.localResult) {
19313
+ active.resolve({
19314
+ text: active.text,
19315
+ agentInvoked: false,
19316
+ ...active.localResult,
19317
+ cancelled: active.localResult.cancelled || active.cancellation === "accepted"
19318
+ });
19319
+ } else if (active.cancellation === "accepted") {
19320
+ active.resolve({ text: active.text, cancelled: true, agentInvoked: true });
19321
+ } else if (active.failure) {
19322
+ active.reject(active.failure);
19323
+ } else if (active.text.trim().length === 0 && !active.sawTool) {
19324
+ active.reject(new Error("Omp RPC settled without displayable output"));
19325
+ } else {
19326
+ active.resolve({ text: active.text, cancelled: false, agentInvoked: true });
19269
19327
  }
19270
19328
  }
19271
- #push(chunk) {
19272
- this.#buffer = this.#buffer.length === 0 ? chunk : Buffer.concat([this.#buffer, chunk]);
19273
- let newline = this.#buffer.indexOf(10);
19274
- while (newline >= 0) {
19275
- const frame = this.#buffer.subarray(0, newline);
19276
- this.#buffer = this.#buffer.subarray(newline + 1);
19277
- let parsed;
19278
- try {
19279
- parsed = JSON.parse(textDecoder.decode(frame));
19280
- } catch (error51) {
19281
- this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC returned invalid JSONL: ${message2(error51)}`));
19282
- newline = this.#buffer.indexOf(10);
19283
- continue;
19284
- }
19285
- try {
19286
- const decoded = this.#frameDecoder.push(parsed);
19287
- if (decoded === null) {
19288
- newline = this.#buffer.indexOf(10);
19289
- continue;
19290
- }
19291
- const value = decoded;
19292
- if (!isRecord8(value) || typeof value.type !== "string") {
19293
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid envelope");
19294
- }
19295
- this.#handle(value);
19296
- } catch (error51) {
19297
- this.#fail(error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC frame handling failed: ${message2(error51)}`));
19298
- }
19299
- newline = this.#buffer.indexOf(10);
19329
+ #startAssistantMessage(active, value) {
19330
+ if (active.assistantMessageId !== null && active.reasoningMessageOpen) {
19331
+ active.onEvent({ type: "reasoning.completed", messageId: active.assistantMessageId });
19300
19332
  }
19333
+ const messageId = assistantMessageId(value) ?? randomUUID();
19334
+ active.assistantMessageId = messageId;
19335
+ active.sawStreamedMessageText = false;
19336
+ active.sawStreamedMessageReasoning = false;
19337
+ active.reasoningMessageOpen = false;
19338
+ return messageId;
19301
19339
  }
19302
- /** Latest `available_commands_update` of the running process, if any. */
19303
- get availableCommands() {
19304
- return this.#availableCommands;
19340
+ #ensureAssistantMessage(active, value) {
19341
+ return active.assistantMessageId ?? this.#startAssistantMessage(active, value);
19305
19342
  }
19306
- #handle(value) {
19307
- if (this.#closed || this.#failed)
19343
+ #finalizeAssistantMessage(active, value) {
19344
+ const finalText = assistantText(value);
19345
+ const finalReasoning = assistantReasoning(value);
19346
+ const failure = assistantFailure(value);
19347
+ const cacheHitRatePercent = optionalOmpCacheHitRatePercent(value);
19348
+ if (finalText === null || finalReasoning === null || failure === void 0)
19308
19349
  return;
19309
- if (value.type === "available_commands_update") {
19310
- this.#availableCommands = parseOmpAvailableCommands(value.commands);
19350
+ if (active.assistantMessageId === null && finalText === active.lastFinalizedMessageText && finalReasoning === active.lastFinalizedMessageReasoning && !active.sawStreamedMessageText && !active.sawStreamedMessageReasoning) {
19311
19351
  return;
19312
19352
  }
19313
- if (value.type === "ready") {
19314
- this.#readyResolve?.();
19315
- this.#readyResolve = null;
19316
- return;
19353
+ active.failure = failure;
19354
+ this.#latestCacheHitRatePercent = cacheHitRatePercent;
19355
+ const messageId = this.#ensureAssistantMessage(active, value);
19356
+ if (!active.sawStreamedMessageReasoning && finalReasoning.length > 0) {
19357
+ active.reasoningMessageOpen = true;
19358
+ active.onEvent({ type: "reasoning.delta", messageId, delta: finalReasoning });
19317
19359
  }
19318
- if (value.type === "response") {
19319
- this.#handleResponse(value);
19320
- return;
19360
+ if (active.reasoningMessageOpen && active.failure === null) {
19361
+ active.onEvent({ type: "reasoning.completed", messageId });
19321
19362
  }
19322
- if (value.type === "auto_compaction_start" || value.type === "compaction_start") {
19323
- this.#compactionActive = true;
19324
- this.#compactionTurn = this.#activeTurn;
19325
- this.#compactionTimeout ??= setTimeout(() => {
19326
- this.#compactionTimeout = null;
19327
- this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC compaction timed out after ${this.#options.compactionTimeoutMs}ms`));
19328
- }, this.#options.compactionTimeoutMs);
19329
- const onEvent = this.#activeTurn?.onEvent ?? this.#manualCompaction?.onEvent;
19330
- onEvent?.({ type: "compaction.started" });
19331
- for (const pending of this.#pending.values()) {
19332
- if (pending.command !== "prompt" && pending.command !== "compact" || !pending.timeout)
19333
- continue;
19334
- clearTimeout(pending.timeout);
19335
- pending.timeout = null;
19336
- }
19337
- return;
19363
+ if (!active.sawStreamedMessageText && finalText.length > 0) {
19364
+ active.text += finalText;
19365
+ active.onEvent({ type: "text.delta", messageId, delta: finalText });
19338
19366
  }
19339
- if (value.type === "auto_compaction_end" || value.type === "compaction_end") {
19340
- this.#compactionActive = false;
19341
- if (this.#compactionTimeout)
19342
- clearTimeout(this.#compactionTimeout);
19343
- this.#compactionTimeout = null;
19344
- const compactionTurn = this.#compactionTurn;
19345
- this.#compactionTurn = null;
19346
- for (const [id, pending] of this.#pending) {
19347
- if (pending.command === "prompt" || pending.command === "compact") {
19348
- this.#armCommandTimeout(id, pending);
19349
- }
19350
- }
19351
- const outcome = value.aborted === true ? "cancelled" : isRecord8(value.result) ? "succeeded" : "failed";
19352
- const event = {
19353
- type: "compaction.completed",
19354
- outcome,
19355
- ...nonBlankString(value.errorMessage) ? { errorMessage: value.errorMessage } : {}
19356
- };
19357
- compactionTurn?.onEvent(event);
19358
- const manual = this.#manualCompaction;
19359
- this.#manualCompaction = null;
19360
- manual?.onEvent(event);
19361
- manual?.resolve({
19362
- outcome,
19363
- ...event.errorMessage ? { errorMessage: event.errorMessage } : {}
19364
- });
19365
- return;
19367
+ active.assistantMessageId = null;
19368
+ active.sawStreamedMessageText = false;
19369
+ active.sawStreamedMessageReasoning = false;
19370
+ active.reasoningMessageOpen = false;
19371
+ active.lastFinalizedMessageText = finalText;
19372
+ active.lastFinalizedMessageReasoning = finalReasoning;
19373
+ active.onEvent({ type: "message.completed", messageId });
19374
+ }
19375
+ #write(value) {
19376
+ const child = this.#child;
19377
+ if (!child?.stdin.writable || this.#closed || this.#failed) {
19378
+ return Promise.reject(new Error("Omp RPC stdin is unavailable"));
19366
19379
  }
19367
- const active = this.#activeTurn;
19368
- if (this.#handleSubagentFrame(active, value))
19369
- return;
19370
- this.#observeUsage(value);
19371
- if (value.type === "agent_start" && active)
19372
- active.terminalEnd = false;
19373
- if ((value.type === "session_settled" || value.type === "agent_start") && active?.cancellation === "accepted") {
19374
- void this.#confirmCancelledTurn(active);
19375
- return;
19380
+ return new Promise((resolve, reject) => {
19381
+ const frame = Buffer.from(`${JSON.stringify(value)}
19382
+ `, "utf8");
19383
+ child.stdin.write(frame, (error51) => error51 ? reject(error51) : resolve());
19384
+ });
19385
+ }
19386
+ #send(type, payload, id = `codexhost-${randomUUID()}`) {
19387
+ const child = this.#child;
19388
+ if (!child?.stdin.writable || this.#closed || this.#failed) {
19389
+ return Promise.reject(new Error("Omp RPC stdin is unavailable"));
19376
19390
  }
19377
- if (!active) {
19378
- if (value.type === "extension_ui_request" && this.#isBlockingInteraction(value)) {
19379
- this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC requested blocking Extension UI outside an active Turn"));
19380
- }
19391
+ return new Promise((resolve, reject) => {
19392
+ const pending = {
19393
+ command: type,
19394
+ resolve,
19395
+ reject,
19396
+ timeout: null
19397
+ };
19398
+ this.#pending.set(id, pending);
19399
+ this.#armCommandTimeout(id, pending);
19400
+ const frame = Buffer.from(`${JSON.stringify({ id, type, ...payload })}
19401
+ `, "utf8");
19402
+ child.stdin.write(frame, (error51) => {
19403
+ if (error51) {
19404
+ if (pending.timeout)
19405
+ clearTimeout(pending.timeout);
19406
+ this.#pending.delete(id);
19407
+ reject(error51);
19408
+ }
19409
+ });
19410
+ });
19411
+ }
19412
+ #armCommandTimeout(id, pending) {
19413
+ if (pending.timeout || this.#pending.get(id) !== pending || (pending.command === "prompt" || pending.command === "compact") && this.#compactionActive) {
19381
19414
  return;
19382
19415
  }
19383
- if (value.type === "command_output") {
19384
- if (active.settlement !== "pending")
19416
+ pending.timeout = setTimeout(() => {
19417
+ if (this.#pending.get(id) !== pending)
19385
19418
  return;
19386
- if (typeof value.text !== "string") {
19387
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned invalid command output");
19388
- }
19389
- active.text += value.text;
19390
- active.onEvent({ type: "text.delta", messageId: active.promptId, delta: value.text });
19391
- return;
19392
- }
19393
- if (value.type === "prompt_result") {
19394
- if (value.id !== active.promptId || value.agentInvoked !== false)
19419
+ pending.timeout = null;
19420
+ const error51 = new Error(`Omp RPC '${pending.command}' command timed out`);
19421
+ if (pending.command !== "prompt") {
19422
+ this.#pending.delete(id);
19423
+ pending.reject(error51);
19395
19424
  return;
19396
- if (value.status === "completed" || value.status === "aborted") {
19397
- this.#settleLocalTurn(active, { cancelled: value.status === "aborted" });
19398
- } else if (value.status === "error" && isRecord8(value.error) && typeof value.error.message === "string") {
19399
- this.#settleLocalTurn(active, { cancelled: false, error: value.error.message });
19400
- } else {
19401
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid local prompt result");
19402
19425
  }
19426
+ const fault = new OmpRpcFaultError("protocolError", error51.message);
19427
+ void this.#terminateTimedOutPrompt(fault);
19428
+ }, this.#options.commandTimeoutMs);
19429
+ }
19430
+ async #terminateTimedOutPrompt(fault) {
19431
+ if (this.#closed || this.#failed)
19403
19432
  return;
19433
+ this.#failed = true;
19434
+ this.#closed = true;
19435
+ for (const pending of this.#pending.values()) {
19436
+ if (pending.timeout)
19437
+ clearTimeout(pending.timeout);
19438
+ pending.timeout = null;
19404
19439
  }
19405
- if (value.type === "extension_ui_request") {
19406
- this.#startInteraction(active, value);
19407
- return;
19408
- }
19409
- if (value.type === "message_start" && assistantText(value.message) !== null) {
19410
- this.#startAssistantMessage(active, value.message);
19411
- return;
19440
+ let finalFault = fault;
19441
+ try {
19442
+ await this.close();
19443
+ } catch (error51) {
19444
+ finalFault = new OmpRpcFaultError("processExited", `Omp RPC timed-out Prompt cleanup failed: ${message2(error51)}`);
19412
19445
  }
19413
- if (value.type === "message_update" && isRecord8(value.assistantMessageEvent)) {
19414
- const event = value.assistantMessageEvent;
19415
- const eventType = String(event.type ?? "");
19416
- const isThinking = eventType === "thinking_delta" || eventType === "reasoning_delta" || eventType === "thought_delta" || eventType === "thinking" || eventType === "reasoning";
19417
- const isText = eventType === "text_delta" || eventType === "text" || eventType === "content_block_delta";
19418
- 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;
19419
- if (isThinking && delta !== null && delta.length > 0) {
19420
- const messageId = this.#ensureAssistantMessage(active, value.message);
19421
- active.reasoningMessageOpen = true;
19422
- active.sawStreamedMessageReasoning = true;
19423
- active.onEvent({ type: "reasoning.delta", messageId, delta });
19424
- } else if (isText && delta !== null && delta.length > 0) {
19425
- const messageId = this.#ensureAssistantMessage(active, value.message);
19426
- active.text += delta;
19427
- active.sawStreamedMessageText = true;
19428
- active.onEvent({ type: "text.delta", messageId, delta });
19429
- } else if (event.type === "error") {
19430
- this.#ensureAssistantMessage(active, value.message);
19431
- active.failure = assistantFailure(event.error) ?? assistantFailure(value.message) ?? new Error("Omp assistant message failed");
19432
- }
19433
- return;
19446
+ this.#rejectAll(finalFault);
19447
+ this.#options.onFault?.(finalFault);
19448
+ }
19449
+ #closeInteractions(active, reason) {
19450
+ for (const [requestId2, pending] of active.interactions) {
19451
+ active.interactions.delete(requestId2);
19452
+ if (pending.timeout)
19453
+ clearTimeout(pending.timeout);
19454
+ active.onEvent({ type: "interaction.closed", requestId: requestId2, reason });
19434
19455
  }
19435
- if (value.type === "message_end") {
19436
- this.#finalizeAssistantMessage(active, value.message);
19456
+ }
19457
+ #rejectActiveTurn(error51) {
19458
+ const active = this.#activeTurn;
19459
+ if (!active)
19437
19460
  return;
19461
+ if (active.cancellationTimeout)
19462
+ clearTimeout(active.cancellationTimeout);
19463
+ if (active.cancellationRecheck)
19464
+ clearTimeout(active.cancellationRecheck);
19465
+ this.#closeInteractions(active, "cancelled");
19466
+ this.#activeTurn = null;
19467
+ active.reject(error51);
19468
+ }
19469
+ #rejectAll(error51) {
19470
+ if (this.#compactionTimeout)
19471
+ clearTimeout(this.#compactionTimeout);
19472
+ this.#compactionTimeout = null;
19473
+ for (const pending of this.#pending.values()) {
19474
+ if (pending.timeout)
19475
+ clearTimeout(pending.timeout);
19476
+ pending.reject(error51);
19438
19477
  }
19439
- if (value.type === "turn_end") {
19440
- this.#finalizeAssistantMessage(active, value.message);
19478
+ this.#pending.clear();
19479
+ const manual = this.#manualCompaction;
19480
+ this.#manualCompaction = null;
19481
+ manual?.reject(error51);
19482
+ this.#rejectActiveTurn(error51);
19483
+ }
19484
+ #fail(error51) {
19485
+ if (this.#closed || this.#failed)
19441
19486
  return;
19487
+ this.#failed = true;
19488
+ this.#startupFaultResolve?.(error51);
19489
+ this.#startupFaultResolve = null;
19490
+ this.#rejectAll(error51);
19491
+ this.#options.onFault?.(error51);
19492
+ }
19493
+ };
19494
+
19495
+ // dist/omp-model-list.js
19496
+ var OMP_MODEL_LIST_TIMEOUT_MS = 15e3;
19497
+ var OMP_MODEL_LIST_OUTPUT_LIMIT = 4 * 1024 * 1024;
19498
+ function listOmpModels(input) {
19499
+ const environment = withNodeRuntimeOnPath({
19500
+ ...process.env,
19501
+ ...input.environment,
19502
+ OMP_SKIP_VERSION_CHECK: "1",
19503
+ OMP_TELEMETRY: "0"
19504
+ });
19505
+ const executable = resolveOmpExecutable({
19506
+ ...input.command ? { command: input.command } : {},
19507
+ environment
19508
+ });
19509
+ const invocation = commandInvocation(executable, ["models", "--json"], environment);
19510
+ return new Promise((resolve) => {
19511
+ if (input.signal?.aborted)
19512
+ return resolve({ kind: "unanswered" });
19513
+ const child = spawn2(invocation.command, invocation.arguments, {
19514
+ cwd: input.cwd,
19515
+ env: environment,
19516
+ // A process group lets a stop reach OMP and its descendants, not only a launcher shim.
19517
+ detached: process.platform !== "win32",
19518
+ stdio: ["ignore", "pipe", "ignore"],
19519
+ windowsHide: true,
19520
+ windowsVerbatimArguments: invocation.windowsVerbatimArguments
19521
+ });
19522
+ let settled = false;
19523
+ let stdout = "";
19524
+ const finish = (result, stop = false) => {
19525
+ if (settled)
19526
+ return;
19527
+ settled = true;
19528
+ clearTimeout(timer);
19529
+ input.signal?.removeEventListener("abort", onAbort);
19530
+ child.stdout.removeAllListeners("data");
19531
+ if (stop) {
19532
+ child.stdout.destroy();
19533
+ try {
19534
+ signalProcessTree(child, "SIGKILL");
19535
+ } catch {
19536
+ }
19537
+ }
19538
+ resolve(result);
19539
+ };
19540
+ const onAbort = () => finish({ kind: "unanswered" }, true);
19541
+ input.signal?.addEventListener("abort", onAbort, { once: true });
19542
+ const timer = setTimeout(() => finish({ kind: "timedOut" }, true), input.timeoutMs ?? OMP_MODEL_LIST_TIMEOUT_MS);
19543
+ child.stdout.setEncoding("utf8").on("data", (chunk) => {
19544
+ stdout += chunk;
19545
+ if (stdout.length > OMP_MODEL_LIST_OUTPUT_LIMIT)
19546
+ finish({ kind: "unanswered" }, true);
19547
+ });
19548
+ child.once("error", (error51) => {
19549
+ finish(error51.code === "ENOENT" ? { kind: "notInstalled", message: "Oh My Pi (omp) is not installed" } : { kind: "unanswered" });
19550
+ });
19551
+ child.once("close", (code) => {
19552
+ if (code !== 0)
19553
+ return finish({ kind: "unanswered" });
19554
+ const models = parseOmpModelList(stdout);
19555
+ finish(models ? { kind: "models", models } : { kind: "unanswered" });
19556
+ });
19557
+ });
19558
+ }
19559
+ function parseOmpModelList(output) {
19560
+ let parsed;
19561
+ try {
19562
+ parsed = JSON.parse(output);
19563
+ } catch {
19564
+ return null;
19565
+ }
19566
+ if (!isRecord9(parsed) || !Array.isArray(parsed.models))
19567
+ return null;
19568
+ const models = [];
19569
+ for (const model2 of parsed.models) {
19570
+ if (!isRecord9(model2) || !nonBlankString2(model2.provider) || !nonBlankString2(model2.id) || typeof model2.reasoning !== "boolean") {
19571
+ return null;
19442
19572
  }
19443
- if (value.type === "tool_execution_start") {
19444
- this.#startTool(active, value);
19445
- return;
19573
+ models.push({ provider: model2.provider, id: model2.id, reasoning: model2.reasoning });
19574
+ }
19575
+ return models;
19576
+ }
19577
+ function isRecord9(value) {
19578
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19579
+ }
19580
+ function nonBlankString2(value) {
19581
+ return typeof value === "string" && value.trim().length > 0;
19582
+ }
19583
+
19584
+ // ../../../node_modules/diff/libesm/diff/base.js
19585
+ var Diff = class {
19586
+ diff(oldStr, newStr, options = {}) {
19587
+ let callback;
19588
+ if (typeof options === "function") {
19589
+ callback = options;
19590
+ options = {};
19591
+ } else if ("callback" in options) {
19592
+ callback = options.callback;
19446
19593
  }
19447
- if (value.type === "tool_execution_update") {
19448
- this.#updateTool(active, value);
19449
- return;
19594
+ const oldString = this.castInput(oldStr, options);
19595
+ const newString = this.castInput(newStr, options);
19596
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
19597
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
19598
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
19599
+ }
19600
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
19601
+ var _a3;
19602
+ const done = (value) => {
19603
+ value = this.postProcess(value, options);
19604
+ if (callback) {
19605
+ setTimeout(function() {
19606
+ callback(value);
19607
+ }, 0);
19608
+ return void 0;
19609
+ } else {
19610
+ return value;
19611
+ }
19612
+ };
19613
+ const newLen = newTokens.length, oldLen = oldTokens.length;
19614
+ let editLength = 1;
19615
+ let maxEditLength = newLen + oldLen;
19616
+ if (options.maxEditLength != null) {
19617
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
19450
19618
  }
19451
- if (value.type === "tool_execution_end") {
19452
- this.#completeTool(active, value);
19453
- return;
19619
+ const maxExecutionTime = (_a3 = options.timeout) !== null && _a3 !== void 0 ? _a3 : Infinity;
19620
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
19621
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
19622
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
19623
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
19624
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
19454
19625
  }
19455
- if (value.type === "agent_end" && Array.isArray(value.messages)) {
19456
- for (let index = value.messages.length - 1; index >= 0; index -= 1) {
19457
- const message3 = value.messages[index];
19458
- if (!isRecord8(message3) || message3.role !== "assistant")
19626
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
19627
+ const execEditLength = () => {
19628
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
19629
+ let basePath;
19630
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
19631
+ if (removePath) {
19632
+ bestPath[diagonalPath - 1] = void 0;
19633
+ }
19634
+ let canAdd = false;
19635
+ if (addPath) {
19636
+ const addPathNewPos = addPath.oldPos - diagonalPath;
19637
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
19638
+ }
19639
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
19640
+ if (!canAdd && !canRemove) {
19641
+ bestPath[diagonalPath] = void 0;
19459
19642
  continue;
19460
- this.#finalizeAssistantMessage(active, message3);
19461
- break;
19643
+ }
19644
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
19645
+ basePath = this.addToPath(addPath, true, false, 0, options);
19646
+ } else {
19647
+ basePath = this.addToPath(removePath, false, true, 1, options);
19648
+ }
19649
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
19650
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
19651
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
19652
+ } else {
19653
+ bestPath[diagonalPath] = basePath;
19654
+ if (basePath.oldPos + 1 >= oldLen) {
19655
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
19656
+ }
19657
+ if (newPos + 1 >= newLen) {
19658
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
19659
+ }
19660
+ }
19462
19661
  }
19463
- }
19464
- if (value.type === "agent_end" && value.isTerminal !== false) {
19465
- active.terminalEnd = true;
19466
- if (active.cancellation !== "none") {
19467
- if (active.cancellation === "accepted")
19468
- void this.#confirmCancelledTurn(active);
19469
- return;
19662
+ editLength++;
19663
+ };
19664
+ if (callback) {
19665
+ (function exec() {
19666
+ setTimeout(function() {
19667
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
19668
+ return callback(void 0);
19669
+ }
19670
+ if (!execEditLength()) {
19671
+ exec();
19672
+ }
19673
+ }, 0);
19674
+ })();
19675
+ } else {
19676
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
19677
+ const ret = execEditLength();
19678
+ if (ret) {
19679
+ return ret;
19680
+ }
19470
19681
  }
19471
- if (active.settlement !== "pending")
19472
- return;
19473
- active.settlement = "confirming";
19474
- void this.#confirmSettledTurn(active);
19475
19682
  }
19476
19683
  }
19477
- /** Every finished assistant message is one model request, with or without an active Turn. */
19478
- #observeUsage(value) {
19479
- if (value.type === "message_start") {
19480
- this.#usageRequestStartedAtMs = null;
19481
- return;
19684
+ addToPath(path9, added, removed, oldPosInc, options) {
19685
+ const last = path9.lastComponent;
19686
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
19687
+ return {
19688
+ oldPos: path9.oldPos + oldPosInc,
19689
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
19690
+ };
19691
+ } else {
19692
+ return {
19693
+ oldPos: path9.oldPos + oldPosInc,
19694
+ lastComponent: { count: 1, added, removed, previousComponent: last }
19695
+ };
19482
19696
  }
19483
- if (value.type === "message_update") {
19484
- const event = value.assistantMessageEvent;
19485
- if (this.#usageRequestStartedAtMs === null && isRecord8(event) && typeof event.type === "string" && /^(text|thinking|reasoning|thought|toolcall)_(start|delta)$/u.test(event.type)) {
19486
- this.#usageRequestStartedAtMs = Date.now();
19697
+ }
19698
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
19699
+ const newLen = newTokens.length, oldLen = oldTokens.length;
19700
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
19701
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
19702
+ newPos++;
19703
+ oldPos++;
19704
+ commonCount++;
19705
+ if (options.oneChangePerToken) {
19706
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
19487
19707
  }
19488
- return;
19489
19708
  }
19490
- if (value.type === "message_end" && isRecord8(value.message)) {
19491
- if (value.message.role !== "assistant")
19492
- return;
19493
- const startedAtMs = this.#usageRequestStartedAtMs;
19494
- this.#usageRequestStartedAtMs = null;
19495
- this.#options.onUsage?.({
19496
- message: value.message,
19497
- startedAtMs,
19498
- completedAtMs: Date.now()
19499
- });
19709
+ if (commonCount && !options.oneChangePerToken) {
19710
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
19500
19711
  }
19712
+ basePath.oldPos = oldPos;
19713
+ return newPos;
19501
19714
  }
19502
- #handleSubagentFrame(active, value) {
19503
- if (value.type !== "subagent_lifecycle" && value.type !== "subagent_progress" && value.type !== "subagent_event") {
19504
- return false;
19505
- }
19506
- const payload = isRecord8(value.payload) ? value.payload : null;
19507
- const progressPayload = value.type === "subagent_progress" && payload && isRecord8(payload.progress) ? payload.progress : null;
19508
- const nativeIdValue = progressPayload?.id ?? payload?.id;
19509
- if (!payload || !nonBlankString(nativeIdValue)) {
19510
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent frame has no stable ID");
19511
- }
19512
- const nativeSubagentId = nativeIdValue;
19513
- const callId = nativeSubagentId;
19514
- const emit = active?.onEvent ?? this.#options.onSubagentEvent;
19515
- if (value.type === "subagent_event") {
19516
- emit?.({ type: "subagent.transcript.changed", callId, nativeSubagentId });
19517
- return true;
19715
+ equals(left, right, options) {
19716
+ if (options.comparator) {
19717
+ return options.comparator(left, right);
19718
+ } else {
19719
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
19518
19720
  }
19519
- if (value.type === "subagent_lifecycle") {
19520
- const status2 = payload.status;
19521
- if (status2 === "started") {
19522
- const description = nonBlankString(payload.description) ? payload.description : nonBlankString(payload.agent) ? `${payload.agent} delegation` : "Subagent delegation";
19523
- emit?.({
19524
- type: "subagent.started",
19525
- callId,
19526
- nativeSubagentId,
19527
- description,
19528
- ...nonBlankString(payload.agent) ? { role: payload.agent } : {},
19529
- ...nonBlankString(payload.task) ? { prompt: payload.task } : {},
19530
- background: payload.detached === true,
19531
- ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
19532
- });
19533
- return true;
19534
- }
19535
- if (status2 !== "completed" && status2 !== "failed" && status2 !== "aborted") {
19536
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent lifecycle status is invalid");
19721
+ }
19722
+ removeEmpty(array2) {
19723
+ const ret = [];
19724
+ for (let i = 0; i < array2.length; i++) {
19725
+ if (array2[i]) {
19726
+ ret.push(array2[i]);
19537
19727
  }
19538
- emit?.({
19539
- type: "subagent.completed",
19540
- callId,
19541
- nativeSubagentId,
19542
- isError: status2 !== "completed",
19543
- ...nonBlankString(payload.resultSummary) ? { resultSummary: payload.resultSummary } : {},
19544
- ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
19545
- });
19546
- return true;
19547
19728
  }
19548
- const progress = progressPayload;
19549
- if (!progress || !nonBlankString(progress.id) || progress.id !== nativeSubagentId) {
19550
- throw new OmpRpcFaultError("protocolError", "Omp RPC Subagent progress is invalid");
19551
- }
19552
- const status = subagentStatus(progress.status);
19553
- const recentOutput = Array.isArray(progress.recentOutput) ? progress.recentOutput.filter((item) => typeof item === "string") : [];
19554
- const resultSummary = recentOutput.at(-1);
19555
- emit?.({
19556
- type: "subagent.updated",
19557
- callId,
19558
- nativeSubagentId,
19559
- status,
19560
- ...nonBlankString(progress.description) ? { description: progress.description } : nonBlankString(payload.task) ? { description: payload.task } : {},
19561
- ...resultSummary !== void 0 ? { resultSummary } : {},
19562
- ...typeof payload.sessionFile === "string" ? { sessionFile: payload.sessionFile } : {}
19563
- });
19564
- return true;
19729
+ return ret;
19565
19730
  }
19566
- #isBlockingInteraction(value) {
19567
- return ["select", "confirm", "input", "editor"].includes(String(value.method));
19731
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
19732
+ castInput(value, options) {
19733
+ return value;
19568
19734
  }
19569
- #startInteraction(active, value) {
19570
- if (!this.#isBlockingInteraction(value))
19571
- return;
19572
- const requestId2 = value.id;
19573
- const method = value.method;
19574
- const title = value.title;
19575
- const timeoutMs = value.timeout;
19576
- if (typeof requestId2 !== "string" || requestId2.length === 0 || typeof method !== "string" || typeof title !== "string" || title.length === 0 || timeoutMs !== void 0 && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs < 0) || active.interactions.has(requestId2)) {
19577
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Interaction request");
19735
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
19736
+ tokenize(value, options) {
19737
+ return Array.from(value);
19738
+ }
19739
+ join(chars) {
19740
+ return chars.join("");
19741
+ }
19742
+ postProcess(changeObjects, options) {
19743
+ return changeObjects;
19744
+ }
19745
+ get useLongestToken() {
19746
+ return false;
19747
+ }
19748
+ buildValues(lastComponent, newTokens, oldTokens) {
19749
+ const components = [];
19750
+ let nextComponent;
19751
+ while (lastComponent) {
19752
+ components.push(lastComponent);
19753
+ nextComponent = lastComponent.previousComponent;
19754
+ delete lastComponent.previousComponent;
19755
+ lastComponent = nextComponent;
19578
19756
  }
19579
- let request;
19580
- if (method === "select") {
19581
- if (!Array.isArray(value.options) || value.options.length === 0 || !value.options.every((option) => typeof option === "string" && option.length > 0)) {
19582
- throw new OmpRpcFaultError("protocolError", "Omp RPC select request has invalid options");
19583
- }
19584
- let optionDetails;
19585
- if (value.optionDetails !== void 0) {
19586
- if (!Array.isArray(value.optionDetails) || value.optionDetails.length !== value.options.length || !value.optionDetails.every((detail) => typeof detail === "object" && detail !== null && !Array.isArray(detail) && (detail.description === void 0 || typeof detail.description === "string"))) {
19587
- throw new OmpRpcFaultError("protocolError", "Omp RPC select option details are invalid");
19757
+ components.reverse();
19758
+ const componentLen = components.length;
19759
+ let componentPos = 0, newPos = 0, oldPos = 0;
19760
+ for (; componentPos < componentLen; componentPos++) {
19761
+ const component = components[componentPos];
19762
+ if (!component.removed) {
19763
+ if (!component.added && this.useLongestToken) {
19764
+ let value = newTokens.slice(newPos, newPos + component.count);
19765
+ value = value.map(function(value2, i) {
19766
+ const oldValue = oldTokens[oldPos + i];
19767
+ return oldValue.length > value2.length ? oldValue : value2;
19768
+ });
19769
+ component.value = this.join(value);
19770
+ } else {
19771
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
19588
19772
  }
19589
- optionDetails = value.optionDetails.map(({ description }) => description === void 0 ? {} : { description });
19590
- }
19591
- request = {
19592
- requestId: requestId2,
19593
- method,
19594
- title,
19595
- options: [...value.options],
19596
- ...optionDetails ? { optionDetails } : {},
19597
- ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19598
- };
19599
- } else if (method === "confirm") {
19600
- if (typeof value.message !== "string") {
19601
- throw new OmpRpcFaultError("protocolError", "Omp RPC confirm request has no message");
19602
- }
19603
- request = {
19604
- requestId: requestId2,
19605
- method,
19606
- title,
19607
- message: value.message,
19608
- ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19609
- };
19610
- } else if (method === "input") {
19611
- if (value.placeholder !== void 0 && typeof value.placeholder !== "string") {
19612
- throw new OmpRpcFaultError("protocolError", "Omp RPC input placeholder is invalid");
19613
- }
19614
- request = {
19615
- requestId: requestId2,
19616
- method,
19617
- title,
19618
- ...typeof value.placeholder === "string" ? { placeholder: value.placeholder } : {},
19619
- ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19620
- };
19621
- } else {
19622
- if (value.prefill !== void 0 && typeof value.prefill !== "string") {
19623
- throw new OmpRpcFaultError("protocolError", "Omp RPC editor prefill is invalid");
19773
+ newPos += component.count;
19774
+ if (!component.added) {
19775
+ oldPos += component.count;
19776
+ }
19777
+ } else {
19778
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
19779
+ oldPos += component.count;
19624
19780
  }
19625
- request = {
19626
- requestId: requestId2,
19627
- method: "editor",
19628
- title,
19629
- ...typeof value.prefill === "string" ? { prefill: value.prefill } : {},
19630
- ...typeof timeoutMs === "number" ? { timeoutMs } : {}
19631
- };
19632
- }
19633
- const pending = { request, timeout: null };
19634
- if (request.timeoutMs !== void 0) {
19635
- pending.timeout = setTimeout(() => {
19636
- if (this.#activeTurn !== active || !active.interactions.has(requestId2))
19637
- return;
19638
- void this.#resolveInteraction({ requestId: requestId2, cancelled: true }, "expired").catch((error51) => {
19639
- if (this.#closed || this.#failed)
19640
- return;
19641
- this.#fail(new OmpRpcFaultError("protocolError", `Omp RPC Interaction timeout handling failed: ${message2(error51)}`));
19642
- });
19643
- }, request.timeoutMs);
19644
19781
  }
19645
- active.interactions.set(requestId2, pending);
19646
- active.onEvent({ type: "interaction.requested", request });
19782
+ return components;
19647
19783
  }
19648
- #handleResponse(value) {
19649
- const id = value.id;
19650
- if (typeof id !== "string") {
19651
- this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC response has no id"));
19652
- return;
19653
- }
19654
- const pending = this.#pending.get(id);
19655
- const active = this.#activeTurn;
19656
- if (!pending) {
19657
- if (active?.promptId === id && value.command === "prompt" && value.success === false) {
19658
- active.failure = new Error(typeof value.error === "string" ? value.error : "Omp RPC command failed");
19659
- return;
19784
+ };
19785
+
19786
+ // ../../../node_modules/diff/libesm/diff/line.js
19787
+ var LineDiff = class extends Diff {
19788
+ constructor() {
19789
+ super(...arguments);
19790
+ this.tokenize = tokenize;
19791
+ }
19792
+ equals(left, right, options) {
19793
+ if (options.ignoreWhitespace) {
19794
+ if (!options.newlineIsToken || !left.includes("\n")) {
19795
+ left = left.trim();
19660
19796
  }
19661
- this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC response id is not pending"));
19662
- return;
19663
- }
19664
- if (pending.timeout)
19665
- clearTimeout(pending.timeout);
19666
- this.#pending.delete(id);
19667
- if (value.success === true) {
19668
- if (pending.command === "prompt" && active?.promptId === id && isRecord8(value.data) && value.data.agentInvoked === false) {
19669
- this.#settleLocalTurn(active, { cancelled: false });
19797
+ if (!options.newlineIsToken || !right.includes("\n")) {
19798
+ right = right.trim();
19670
19799
  }
19671
- pending.resolve(value);
19672
- } else {
19673
- const error51 = typeof value.error === "string" ? value.error : "Omp RPC command failed";
19674
- if (value.command === pending.command && error51 === `Unknown command: ${pending.command}`) {
19675
- pending.reject(new OmpRpcUnsupportedCommandError(pending.command));
19676
- } else {
19677
- pending.reject(new Error(error51));
19800
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
19801
+ if (left.endsWith("\n")) {
19802
+ left = left.slice(0, -1);
19803
+ }
19804
+ if (right.endsWith("\n")) {
19805
+ right = right.slice(0, -1);
19678
19806
  }
19679
19807
  }
19808
+ return super.equals(left, right, options);
19680
19809
  }
19681
- #startTool(active, value) {
19682
- const callId = value.toolCallId;
19683
- const toolName = value.toolName;
19684
- const argumentsResult = jsonValueSchema.safeParse(value.args);
19685
- if (typeof callId !== "string" || callId.length === 0 || typeof toolName !== "string" || toolName.length === 0 || !argumentsResult.success || active.tools.has(callId)) {
19686
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool start");
19687
- }
19688
- active.sawTool = true;
19689
- active.tools.set(callId, toolName);
19690
- active.onEvent({
19691
- type: "tool.started",
19692
- callId,
19693
- toolName,
19694
- arguments: argumentsResult.data
19695
- });
19810
+ };
19811
+ var lineDiff = new LineDiff();
19812
+ function diffLines(oldStr, newStr, options) {
19813
+ return lineDiff.diff(oldStr, newStr, options);
19814
+ }
19815
+ function tokenize(value, options) {
19816
+ if (options.stripTrailingCr) {
19817
+ value = value.replace(/\r\n/g, "\n");
19696
19818
  }
19697
- #updateTool(active, value) {
19698
- const callId = value.toolCallId;
19699
- if (typeof callId !== "string" || callId.length === 0) {
19700
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool update");
19701
- }
19702
- if (!active.tools.has(callId))
19703
- return;
19704
- const outputResult = jsonValueSchema.safeParse(value.partialResult ?? null);
19705
- if (!outputResult.success) {
19706
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool update");
19707
- }
19708
- active.onEvent({ type: "tool.updated", callId, output: outputResult.data });
19819
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
19820
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
19821
+ linesAndNewlines.pop();
19709
19822
  }
19710
- #completeTool(active, value) {
19711
- const callId = value.toolCallId;
19712
- const toolName = value.toolName;
19713
- if (typeof callId !== "string" || callId.length === 0) {
19714
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool end");
19715
- }
19716
- const expectedName = active.tools.get(callId);
19717
- if (expectedName === void 0)
19718
- return;
19719
- const result = jsonValueSchema.safeParse(value.result);
19720
- if (typeof toolName !== "string" || expectedName !== toolName || value.isError !== void 0 && typeof value.isError !== "boolean" || !result.success) {
19721
- throw new OmpRpcFaultError("protocolError", "Omp RPC returned an invalid Tool end");
19823
+ for (let i = 0; i < linesAndNewlines.length; i++) {
19824
+ const line = linesAndNewlines[i];
19825
+ if (i % 2 && !options.newlineIsToken) {
19826
+ retLines[retLines.length - 1] += line;
19827
+ } else {
19828
+ retLines.push(line);
19722
19829
  }
19723
- active.tools.delete(callId);
19724
- active.onEvent({
19725
- type: "tool.completed",
19726
- callId,
19727
- toolName,
19728
- result: result.data,
19729
- isError: value.isError === true
19730
- });
19731
19830
  }
19732
- async #confirmCancelledTurn(active) {
19733
- if (this.#activeTurn !== active || active.cancellation !== "accepted" || active.settlement === "confirming" || active.cancellationChecking)
19734
- return;
19735
- active.cancellationChecking = true;
19736
- if (active.cancellationRecheck)
19737
- clearTimeout(active.cancellationRecheck);
19738
- active.cancellationRecheck = null;
19739
- try {
19740
- let response = await this.#send("get_state", {});
19741
- if (this.#activeTurn !== active)
19742
- return;
19743
- if (parseSessionStreaming(response)) {
19744
- await this.#send("abort", {});
19745
- if (this.#activeTurn !== active)
19746
- return;
19747
- response = await this.#send("get_state", {});
19748
- }
19749
- let state = parseSessionState(response);
19750
- if (this.#activeTurn !== active || active.cancellation !== "accepted")
19751
- return;
19752
- const data = sessionStateData(response);
19753
- if (!active.cancellationReloaded && state.sessionFile && state.sessionId === this.state.sessionId && state.sessionFile === this.state.sessionFile && data.isStreaming === false && data.isCompacting === false && data.queuedMessageCount === 0 && data.hasPendingAsyncWork === true && active.tools.size === 0) {
19754
- await this.verifySessionCwd(this.#options.cwd);
19755
- if (this.#activeTurn !== active)
19756
- return;
19757
- active.cancellationReloaded = true;
19758
- const reset = await this.#send("new_session", {});
19759
- if (this.#activeTurn !== active)
19760
- return;
19761
- if (!isRecord8(reset.data) || reset.data.cancelled !== false) {
19762
- throw new OmpRpcFaultError("unavailable", "Omp cancelled Session reset was refused");
19763
- }
19764
- const reloaded = await this.#send("switch_session", { sessionPath: state.sessionFile });
19765
- if (this.#activeTurn !== active)
19766
- return;
19767
- if (!isRecord8(reloaded.data) || reloaded.data.cancelled !== false) {
19768
- throw new OmpRpcFaultError("unavailable", "Omp cancelled Session reload was refused");
19769
- }
19770
- const previous = state;
19771
- response = await this.#send("get_state", {});
19772
- state = parseSessionState(response);
19773
- if (state.sessionId !== previous.sessionId || state.sessionFile !== previous.sessionFile) {
19774
- throw new OmpRpcFaultError("protocolError", "Omp cancelled Session reload changed its identity");
19775
- }
19776
- if (state.provider !== previous.provider || state.modelId !== previous.modelId) {
19777
- if (!previous.provider || !previous.modelId) {
19778
- throw new OmpRpcFaultError("protocolError", "Omp cancelled Session has no restorable Model");
19779
- }
19780
- await this.#send("set_model", { provider: previous.provider, modelId: previous.modelId });
19781
- }
19782
- if (previous.thinkingLevel && state.thinkingLevel !== previous.thinkingLevel) {
19783
- await this.#send("set_thinking_level", { level: previous.thinkingLevel });
19784
- }
19785
- response = await this.#send("get_state", {});
19786
- state = parseSessionState(response);
19787
- if (state.sessionId !== previous.sessionId || state.sessionFile !== previous.sessionFile || state.provider !== previous.provider || state.modelId !== previous.modelId || previous.thinkingLevel !== null && state.thinkingLevel !== previous.thinkingLevel) {
19788
- throw new OmpRpcFaultError("protocolError", "Omp cancelled Session reload changed its identity or configuration");
19789
- }
19831
+ return retLines;
19832
+ }
19833
+
19834
+ // ../../../node_modules/diff/libesm/patch/parse.js
19835
+ function parsePatch(uniDiff) {
19836
+ const diffstr = uniDiff.split(/\n/), list = [];
19837
+ let i = 0;
19838
+ function parseIndex() {
19839
+ const index = {};
19840
+ list.push(index);
19841
+ while (i < diffstr.length) {
19842
+ const line = diffstr[i];
19843
+ if (/^(---|\+\+\+|@@)\s/.test(line)) {
19844
+ break;
19790
19845
  }
19791
- if (!cancellationSettled(response, active.terminalEnd)) {
19792
- active.settlement = "pending";
19793
- active.cancellationRecheck = setTimeout(() => {
19794
- active.cancellationRecheck = null;
19795
- void this.#confirmCancelledTurn(active);
19796
- }, 250);
19797
- return;
19846
+ const header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line);
19847
+ if (header) {
19848
+ index.index = header[1];
19849
+ }
19850
+ i++;
19851
+ }
19852
+ parseFileHeader(index);
19853
+ parseFileHeader(index);
19854
+ index.hunks = [];
19855
+ while (i < diffstr.length) {
19856
+ const line = diffstr[i];
19857
+ if (/^(Index:\s|diff\s|---\s|\+\+\+\s|===================================================================)/.test(line)) {
19858
+ break;
19859
+ } else if (/^@@/.test(line)) {
19860
+ index.hunks.push(parseHunk());
19861
+ } else if (line) {
19862
+ throw new Error("Unknown line " + (i + 1) + " " + JSON.stringify(line));
19863
+ } else {
19864
+ i++;
19798
19865
  }
19799
- this.#state = state;
19800
- active.settlement = "confirmed";
19801
- this.#finishSettledTurn(active);
19802
- } catch (error51) {
19803
- if (this.#activeTurn !== active)
19804
- return;
19805
- const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC cancelled Turn state could not be confirmed: ${message2(error51)}`);
19806
- this.#terminateFailedCancellation(active, fault);
19807
- } finally {
19808
- active.cancellationChecking = false;
19809
19866
  }
19810
19867
  }
19811
- #settleLocalTurn(active, result) {
19812
- if (this.#activeTurn !== active || active.settlement !== "pending")
19813
- return;
19814
- active.localResult = result;
19815
- active.settlement = "confirming";
19816
- void this.#confirmSettledTurn(active);
19817
- }
19818
- async #confirmSettledTurn(active) {
19819
- try {
19820
- const response = await this.#send("get_state", {});
19821
- const state = parseSessionState(response);
19822
- if (this.#activeTurn !== active)
19823
- return;
19824
- if (active.cancellation !== "none") {
19825
- active.settlement = "pending";
19826
- if (active.cancellation === "accepted")
19827
- void this.#confirmCancelledTurn(active);
19828
- return;
19868
+ function parseFileHeader(index) {
19869
+ const fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]);
19870
+ if (fileHeader) {
19871
+ const data = fileHeader[2].split(" ", 2), header = (data[1] || "").trim();
19872
+ let fileName = data[0].replace(/\\\\/g, "\\");
19873
+ if (/^".*"$/.test(fileName)) {
19874
+ fileName = fileName.substr(1, fileName.length - 2);
19829
19875
  }
19830
- if (parseSessionStreaming(response)) {
19831
- throw new OmpRpcFaultError("protocolError", "Omp RPC completed Turn state is still Streaming");
19876
+ if (fileHeader[1] === "---") {
19877
+ index.oldFileName = fileName;
19878
+ index.oldHeader = header;
19879
+ } else {
19880
+ index.newFileName = fileName;
19881
+ index.newHeader = header;
19832
19882
  }
19833
- this.#state = state;
19834
- active.settlement = "confirmed";
19835
- this.#finishSettledTurn(active);
19836
- } catch (error51) {
19837
- if (this.#activeTurn !== active)
19838
- return;
19839
- const fault = error51 instanceof OmpRpcFaultError ? error51 : new OmpRpcFaultError("protocolError", `Omp RPC stable Turn state could not be confirmed: ${message2(error51)}`);
19840
- this.#fail(fault);
19883
+ i++;
19841
19884
  }
19842
19885
  }
19843
- #finishSettledTurn(active) {
19844
- if (this.#activeTurn !== active || active.settlement !== "confirmed" || active.cancellation === "requesting") {
19845
- return;
19886
+ function parseHunk() {
19887
+ var _a3;
19888
+ const chunkHeaderIndex = i, chunkHeaderLine = diffstr[i++], chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
19889
+ const hunk = {
19890
+ oldStart: +chunkHeader[1],
19891
+ oldLines: typeof chunkHeader[2] === "undefined" ? 1 : +chunkHeader[2],
19892
+ newStart: +chunkHeader[3],
19893
+ newLines: typeof chunkHeader[4] === "undefined" ? 1 : +chunkHeader[4],
19894
+ lines: []
19895
+ };
19896
+ if (hunk.oldLines === 0) {
19897
+ hunk.oldStart += 1;
19846
19898
  }
19847
- if (active.cancellationTimeout)
19848
- clearTimeout(active.cancellationTimeout);
19849
- active.cancellationTimeout = null;
19850
- if (active.cancellationRecheck)
19851
- clearTimeout(active.cancellationRecheck);
19852
- active.cancellationRecheck = null;
19853
- this.#closeInteractions(active, active.cancellation === "accepted" ? "cancelled" : "superseded");
19854
- if (active.tools.size > 0) {
19855
- this.#fail(new OmpRpcFaultError("protocolError", "Omp RPC settled with active Tool executions"));
19856
- return;
19899
+ if (hunk.newLines === 0) {
19900
+ hunk.newStart += 1;
19857
19901
  }
19858
- this.#activeTurn = null;
19859
- if (active.localResult) {
19860
- active.resolve({
19861
- text: active.text,
19862
- agentInvoked: false,
19863
- ...active.localResult,
19864
- cancelled: active.localResult.cancelled || active.cancellation === "accepted"
19865
- });
19866
- } else if (active.cancellation === "accepted") {
19867
- active.resolve({ text: active.text, cancelled: true, agentInvoked: true });
19868
- } else if (active.failure) {
19869
- active.reject(active.failure);
19870
- } else if (active.text.trim().length === 0 && !active.sawTool) {
19871
- active.reject(new Error("Omp RPC settled without displayable output"));
19872
- } else {
19873
- active.resolve({ text: active.text, cancelled: false, agentInvoked: true });
19902
+ let addCount = 0, removeCount = 0;
19903
+ for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || ((_a3 = diffstr[i]) === null || _a3 === void 0 ? void 0 : _a3.startsWith("\\"))); i++) {
19904
+ const operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? " " : diffstr[i][0];
19905
+ if (operation === "+" || operation === "-" || operation === " " || operation === "\\") {
19906
+ hunk.lines.push(diffstr[i]);
19907
+ if (operation === "+") {
19908
+ addCount++;
19909
+ } else if (operation === "-") {
19910
+ removeCount++;
19911
+ } else if (operation === " ") {
19912
+ addCount++;
19913
+ removeCount++;
19914
+ }
19915
+ } else {
19916
+ throw new Error(`Hunk at line ${chunkHeaderIndex + 1} contained invalid line ${diffstr[i]}`);
19917
+ }
19874
19918
  }
19875
- }
19876
- #startAssistantMessage(active, value) {
19877
- if (active.assistantMessageId !== null && active.reasoningMessageOpen) {
19878
- active.onEvent({ type: "reasoning.completed", messageId: active.assistantMessageId });
19919
+ if (!addCount && hunk.newLines === 1) {
19920
+ hunk.newLines = 0;
19879
19921
  }
19880
- const messageId = assistantMessageId(value) ?? randomUUID();
19881
- active.assistantMessageId = messageId;
19882
- active.sawStreamedMessageText = false;
19883
- active.sawStreamedMessageReasoning = false;
19884
- active.reasoningMessageOpen = false;
19885
- return messageId;
19922
+ if (!removeCount && hunk.oldLines === 1) {
19923
+ hunk.oldLines = 0;
19924
+ }
19925
+ if (addCount !== hunk.newLines) {
19926
+ throw new Error("Added line count did not match for hunk at line " + (chunkHeaderIndex + 1));
19927
+ }
19928
+ if (removeCount !== hunk.oldLines) {
19929
+ throw new Error("Removed line count did not match for hunk at line " + (chunkHeaderIndex + 1));
19930
+ }
19931
+ return hunk;
19886
19932
  }
19887
- #ensureAssistantMessage(active, value) {
19888
- return active.assistantMessageId ?? this.#startAssistantMessage(active, value);
19933
+ while (i < diffstr.length) {
19934
+ parseIndex();
19889
19935
  }
19890
- #finalizeAssistantMessage(active, value) {
19891
- const finalText = assistantText(value);
19892
- const finalReasoning = assistantReasoning(value);
19893
- const failure = assistantFailure(value);
19894
- const cacheHitRatePercent = optionalOmpCacheHitRatePercent(value);
19895
- if (finalText === null || finalReasoning === null || failure === void 0)
19896
- return;
19897
- if (active.assistantMessageId === null && finalText === active.lastFinalizedMessageText && finalReasoning === active.lastFinalizedMessageReasoning && !active.sawStreamedMessageText && !active.sawStreamedMessageReasoning) {
19936
+ return list;
19937
+ }
19938
+
19939
+ // ../../../node_modules/diff/libesm/patch/create.js
19940
+ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
19941
+ let optionsObj;
19942
+ if (!options) {
19943
+ optionsObj = {};
19944
+ } else if (typeof options === "function") {
19945
+ optionsObj = { callback: options };
19946
+ } else {
19947
+ optionsObj = options;
19948
+ }
19949
+ if (typeof optionsObj.context === "undefined") {
19950
+ optionsObj.context = 4;
19951
+ }
19952
+ const context = optionsObj.context;
19953
+ if (optionsObj.newlineIsToken) {
19954
+ throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
19955
+ }
19956
+ if (!optionsObj.callback) {
19957
+ return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
19958
+ } else {
19959
+ const { callback } = optionsObj;
19960
+ diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
19961
+ const patch = diffLinesResultToPatch(diff);
19962
+ callback(patch);
19963
+ } }));
19964
+ }
19965
+ function diffLinesResultToPatch(diff) {
19966
+ if (!diff) {
19898
19967
  return;
19899
19968
  }
19900
- active.failure = failure;
19901
- this.#latestCacheHitRatePercent = cacheHitRatePercent;
19902
- const messageId = this.#ensureAssistantMessage(active, value);
19903
- if (!active.sawStreamedMessageReasoning && finalReasoning.length > 0) {
19904
- active.reasoningMessageOpen = true;
19905
- active.onEvent({ type: "reasoning.delta", messageId, delta: finalReasoning });
19969
+ diff.push({ value: "", lines: [] });
19970
+ function contextLines(lines) {
19971
+ return lines.map(function(entry) {
19972
+ return " " + entry;
19973
+ });
19906
19974
  }
19907
- if (active.reasoningMessageOpen && active.failure === null) {
19908
- active.onEvent({ type: "reasoning.completed", messageId });
19975
+ const hunks = [];
19976
+ let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
19977
+ for (let i = 0; i < diff.length; i++) {
19978
+ const current = diff[i], lines = current.lines || splitLines(current.value);
19979
+ current.lines = lines;
19980
+ if (current.added || current.removed) {
19981
+ if (!oldRangeStart) {
19982
+ const prev = diff[i - 1];
19983
+ oldRangeStart = oldLine;
19984
+ newRangeStart = newLine;
19985
+ if (prev) {
19986
+ curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
19987
+ oldRangeStart -= curRange.length;
19988
+ newRangeStart -= curRange.length;
19989
+ }
19990
+ }
19991
+ for (const line of lines) {
19992
+ curRange.push((current.added ? "+" : "-") + line);
19993
+ }
19994
+ if (current.added) {
19995
+ newLine += lines.length;
19996
+ } else {
19997
+ oldLine += lines.length;
19998
+ }
19999
+ } else {
20000
+ if (oldRangeStart) {
20001
+ if (lines.length <= context * 2 && i < diff.length - 2) {
20002
+ for (const line of contextLines(lines)) {
20003
+ curRange.push(line);
20004
+ }
20005
+ } else {
20006
+ const contextSize = Math.min(lines.length, context);
20007
+ for (const line of contextLines(lines.slice(0, contextSize))) {
20008
+ curRange.push(line);
20009
+ }
20010
+ const hunk = {
20011
+ oldStart: oldRangeStart,
20012
+ oldLines: oldLine - oldRangeStart + contextSize,
20013
+ newStart: newRangeStart,
20014
+ newLines: newLine - newRangeStart + contextSize,
20015
+ lines: curRange
20016
+ };
20017
+ hunks.push(hunk);
20018
+ oldRangeStart = 0;
20019
+ newRangeStart = 0;
20020
+ curRange = [];
20021
+ }
20022
+ }
20023
+ oldLine += lines.length;
20024
+ newLine += lines.length;
20025
+ }
20026
+ }
20027
+ for (const hunk of hunks) {
20028
+ for (let i = 0; i < hunk.lines.length; i++) {
20029
+ if (hunk.lines[i].endsWith("\n")) {
20030
+ hunk.lines[i] = hunk.lines[i].slice(0, -1);
20031
+ } else {
20032
+ hunk.lines.splice(i + 1, 0, "\");
20033
+ i++;
20034
+ }
20035
+ }
20036
+ }
20037
+ return {
20038
+ oldFileName,
20039
+ newFileName,
20040
+ oldHeader,
20041
+ newHeader,
20042
+ hunks
20043
+ };
20044
+ }
20045
+ }
20046
+ function formatPatch(patch) {
20047
+ if (Array.isArray(patch)) {
20048
+ return patch.map(formatPatch).join("\n");
20049
+ }
20050
+ const ret = [];
20051
+ if (patch.oldFileName == patch.newFileName) {
20052
+ ret.push("Index: " + patch.oldFileName);
20053
+ }
20054
+ ret.push("===================================================================");
20055
+ ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : " " + patch.oldHeader));
20056
+ ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : " " + patch.newHeader));
20057
+ for (let i = 0; i < patch.hunks.length; i++) {
20058
+ const hunk = patch.hunks[i];
20059
+ if (hunk.oldLines === 0) {
20060
+ hunk.oldStart -= 1;
19909
20061
  }
19910
- if (!active.sawStreamedMessageText && finalText.length > 0) {
19911
- active.text += finalText;
19912
- active.onEvent({ type: "text.delta", messageId, delta: finalText });
20062
+ if (hunk.newLines === 0) {
20063
+ hunk.newStart -= 1;
19913
20064
  }
19914
- active.assistantMessageId = null;
19915
- active.sawStreamedMessageText = false;
19916
- active.sawStreamedMessageReasoning = false;
19917
- active.reasoningMessageOpen = false;
19918
- active.lastFinalizedMessageText = finalText;
19919
- active.lastFinalizedMessageReasoning = finalReasoning;
19920
- active.onEvent({ type: "message.completed", messageId });
19921
- }
19922
- #write(value) {
19923
- const child = this.#child;
19924
- if (!child?.stdin.writable || this.#closed || this.#failed) {
19925
- return Promise.reject(new Error("Omp RPC stdin is unavailable"));
20065
+ ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
20066
+ for (const line of hunk.lines) {
20067
+ ret.push(line);
19926
20068
  }
19927
- return new Promise((resolve, reject) => {
19928
- const frame = Buffer.from(`${JSON.stringify(value)}
19929
- `, "utf8");
19930
- child.stdin.write(frame, (error51) => error51 ? reject(error51) : resolve());
19931
- });
19932
20069
  }
19933
- #send(type, payload, id = `codexhost-${randomUUID()}`) {
19934
- const child = this.#child;
19935
- if (!child?.stdin.writable || this.#closed || this.#failed) {
19936
- return Promise.reject(new Error("Omp RPC stdin is unavailable"));
19937
- }
19938
- return new Promise((resolve, reject) => {
19939
- const pending = {
19940
- command: type,
19941
- resolve,
19942
- reject,
19943
- timeout: null
19944
- };
19945
- this.#pending.set(id, pending);
19946
- this.#armCommandTimeout(id, pending);
19947
- const frame = Buffer.from(`${JSON.stringify({ id, type, ...payload })}
19948
- `, "utf8");
19949
- child.stdin.write(frame, (error51) => {
19950
- if (error51) {
19951
- if (pending.timeout)
19952
- clearTimeout(pending.timeout);
19953
- this.#pending.delete(id);
19954
- reject(error51);
19955
- }
19956
- });
19957
- });
20070
+ return ret.join("\n") + "\n";
20071
+ }
20072
+ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
20073
+ if (typeof options === "function") {
20074
+ options = { callback: options };
19958
20075
  }
19959
- #armCommandTimeout(id, pending) {
19960
- if (pending.timeout || this.#pending.get(id) !== pending || (pending.command === "prompt" || pending.command === "compact") && this.#compactionActive) {
20076
+ if (!(options === null || options === void 0 ? void 0 : options.callback)) {
20077
+ const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
20078
+ if (!patchObj) {
19961
20079
  return;
19962
20080
  }
19963
- pending.timeout = setTimeout(() => {
19964
- if (this.#pending.get(id) !== pending)
19965
- return;
19966
- pending.timeout = null;
19967
- const error51 = new Error(`Omp RPC '${pending.command}' command timed out`);
19968
- if (pending.command !== "prompt") {
19969
- this.#pending.delete(id);
19970
- pending.reject(error51);
19971
- return;
20081
+ return formatPatch(patchObj);
20082
+ } else {
20083
+ const { callback } = options;
20084
+ structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
20085
+ if (!patchObj) {
20086
+ callback(void 0);
20087
+ } else {
20088
+ callback(formatPatch(patchObj));
19972
20089
  }
19973
- const fault = new OmpRpcFaultError("protocolError", error51.message);
19974
- void this.#terminateTimedOutPrompt(fault);
19975
- }, this.#options.commandTimeoutMs);
20090
+ } }));
19976
20091
  }
19977
- async #terminateTimedOutPrompt(fault) {
19978
- if (this.#closed || this.#failed)
19979
- return;
19980
- this.#failed = true;
19981
- this.#closed = true;
19982
- for (const pending of this.#pending.values()) {
19983
- if (pending.timeout)
19984
- clearTimeout(pending.timeout);
19985
- pending.timeout = null;
19986
- }
19987
- let finalFault = fault;
19988
- try {
19989
- await this.close();
19990
- } catch (error51) {
19991
- finalFault = new OmpRpcFaultError("processExited", `Omp RPC timed-out Prompt cleanup failed: ${message2(error51)}`);
19992
- }
19993
- this.#rejectAll(finalFault);
19994
- this.#options.onFault?.(finalFault);
20092
+ }
20093
+ function splitLines(text) {
20094
+ const hasTrailingNl = text.endsWith("\n");
20095
+ const result = text.split("\n").map((line) => line + "\n");
20096
+ if (hasTrailingNl) {
20097
+ result.pop();
20098
+ } else {
20099
+ result.push(result.pop().slice(0, -1));
19995
20100
  }
19996
- #closeInteractions(active, reason) {
19997
- for (const [requestId2, pending] of active.interactions) {
19998
- active.interactions.delete(requestId2);
19999
- if (pending.timeout)
20000
- clearTimeout(pending.timeout);
20001
- active.onEvent({ type: "interaction.closed", requestId: requestId2, reason });
20002
- }
20101
+ return result;
20102
+ }
20103
+
20104
+ // dist/omp-adapter.js
20105
+ import { randomUUID as randomUUID2 } from "node:crypto";
20106
+ import { realpath as realpath3, stat as stat2 } from "node:fs/promises";
20107
+ import path7 from "node:path";
20108
+
20109
+ // dist/omp-last-turn-rollback.js
20110
+ function modelFromState(state) {
20111
+ if (state.provider === null && state.modelId === null)
20112
+ return null;
20113
+ if (state.provider === null || state.modelId === null) {
20114
+ throw new Error("Omp state contains a partial Model identity");
20003
20115
  }
20004
- #rejectActiveTurn(error51) {
20005
- const active = this.#activeTurn;
20006
- if (!active)
20007
- return;
20008
- if (active.cancellationTimeout)
20009
- clearTimeout(active.cancellationTimeout);
20010
- if (active.cancellationRecheck)
20011
- clearTimeout(active.cancellationRecheck);
20012
- this.#closeInteractions(active, "cancelled");
20013
- this.#activeTurn = null;
20014
- active.reject(error51);
20116
+ return { provider: state.provider, id: state.modelId };
20117
+ }
20118
+ async function rollbackOmpLastTurn(transport, sourceSessionId, cwd) {
20119
+ const startupState = transport.state;
20120
+ const startupSessionId = startupState.sessionId;
20121
+ const currentModel = modelFromState(startupState);
20122
+ const currentThinking = startupState.thinkingLevel;
20123
+ const copiedHistory = await transport.getEntries();
20124
+ const boundary = resolveOmpLastTurnBoundary(copiedHistory);
20125
+ if (!boundary)
20126
+ return { ok: false, reason: "empty" };
20127
+ const sourceSnapshot = mapOmpSnapshot(copiedHistory, {
20128
+ sessionId: startupSessionId,
20129
+ model: currentModel
20130
+ });
20131
+ let state = await transport.fork(boundary.lastUserEntryId);
20132
+ if (state.sessionId === sourceSessionId || state.sessionId === startupSessionId) {
20133
+ throw new Error("Omp last-Turn rollback did not create a distinct Native Session");
20015
20134
  }
20016
- #rejectAll(error51) {
20017
- if (this.#compactionTimeout)
20018
- clearTimeout(this.#compactionTimeout);
20019
- this.#compactionTimeout = null;
20020
- for (const pending of this.#pending.values()) {
20021
- if (pending.timeout)
20022
- clearTimeout(pending.timeout);
20023
- pending.reject(error51);
20024
- }
20025
- this.#pending.clear();
20026
- const manual = this.#manualCompaction;
20027
- this.#manualCompaction = null;
20028
- manual?.reject(error51);
20029
- this.#rejectActiveTurn(error51);
20135
+ if (!sameOmpModel(modelFromState(state), currentModel)) {
20136
+ if (!currentModel)
20137
+ throw new Error("Omp last-Turn rollback changed the current Model");
20138
+ state = await transport.selectModel(currentModel);
20030
20139
  }
20031
- #fail(error51) {
20032
- if (this.#closed || this.#failed)
20033
- return;
20034
- this.#failed = true;
20035
- this.#startupFaultResolve?.(error51);
20036
- this.#startupFaultResolve = null;
20037
- this.#rejectAll(error51);
20038
- this.#options.onFault?.(error51);
20140
+ if (state.thinkingLevel !== currentThinking) {
20141
+ if (!currentThinking)
20142
+ throw new Error("Omp last-Turn rollback changed current Thinking");
20143
+ state = await transport.selectThinkingOption(currentThinking);
20039
20144
  }
20040
- };
20145
+ if (!sameOmpModel(modelFromState(state), currentModel) || state.thinkingLevel !== currentThinking) {
20146
+ throw new Error("Omp last-Turn rollback could not restore current Model and Thinking");
20147
+ }
20148
+ const snapshot = mapOmpSnapshot(await transport.getEntries(), {
20149
+ sessionId: state.sessionId,
20150
+ model: modelFromState(state)
20151
+ });
20152
+ const expectedTurnKeys = sourceSnapshot.turns.slice(0, -1).map((turn) => turn.nativeTurnRef.nativeTurnKey);
20153
+ const actualTurnKeys = snapshot.turns.map((turn) => turn.nativeTurnRef.nativeTurnKey);
20154
+ if (snapshot.turns.length !== boundary.sourceTurnCount - 1 || actualTurnKeys.some((key, index) => key !== expectedTurnKeys[index])) {
20155
+ throw new Error("Omp last-Turn rollback did not produce the exact retained history prefix");
20156
+ }
20157
+ await transport.verifySessionCwd(cwd);
20158
+ return { ok: true };
20159
+ }
20041
20160
 
20042
20161
  // dist/omp-permission-modes.js
20043
20162
  var nativePermissionModes = /* @__PURE__ */ new Set(["always-ask", "write", "yolo"]);
@@ -20190,6 +20309,24 @@ var OmpSubagentLifecycle = class {
20190
20309
  };
20191
20310
 
20192
20311
  // dist/omp-adapter.js
20312
+ function closedInspection() {
20313
+ return {
20314
+ status: "unavailable",
20315
+ error: { code: "invalidState", message: "Omp Adapter is closed", retryable: false }
20316
+ };
20317
+ }
20318
+ function ompInspectionCapabilities(selectThinkingOption) {
20319
+ return {
20320
+ configuration: {
20321
+ selectModel: true,
20322
+ selectThinkingOption,
20323
+ selectPermissionMode: true,
20324
+ permissionModeScope: "live"
20325
+ },
20326
+ history: { fork: true, forkAcrossCwd: true, rollbackLastTurn: true },
20327
+ subagents: { observe: true, readTranscript: true }
20328
+ };
20329
+ }
20193
20330
  var ompHarnessId2 = harnessIdSchema.parse("omp");
20194
20331
  var ompCommandCatalog = harnessCommandCatalogSchema.parse({
20195
20332
  commands: [
@@ -20204,7 +20341,7 @@ var ompCommandCatalog = harnessCommandCatalogSchema.parse({
20204
20341
  ]
20205
20342
  });
20206
20343
  var DEFAULT_TOOL_OUTPUT_LIMIT = 64e3;
20207
- function isRecord9(value) {
20344
+ function isRecord10(value) {
20208
20345
  return typeof value === "object" && value !== null && !Array.isArray(value);
20209
20346
  }
20210
20347
  function errorMessage(error51) {
@@ -20219,7 +20356,7 @@ var OmpAdapterFaultError = class extends Error {
20219
20356
  }
20220
20357
  };
20221
20358
  function normalizedError(error51, fallbackCode) {
20222
- if (isRecord9(error51) && error51.code === "ENOENT") {
20359
+ if (isRecord10(error51) && error51.code === "ENOENT") {
20223
20360
  return { code: "notInstalled", message: errorMessage(error51), retryable: false };
20224
20361
  }
20225
20362
  if (error51 instanceof OmpAdapterFaultError)
@@ -20296,7 +20433,7 @@ function nativeModelForHistory(state) {
20296
20433
  return nativeModelFromState(state);
20297
20434
  }
20298
20435
  function sessionFileFromRef(ref) {
20299
- if (ref.harnessId !== ompHarnessId2 || !isRecord9(ref.locator) || typeof ref.locator.sessionFile !== "string" || ref.locator.sessionFile.length === 0) {
20436
+ if (ref.harnessId !== ompHarnessId2 || !isRecord10(ref.locator) || typeof ref.locator.sessionFile !== "string" || ref.locator.sessionFile.length === 0) {
20300
20437
  throw new Error("Omp Native Session Ref has no resumable Session file");
20301
20438
  }
20302
20439
  return ref.locator.sessionFile;
@@ -20351,9 +20488,9 @@ function toolFailure(toolName) {
20351
20488
  function nativeText(value) {
20352
20489
  if (typeof value === "string")
20353
20490
  return value;
20354
- if (!isRecord9(value) || !Array.isArray(value.content))
20491
+ if (!isRecord10(value) || !Array.isArray(value.content))
20355
20492
  return "";
20356
- return value.content.filter((content) => isRecord9(content) && content.type === "text" && typeof content.text === "string").map(({ text }) => text).join("");
20493
+ return value.content.filter((content) => isRecord10(content) && content.type === "text" && typeof content.text === "string").map(({ text }) => text).join("");
20357
20494
  }
20358
20495
  function boundedOutput(value, limit) {
20359
20496
  const text = nativeText(value);
@@ -20369,17 +20506,17 @@ function outputText(output) {
20369
20506
  return output?.content.filter((content) => content.type === "text").map(({ text }) => text).join("") ?? "";
20370
20507
  }
20371
20508
  function stringField2(value, key) {
20372
- if (!isRecord9(value))
20509
+ if (!isRecord10(value))
20373
20510
  return void 0;
20374
20511
  const field = value[key];
20375
20512
  if (typeof field === "string" && field.length > 0)
20376
20513
  return field;
20377
- if (isRecord9(value.input)) {
20514
+ if (isRecord10(value.input)) {
20378
20515
  const nested = value.input[key];
20379
20516
  if (typeof nested === "string" && nested.length > 0)
20380
20517
  return nested;
20381
20518
  }
20382
- if (isRecord9(value.arguments)) {
20519
+ if (isRecord10(value.arguments)) {
20383
20520
  const nested = value.arguments[key];
20384
20521
  if (typeof nested === "string" && nested.length > 0)
20385
20522
  return nested;
@@ -20387,7 +20524,7 @@ function stringField2(value, key) {
20387
20524
  return void 0;
20388
20525
  }
20389
20526
  function numberField(value, key) {
20390
- if (!isRecord9(value))
20527
+ if (!isRecord10(value))
20391
20528
  return void 0;
20392
20529
  const field = value[key];
20393
20530
  return typeof field === "number" || field === null ? field : void 0;
@@ -20426,7 +20563,7 @@ function fileMutatingKind(toolName) {
20426
20563
  return null;
20427
20564
  }
20428
20565
  function nestedToolString(value, keys) {
20429
- if (!isRecord9(value))
20566
+ if (!isRecord10(value))
20430
20567
  return void 0;
20431
20568
  for (const key of keys) {
20432
20569
  const field = value[key];
@@ -20441,14 +20578,14 @@ function nestedToolString(value, keys) {
20441
20578
  return void 0;
20442
20579
  }
20443
20580
  function patchFromResult(result) {
20444
- if (!isRecord9(result))
20581
+ if (!isRecord10(result))
20445
20582
  return void 0;
20446
20583
  for (const key of ["patch", "diff", "unifiedDiff"]) {
20447
20584
  const field = result[key];
20448
20585
  if (typeof field === "string" && field.length > 0)
20449
20586
  return field;
20450
20587
  }
20451
- if (isRecord9(result.details)) {
20588
+ if (isRecord10(result.details)) {
20452
20589
  for (const key of ["patch", "diff", "unifiedDiff"]) {
20453
20590
  const field = result.details[key];
20454
20591
  if (typeof field === "string" && field.length > 0)
@@ -22023,6 +22160,9 @@ var OmpAdapter = class {
22023
22160
  #inspectionCache = /* @__PURE__ */ new Map();
22024
22161
  #inspectionInFlight = /* @__PURE__ */ new Map();
22025
22162
  #inspections = /* @__PURE__ */ new Set();
22163
+ #availabilityInFlight = /* @__PURE__ */ new Map();
22164
+ #availabilityListings = /* @__PURE__ */ new Set();
22165
+ #listModels;
22026
22166
  #sessions = /* @__PURE__ */ new Set();
22027
22167
  #toolOutputLimit;
22028
22168
  #closePromise = null;
@@ -22031,22 +22171,21 @@ var OmpAdapter = class {
22031
22171
  createTransport: (sessionOptions) => new OmpRpcSession({ ...options, ...sessionOptions })
22032
22172
  }) {
22033
22173
  this.#createTransport = dependencies.createTransport;
22174
+ const listModels = dependencies.listModels ?? listOmpModels;
22175
+ this.#listModels = ({ cwd, signal }) => listModels({
22176
+ cwd,
22177
+ signal,
22178
+ ...options.command ? { command: options.command } : {},
22179
+ ...options.environment ? { environment: options.environment } : {}
22180
+ });
22034
22181
  this.sessionImport = new OmpSessionImport({ ...process.env, ...options.environment });
22035
22182
  this.usageStatistics = createOmpUsageStatistics({ ...process.env, ...options.environment });
22036
22183
  this.#closeTimeoutMs = options.closeTimeoutMs ?? 2e3;
22037
22184
  this.#toolOutputLimit = options.toolOutputLimit ?? DEFAULT_TOOL_OUTPUT_LIMIT;
22038
22185
  }
22039
22186
  async inspect(input = {}) {
22040
- if (this.#closePromise) {
22041
- return {
22042
- status: "unavailable",
22043
- error: {
22044
- code: "invalidState",
22045
- message: "Omp Adapter is closed",
22046
- retryable: false
22047
- }
22048
- };
22049
- }
22187
+ if (this.#closePromise)
22188
+ return closedInspection();
22050
22189
  const cwd = input.cwd ?? process.cwd();
22051
22190
  const inFlight = this.#inspectionInFlight.get(cwd);
22052
22191
  if (inFlight)
@@ -22056,6 +22195,37 @@ var OmpAdapter = class {
22056
22195
  if (cached2)
22057
22196
  return cached2;
22058
22197
  }
22198
+ if (input.purpose === "availability")
22199
+ return this.#inspectAvailability(cwd, input.refresh);
22200
+ return this.#startFullInspection(cwd);
22201
+ }
22202
+ /**
22203
+ * Availability without a Session: a Session startup runs OMP's own startup work, including
22204
+ * Provider connection prewarm, for a Harness the user may never select. The listed result
22205
+ * carries no native default Model or Thinking option, so it is neither cached nor used to
22206
+ * update `#thinkingSelectionSupported`. When the list cannot answer, a full inspection answers
22207
+ * instead, reusing one that is in flight or finished meanwhile.
22208
+ */
22209
+ async #inspectAvailability(cwd, refresh = false) {
22210
+ if (refresh)
22211
+ this.#inspectionCache.delete(cwd);
22212
+ let pending = this.#availabilityInFlight.get(cwd);
22213
+ if (!pending) {
22214
+ const listing = this.#listAvailability(cwd);
22215
+ pending = listing.finally(() => {
22216
+ if (this.#availabilityInFlight.get(cwd) === pending)
22217
+ this.#availabilityInFlight.delete(cwd);
22218
+ });
22219
+ this.#availabilityInFlight.set(cwd, pending);
22220
+ }
22221
+ const listed = await pending;
22222
+ if (listed)
22223
+ return listed;
22224
+ if (this.#closePromise)
22225
+ return closedInspection();
22226
+ return this.#inspectionInFlight.get(cwd) ?? this.#inspectionCache.get(cwd) ?? this.#startFullInspection(cwd);
22227
+ }
22228
+ #startFullInspection(cwd) {
22059
22229
  const inspection = this.#inspectCwd(cwd).then((result) => {
22060
22230
  if (result.status === "ready")
22061
22231
  this.#inspectionCache.set(cwd, result);
@@ -22068,6 +22238,59 @@ var OmpAdapter = class {
22068
22238
  }
22069
22239
  });
22070
22240
  }
22241
+ /** Returns null when a Session must answer: this OMP cannot list Models, or lists none. */
22242
+ async #listAvailability(cwd) {
22243
+ const startedAt = Date.now();
22244
+ const stage = "model-list";
22245
+ const controller = new AbortController();
22246
+ this.#availabilityListings.add(controller);
22247
+ let listed;
22248
+ try {
22249
+ listed = await this.#listModels({ cwd, signal: controller.signal });
22250
+ } finally {
22251
+ this.#availabilityListings.delete(controller);
22252
+ }
22253
+ if (listed.kind === "unanswered")
22254
+ return null;
22255
+ if (listed.kind === "notInstalled") {
22256
+ return {
22257
+ status: "notInstalled",
22258
+ error: {
22259
+ code: "notInstalled",
22260
+ message: listed.message,
22261
+ retryable: false,
22262
+ stage,
22263
+ durationMs: Date.now() - startedAt
22264
+ }
22265
+ };
22266
+ }
22267
+ if (listed.kind === "timedOut") {
22268
+ return {
22269
+ status: "error",
22270
+ error: {
22271
+ code: "unavailable",
22272
+ message: "Oh My Pi did not list its models in time",
22273
+ retryable: true,
22274
+ stage,
22275
+ durationMs: Date.now() - startedAt
22276
+ }
22277
+ };
22278
+ }
22279
+ if (listed.models.length === 0)
22280
+ return null;
22281
+ let catalog;
22282
+ try {
22283
+ catalog = normalizeOmpModelCatalog(listed.models, null, null, null);
22284
+ } catch {
22285
+ return null;
22286
+ }
22287
+ return {
22288
+ status: "ready",
22289
+ catalog,
22290
+ permissionModes: OMP_PERMISSION_MODE_CATALOG,
22291
+ capabilities: ompInspectionCapabilities(false)
22292
+ };
22293
+ }
22071
22294
  async #inspectCwd(cwd) {
22072
22295
  const startedAt = Date.now();
22073
22296
  let stage = "spawn";
@@ -22091,16 +22314,7 @@ var OmpAdapter = class {
22091
22314
  status: "ready",
22092
22315
  catalog,
22093
22316
  permissionModes: OMP_PERMISSION_MODE_CATALOG,
22094
- capabilities: {
22095
- configuration: {
22096
- selectModel: true,
22097
- selectThinkingOption: thinkingLevels !== null,
22098
- selectPermissionMode: true,
22099
- permissionModeScope: "live"
22100
- },
22101
- history: { fork: true, forkAcrossCwd: true, rollbackLastTurn: true },
22102
- subagents: { observe: true, readTranscript: true }
22103
- }
22317
+ capabilities: ompInspectionCapabilities(thinkingLevels !== null)
22104
22318
  };
22105
22319
  } catch (error51) {
22106
22320
  await transport.close().catch(() => void 0);
@@ -22311,6 +22525,8 @@ var OmpAdapter = class {
22311
22525
  }
22312
22526
  close() {
22313
22527
  if (!this.#closePromise) {
22528
+ for (const listing of this.#availabilityListings)
22529
+ listing.abort();
22314
22530
  this.#closePromise = Promise.all([
22315
22531
  this.sessionImport.close(),
22316
22532
  ...[...this.#inspections].map((transport) => transport.close()),