@codexhost/cli-win32-arm64 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +2 -2
  2. package/THIRD_PARTY_NOTICES.txt +6 -2
  3. package/app/codexhost-distribution.json +1 -1
  4. package/app/console-server.mjs +59 -21
  5. package/app/console-web.js +49 -18
  6. package/app/desktop-controller.mjs +554 -34
  7. package/app/host-runtime.mjs +3436 -1846
  8. package/app/plugins/antigravity/plugin.mjs +39 -3
  9. package/app/plugins/claude-code/plugin.mjs +16 -7
  10. package/app/plugins/codebuddy/plugin.mjs +14 -1
  11. package/app/plugins/codex-usage/plugin.mjs +4 -1
  12. package/app/plugins/copilot/plugin.mjs +4 -1
  13. package/app/plugins/cursor-cli/plugin.mjs +13 -6
  14. package/app/plugins/deepseek-harness/plugin.mjs +463 -47
  15. package/app/plugins/devin/assets/icon.svg +1 -0
  16. package/app/plugins/devin/manifest.json +30 -0
  17. package/app/plugins/devin/plugin.mjs +23363 -0
  18. package/app/plugins/enabled.json +2 -0
  19. package/app/plugins/grok/plugin.mjs +476 -392
  20. package/app/plugins/hermes/plugin.mjs +457 -197
  21. package/app/plugins/kimi-code/plugin.mjs +4 -1
  22. package/app/plugins/kiro-cli/plugin.mjs +4 -1
  23. package/app/plugins/omp/plugin.mjs +4 -1
  24. package/app/plugins/opencode/plugin.mjs +4 -1
  25. package/app/plugins/pi/plugin.mjs +1464 -412
  26. package/app/plugins/qoder/plugin.mjs +9528 -8655
  27. package/app/plugins/qoder-cn/plugin.mjs +9528 -8655
  28. package/app/plugins/qwen-code/assets/icon.svg +1 -0
  29. package/app/plugins/qwen-code/manifest.json +29 -0
  30. package/app/plugins/qwen-code/plugin.mjs +29298 -0
  31. package/app/plugins/workbuddy/plugin.mjs +14 -1
  32. package/app/plugins/zcode/plugin.mjs +4 -1
  33. package/app/renderer-extension.js +510 -114
  34. package/bin/codexhost.exe +0 -0
  35. package/libexec/codexhost-node-repl.exe +0 -0
  36. package/libexec/codexhost-shim.exe +0 -0
  37. package/libexec/codexhost-updater.exe +0 -0
  38. package/licenses/Qwen-Code-SDK-LICENSE.txt +203 -0
  39. package/package.json +2 -2
@@ -1138,10 +1138,10 @@ function mergeDefs(...defs) {
1138
1138
  function cloneDef(schema) {
1139
1139
  return mergeDefs(schema._zod.def);
1140
1140
  }
1141
- function getElementAtPath(obj, path9) {
1142
- if (!path9)
1141
+ function getElementAtPath(obj, path8) {
1142
+ if (!path8)
1143
1143
  return obj;
1144
- return path9.reduce((acc, key) => acc?.[key], obj);
1144
+ return path8.reduce((acc, key) => acc?.[key], obj);
1145
1145
  }
1146
1146
  function promiseAllObject(promisesObj) {
1147
1147
  const keys = Object.keys(promisesObj);
@@ -1550,11 +1550,11 @@ function explicitlyAborted(x, startIndex = 0) {
1550
1550
  }
1551
1551
  return false;
1552
1552
  }
1553
- function prefixIssues(path9, issues) {
1553
+ function prefixIssues(path8, issues) {
1554
1554
  return issues.map((iss) => {
1555
1555
  var _a3;
1556
1556
  (_a3 = iss).path ?? (_a3.path = []);
1557
- iss.path.unshift(path9);
1557
+ iss.path.unshift(path8);
1558
1558
  return iss;
1559
1559
  });
1560
1560
  }
@@ -1701,16 +1701,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
1701
1701
  }
1702
1702
  function formatError(error51, mapper = (issue2) => issue2.message) {
1703
1703
  const fieldErrors = { _errors: [] };
1704
- const processError = (error52, path9 = []) => {
1704
+ const processError = (error52, path8 = []) => {
1705
1705
  for (const issue2 of error52.issues) {
1706
1706
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1707
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
1707
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
1708
1708
  } else if (issue2.code === "invalid_key") {
1709
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
1709
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1710
1710
  } else if (issue2.code === "invalid_element") {
1711
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
1711
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1712
1712
  } else {
1713
- const fullpath = [...path9, ...issue2.path];
1713
+ const fullpath = [...path8, ...issue2.path];
1714
1714
  if (fullpath.length === 0) {
1715
1715
  fieldErrors._errors.push(mapper(issue2));
1716
1716
  } else {
@@ -1737,17 +1737,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
1737
1737
  }
1738
1738
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
1739
1739
  const result = { errors: [] };
1740
- const processError = (error52, path9 = []) => {
1740
+ const processError = (error52, path8 = []) => {
1741
1741
  var _a3, _b;
1742
1742
  for (const issue2 of error52.issues) {
1743
1743
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1744
- issue2.errors.map((issues) => processError({ issues }, [...path9, ...issue2.path]));
1744
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
1745
1745
  } else if (issue2.code === "invalid_key") {
1746
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
1746
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1747
1747
  } else if (issue2.code === "invalid_element") {
1748
- processError({ issues: issue2.issues }, [...path9, ...issue2.path]);
1748
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
1749
1749
  } else {
1750
- const fullpath = [...path9, ...issue2.path];
1750
+ const fullpath = [...path8, ...issue2.path];
1751
1751
  if (fullpath.length === 0) {
1752
1752
  result.errors.push(mapper(issue2));
1753
1753
  continue;
@@ -1779,8 +1779,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
1779
1779
  }
1780
1780
  function toDotPath(_path) {
1781
1781
  const segs = [];
1782
- const path9 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1783
- for (const seg of path9) {
1782
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1783
+ for (const seg of path8) {
1784
1784
  if (typeof seg === "number")
1785
1785
  segs.push(`[${seg}]`);
1786
1786
  else if (typeof seg === "symbol")
@@ -14472,13 +14472,13 @@ function resolveRef(ref, ctx) {
14472
14472
  if (!ref.startsWith("#")) {
14473
14473
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
14474
14474
  }
14475
- const path9 = ref.slice(1).split("/").filter(Boolean);
14476
- if (path9.length === 0) {
14475
+ const path8 = ref.slice(1).split("/").filter(Boolean);
14476
+ if (path8.length === 0) {
14477
14477
  return ctx.rootSchema;
14478
14478
  }
14479
14479
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
14480
- if (path9[0] === defsKey) {
14481
- const key = path9[1];
14480
+ if (path8[0] === defsKey) {
14481
+ const key = path8[1];
14482
14482
  if (!key || !ctx.defs[key]) {
14483
14483
  throw new Error(`Reference not found: ${ref}`);
14484
14484
  }
@@ -15932,7 +15932,10 @@ var harnessPluginRouteSchema = external_exports.object({
15932
15932
  harnessId: harnessPluginIdSchema,
15933
15933
  model: harnessModelRefSchema.optional(),
15934
15934
  thinkingOptionId: harnessThinkingOptionIdSchema.optional(),
15935
- permissionModeId: harnessPermissionModeIdSchema.optional()
15935
+ permissionModeId: harnessPermissionModeIdSchema.optional(),
15936
+ /** A draft prewarm create: stays unsubmitted until the user's first Turn.
15937
+ * Desktop forwards only the Model of a main-process prewarm to thread/start. */
15938
+ prewarm: external_exports.literal(true).optional()
15936
15939
  }).strict();
15937
15940
 
15938
15941
  // ../../shared-contracts/dist/harness-launch-settings.js
@@ -16617,7 +16620,35 @@ import path4 from "node:path";
16617
16620
 
16618
16621
  // dist/hermes-runtime.js
16619
16622
  import { execFile as execFile2 } from "node:child_process";
16623
+ import { statSync } from "node:fs";
16620
16624
  import { promisify } from "node:util";
16625
+ var launcherPrefixCache = /* @__PURE__ */ new Map();
16626
+ function launcherCacheKey(launcher, environment) {
16627
+ let stat;
16628
+ try {
16629
+ stat = statSync(launcher);
16630
+ } catch {
16631
+ return null;
16632
+ }
16633
+ return [
16634
+ launcher,
16635
+ stat.mtimeMs,
16636
+ stat.size,
16637
+ environment.HERMES_HOME ?? "",
16638
+ environment.PATH ?? ""
16639
+ ].join("\0");
16640
+ }
16641
+ function timeitPassthrough(script) {
16642
+ return [
16643
+ "-n",
16644
+ "1",
16645
+ "-r",
16646
+ "1",
16647
+ "-s",
16648
+ `exec(${JSON.stringify(script)}, {}); import sys, io; sys.stdout = io.StringIO()`,
16649
+ "pass"
16650
+ ];
16651
+ }
16621
16652
  function legacyHermesPythonCommand(python, script) {
16622
16653
  return {
16623
16654
  command: python,
@@ -16633,22 +16664,12 @@ ${script}`
16633
16664
  };
16634
16665
  }
16635
16666
  async function nativeHermesPythonCommand(launcher, script, environment, timeoutMs = 2e4, platform = process.platform) {
16636
- const invocation = commandInvocation(launcher, [
16637
- "--print-runtime-command",
16638
- "--module",
16639
- "timeit",
16640
- "--",
16641
- "-n",
16642
- "1",
16643
- "-r",
16644
- "1",
16645
- "-s",
16646
- // timeit is a stdlib entry point supported by the native launcher. After
16647
- // the operation returns, redirect its timing footer, preserving normal
16648
- // native cleanup and exit status without contaminating JSON/RPC stdout.
16649
- `exec(${JSON.stringify(script)}, {}); import sys, io; sys.stdout = io.StringIO()`,
16650
- "pass"
16651
- ], environment, platform);
16667
+ const passthrough = timeitPassthrough(script);
16668
+ const cacheKey = launcherCacheKey(launcher, environment);
16669
+ const cached2 = cacheKey !== null ? launcherPrefixCache.get(cacheKey) : void 0;
16670
+ if (cached2 !== void 0 && cached2.length > 0 && cached2[0])
16671
+ return { command: cached2[0], arguments: [...cached2.slice(1), ...passthrough] };
16672
+ const invocation = commandInvocation(launcher, ["--print-runtime-command", "--module", "timeit", "--", ...passthrough], environment, platform);
16652
16673
  let stdout;
16653
16674
  try {
16654
16675
  ({ stdout } = await promisify(execFile2)(invocation.command, invocation.arguments, {
@@ -16668,6 +16689,9 @@ async function nativeHermesPythonCommand(launcher, script, environment, timeoutM
16668
16689
  if (!Array.isArray(value) || value.length === 0 || !value.every((arg) => typeof arg === "string") || !value[0]) {
16669
16690
  throw new Error("Hermes launcher returned an invalid runtime command");
16670
16691
  }
16692
+ const passthroughStart = value.length - passthrough.length;
16693
+ if (cacheKey !== null && passthroughStart >= 1 && value.slice(passthroughStart).every((arg, index) => arg === passthrough[index]))
16694
+ launcherPrefixCache.set(cacheKey, value.slice(0, passthroughStart));
16671
16695
  return { command: value[0], arguments: value.slice(1) };
16672
16696
  }
16673
16697
  async function hermesPythonCommand(runtime, script, environment, timeoutMs = 2e4) {
@@ -16980,9 +17004,6 @@ function catalogModelsFromInventory(inventory) {
16980
17004
  }
16981
17005
 
16982
17006
  // dist/gateway-delegation.js
16983
- import { mkdtemp as mkdtemp2, writeFile as writeFile2, rm as rm2 } from "node:fs/promises";
16984
- import os2 from "node:os";
16985
- import path5 from "node:path";
16986
17007
  var HERMES_DELEGATION_GUIDANCE = `This Session runs inside codexhost.
16987
17008
  When the user authorizes cross-Harness delegation, discover the executable named by CODEXHOST_CLI_PATH through your native terminal tool. Read its --help and harness list, then use delegate start and thread send/read/wait/cancel as documented. Prefer --format compact. Preserve CODEXHOST_RUNTIME_ENDPOINT, CODEXHOST_RUNTIME_TOKEN and CODEXHOST_THREAD_ID in these calls: they identify the Runtime and parent Thread. Never print their values or substitute another executable. Native Hermes delegate_task remains available for Hermes subagents.`;
16988
17009
  var required2 = [
@@ -16993,48 +17014,40 @@ var required2 = [
16993
17014
  ];
16994
17015
  async function prepareGatewayDelegation(environment) {
16995
17016
  if (!required2.every((key) => environment[key]))
16996
- return { environment, bootstrap: "", dispose: async () => {
17017
+ return { environment, bootstrap: "", guidance: null, dispose: async () => {
16997
17018
  } };
16998
- const directory = await mkdtemp2(path5.join(os2.tmpdir(), "codexhost-hermes-delegation-"));
16999
- const file2 = path5.join(directory, "SKILL.md");
17000
- try {
17001
- await writeFile2(file2, `---
17002
- name: delegation
17003
- description: Discover authorized codexhost agent collaboration.
17004
- ---
17005
-
17006
- ${HERMES_DELEGATION_GUIDANCE}
17007
- `, { mode: 384 });
17008
- return {
17009
- environment: {
17010
- ...environment,
17011
- HERMES_TUI_SKILLS: [environment.HERMES_TUI_SKILLS, "codexhost-runtime:delegation"].filter(Boolean).join(",")
17012
- },
17013
- // Reserve RPC stdout before native plugin imports. The registration is
17014
- // process-local; no plugin is installed and no user config is changed.
17015
- //
17016
- // ``hermes_bootstrap`` must run before ``tui_gateway.server`` is imported:
17017
- // the gateway spawn uses ``python -I``, so PYTHONPATH and the launcher's
17018
- // activation are stripped. Hermes's own entry point (``tui_gateway/entry.py``)
17019
- // imports this module first to put the selected dependency environment on
17020
- // ``sys.path``; skipping it makes bare third-party imports (e.g.
17021
- // ``hermes_yaml`` -> ``ruamel``) fail on interpreters whose site-packages
17022
- // do not already carry Hermes's dependencies. ``hermes_bootstrap`` is
17023
- // import-safe here for the same reason entry.py relies on it: stdlib-only
17024
- // at import time, so nothing can shadow it from the launch directory.
17025
- bootstrap: `import hermes_bootstrap
17019
+ return {
17020
+ environment,
17021
+ // Reserve RPC stdout before native plugin imports.
17022
+ //
17023
+ // ``hermes_bootstrap`` must run before ``tui_gateway.server`` is imported:
17024
+ // the gateway spawn uses ``python -I``, so PYTHONPATH and the launcher's
17025
+ // activation are stripped. Hermes's own entry point (``tui_gateway/entry.py``)
17026
+ // imports this module first to put the selected dependency environment on
17027
+ // ``sys.path``; skipping it makes bare third-party imports (e.g.
17028
+ // ``hermes_yaml`` -> ``ruamel``) fail on interpreters whose site-packages
17029
+ // do not already carry Hermes's dependencies. ``hermes_bootstrap`` is
17030
+ // import-safe here for the same reason entry.py relies on it: stdlib-only
17031
+ // at import time, so nothing can shadow it from the launch directory.
17032
+ bootstrap: `import sys
17033
+ import hermes_bootstrap
17034
+ _stdout = sys.stdout # tui_gateway.server redirects sys.stdout to sys.stderr at import (stdio-entry protection); this bridge owns stdout as its RPC channel to the host
17026
17035
  from tui_gateway import server
17027
- from pathlib import Path
17028
- from hermes_cli.plugins import PluginContext, PluginManifest, get_plugin_manager
17029
- PluginContext(PluginManifest(name="codexhost-runtime"), get_plugin_manager()).register_skill(
17030
- "delegation", Path(${JSON.stringify(file2)}))
17036
+ sys.stdout = _stdout
17031
17037
  `,
17032
- dispose: () => rm2(directory, { recursive: true, force: true })
17033
- };
17034
- } catch (error51) {
17035
- await rm2(directory, { recursive: true, force: true });
17036
- throw error51;
17037
- }
17038
+ guidance: `${HERMES_DELEGATION_GUIDANCE}
17039
+
17040
+ Runtime identity for this Thread:
17041
+
17042
+ - CODEXHOST_CLI_PATH: ${environment.CODEXHOST_CLI_PATH}
17043
+ - CODEXHOST_RUNTIME_ENDPOINT: ${environment.CODEXHOST_RUNTIME_ENDPOINT}
17044
+ - CODEXHOST_RUNTIME_TOKEN: ${environment.CODEXHOST_RUNTIME_TOKEN}
17045
+ - CODEXHOST_THREAD_ID: ${environment.CODEXHOST_THREAD_ID}
17046
+
17047
+ (end of codexhost delegation note; the user's message follows)`,
17048
+ dispose: async () => {
17049
+ }
17050
+ };
17038
17051
  }
17039
17052
 
17040
17053
  // dist/hermes-transport.js
@@ -17068,8 +17081,87 @@ function gatewayRecord(value) {
17068
17081
  function gatewayString(value) {
17069
17082
  return typeof value === "string" ? value : "";
17070
17083
  }
17071
- var GATEWAY_LAUNCH = `import runpy
17072
- runpy.run_module('tui_gateway.entry', run_name='__main__')`;
17084
+ var GATEWAY_LAUNCH = `import asyncio
17085
+ import sys
17086
+
17087
+
17088
+ async def _bridge():
17089
+ from hermes_constants import get_hermes_home
17090
+ from hermes_cli.gateway_client import _session_ticket, gateway_ws_target
17091
+ from hermes_cli.gateway_runtime import (
17092
+ attachable,
17093
+ ensure_gateway_runtime,
17094
+ gateway_profile_home,
17095
+ )
17096
+ from gateway.session_contract import CANONICAL_GATEWAY_PROTOCOL
17097
+ from websockets.asyncio.client import connect
17098
+
17099
+ home = gateway_profile_home(get_hermes_home())
17100
+ result = await asyncio.to_thread(ensure_gateway_runtime, home, idle_exit=True)
17101
+ if not attachable(result):
17102
+ detail = f" ({result.detail})" if result.detail else ""
17103
+ raise SystemExit(
17104
+ f"[bridge-exit] gateway not attachable: {result.state} "
17105
+ f"{result.reason_code or 'not ready'}{detail}"
17106
+ )
17107
+ endpoint = result.endpoint
17108
+ ticket = await asyncio.to_thread(_session_ticket, home, endpoint)
17109
+ url, protocols = gateway_ws_target(endpoint, ticket)
17110
+
17111
+ async with connect(
17112
+ url, subprotocols=protocols, open_timeout=10, max_size=8 * 1024 * 1024, proxy=None
17113
+ ) as ws:
17114
+ if protocols and ws.subprotocol != CANONICAL_GATEWAY_PROTOCOL:
17115
+ raise SystemExit("[bridge-exit] gateway protocol mismatch; update/restart required")
17116
+
17117
+ loop = asyncio.get_running_loop()
17118
+ # A full prompt rides on ONE JSON-RPC line; StreamReader's default 64 KiB
17119
+ # line limit aborts the bridge on large pastes (LimitOverrunError), so the
17120
+ # stdin side matches the WebSocket side's 8 MiB frame ceiling.
17121
+ reader = asyncio.StreamReader(limit=8 * 1024 * 1024)
17122
+ await loop.connect_read_pipe(
17123
+ lambda: asyncio.StreamReaderProtocol(reader), sys.stdin
17124
+ )
17125
+
17126
+ async def pump_in():
17127
+ async for raw in reader:
17128
+ line = raw.decode("utf-8", "replace").strip()
17129
+ if line:
17130
+ await ws.send(line)
17131
+
17132
+ async def pump_out():
17133
+ async for raw in ws:
17134
+ if isinstance(raw, bytes):
17135
+ raw = raw.decode("utf-8", "replace")
17136
+ sys.stdout.write(raw + "\\n")
17137
+ sys.stdout.flush()
17138
+
17139
+ pump_out_task = asyncio.create_task(pump_out())
17140
+ pump_in_task = asyncio.create_task(pump_in())
17141
+ # stdin EOF (host closed us) or socket close (gateway went away): end both.
17142
+ done, _pending = await asyncio.wait(
17143
+ {pump_in_task, pump_out_task}, return_when=asyncio.FIRST_COMPLETED
17144
+ )
17145
+ for task in (pump_in_task, pump_out_task):
17146
+ task.cancel()
17147
+ for task in done:
17148
+ if task.cancelled():
17149
+ continue
17150
+ exc = task.exception()
17151
+ if exc is not None:
17152
+ raise exc
17153
+
17154
+
17155
+ # No __main__ guard: the native launcher spawns this through exec(script, {})
17156
+ # whose fresh globals lack __name__; top-level code runs in both the legacy
17157
+ # (-c) and native (exec) spawn forms.
17158
+ try:
17159
+ asyncio.run(_bridge())
17160
+ except SystemExit:
17161
+ raise
17162
+ except Exception as exc:
17163
+ raise SystemExit(f"[bridge-exit] bridge failed: {type(exc).__name__}: {exc}")
17164
+ `;
17073
17165
  var HermesGatewayTransport = class _HermesGatewayTransport {
17074
17166
  runtime;
17075
17167
  cwd;
@@ -17096,6 +17188,18 @@ var HermesGatewayTransport = class _HermesGatewayTransport {
17096
17188
  this.environment = environment;
17097
17189
  this.timeoutMs = timeoutMs;
17098
17190
  }
17191
+ /** Model-facing delegation guidance for this Thread, or null without delegation. */
17192
+ get delegationGuidance() {
17193
+ return this.#delegation?.guidance ?? null;
17194
+ }
17195
+ /** Whether start() already ran (a warm transport skips prepare/start on reuse). */
17196
+ get started() {
17197
+ return this.#startRequested;
17198
+ }
17199
+ /** Whether the bridge child is gone; a closed transport cannot be reused. */
17200
+ get closed() {
17201
+ return this.#closed;
17202
+ }
17099
17203
  static async probe(executable, cwd, environment) {
17100
17204
  const override = environment.CODEXHOST_HERMES_GATEWAY_PYTHON;
17101
17205
  if (override) {
@@ -17608,7 +17712,7 @@ async function resolveHermesSessionCandidate(input) {
17608
17712
  }
17609
17713
 
17610
17714
  // dist/command.js
17611
- import path6 from "node:path";
17715
+ import path5 from "node:path";
17612
17716
  var HermesExecutableError = class extends Error {
17613
17717
  code = "HERMES_NOT_FOUND";
17614
17718
  };
@@ -17631,7 +17735,62 @@ function resolveHermesExecutable(input = {}) {
17631
17735
  });
17632
17736
  if (!resolution)
17633
17737
  throw new HermesExecutableError("Hermes CLI is not installed");
17634
- return targetPath(platform).isAbsolute(resolution.executable) ? resolution.executable : path6.resolve(resolution.executable);
17738
+ return targetPath(platform).isAbsolute(resolution.executable) ? resolution.executable : path5.resolve(resolution.executable);
17739
+ }
17740
+
17741
+ // dist/gateway-transport-pool.js
17742
+ function sameRuntime(a, b) {
17743
+ if (typeof a === "string" || typeof b === "string")
17744
+ return a === b;
17745
+ return a.launcher === b.launcher;
17746
+ }
17747
+ var ENVIRONMENT_IDENTITY_KEYS = ["HERMES_HOME", "HERMES_TUI_SKILLS"];
17748
+ function environmentIdentity(environment) {
17749
+ return ENVIRONMENT_IDENTITY_KEYS.map((key) => `${key}=${environment[key] ?? ""}`).join("\0");
17750
+ }
17751
+ function sameBridge(transport, runtime, cwd, environment) {
17752
+ return sameRuntime(transport.runtime, runtime) && transport.cwd === cwd && environmentIdentity(transport.environment) === environmentIdentity(environment);
17753
+ }
17754
+ var GatewayTransportPool = class {
17755
+ #idle = [];
17756
+ #capacity;
17757
+ #closed = false;
17758
+ constructor(capacity = 2) {
17759
+ this.#capacity = Math.max(1, capacity);
17760
+ }
17761
+ /** A healthy started bridge equivalent to a fresh spawn for these inputs, or null. */
17762
+ acquire(runtime, cwd, environment) {
17763
+ while (this.#idle.length > 0) {
17764
+ const index = this.#idle.findIndex((transport2) => sameBridge(transport2, runtime, cwd, environment));
17765
+ const transport = index >= 0 ? this.#idle.splice(index, 1)[0] : void 0;
17766
+ if (transport === void 0)
17767
+ return null;
17768
+ if (!transport.closed && transport.started)
17769
+ return transport;
17770
+ void transport.close().catch(() => void 0);
17771
+ }
17772
+ return null;
17773
+ }
17774
+ /** Return a bridge after its session settled; unhealthy or late ones are discarded. */
17775
+ release(transport) {
17776
+ if (transport.closed || this.#closed || this.#idle.length >= this.#capacity) {
17777
+ void transport.close().catch(() => void 0);
17778
+ return;
17779
+ }
17780
+ this.#idle.push(transport);
17781
+ }
17782
+ async close() {
17783
+ this.#closed = true;
17784
+ const transports = this.#idle;
17785
+ this.#idle = [];
17786
+ await Promise.all(transports.map((transport) => transport.close().catch(() => void 0)));
17787
+ }
17788
+ };
17789
+ async function startGatewayTransport(runtime, cwd, environment, timeoutMs) {
17790
+ const transport = new HermesGatewayTransport(runtime, cwd, environment, timeoutMs);
17791
+ await transport.prepareSession();
17792
+ await transport.start();
17793
+ return transport;
17635
17794
  }
17636
17795
 
17637
17796
  // dist/gateway-history.js
@@ -17721,21 +17880,24 @@ def capture(db):
17721
17880
  boundaries[str(row['id'])] = {'count': end, 'digest': digest(active[:end])}
17722
17881
  return exported, {'rows': rows, 'derivable': derivable, 'boundaries': boundaries, 'physicalSessionId': sid}
17723
17882
 
17883
+ def ensure_session_row(db):
17884
+ if db.get_session(sid) is None:
17885
+ from hermes_constants import parse_reasoning_effort
17886
+ initial = {}
17887
+ if isinstance(p.get('provider'), str) and p['provider']: initial['provider'] = p['provider']
17888
+ if isinstance(p.get('model'), str) and p['model']: initial['model'] = p['model']
17889
+ if isinstance(p.get('reasoningEffort'), str):
17890
+ reasoning = parse_reasoning_effort(p['reasoningEffort'])
17891
+ if reasoning is not None: initial['reasoning_config'] = reasoning
17892
+ if isinstance(p.get('yolo'), bool): initial['yolo_mode'] = p['yolo']
17893
+ db.create_session(session_id=sid, source='cli', cwd=p['cwd'], model=p.get('model'), model_config=initial)
17894
+ return {'created': True}
17895
+
17724
17896
  try:
17725
17897
  if p['operation'] == 'ensure':
17726
17898
  db = SessionDB()
17727
17899
  try:
17728
- if db.get_session(sid) is None:
17729
- from hermes_constants import parse_reasoning_effort
17730
- initial = {}
17731
- if isinstance(p.get('provider'), str) and p['provider']: initial['provider'] = p['provider']
17732
- if isinstance(p.get('model'), str) and p['model']: initial['model'] = p['model']
17733
- if isinstance(p.get('reasoningEffort'), str):
17734
- reasoning = parse_reasoning_effort(p['reasoningEffort'])
17735
- if reasoning is not None: initial['reasoning_config'] = reasoning
17736
- if isinstance(p.get('yolo'), bool): initial['yolo_mode'] = p['yolo']
17737
- db.create_session(session_id=sid, source='cli', cwd=p['cwd'], model=p.get('model'), model_config=initial)
17738
- result = {'created': True}
17900
+ result = ensure_session_row(db)
17739
17901
  finally: db.close()
17740
17902
  elif p['operation'] == 'discard':
17741
17903
  child = p['derivedSessionId']
@@ -18103,28 +18265,6 @@ var HermesUsage = class {
18103
18265
  }
18104
18266
  };
18105
18267
 
18106
- // dist/gateway-configuration.js
18107
- import { execFile as execFile4 } from "node:child_process";
18108
- import { promisify as promisify3 } from "node:util";
18109
- async function resolveGatewayModel(transport, modelId) {
18110
- if (/\s/u.test(modelId) || modelId.startsWith("-"))
18111
- throw new Error("Invalid Hermes Model identifier");
18112
- const command = await hermesPythonCommand(transport.runtime, `import json
18113
- from hermes_cli.models import parse_model_input
18114
- p,m=parse_model_input(${JSON.stringify(modelId)}, '')
18115
- print(json.dumps({'provider':p,'model':m}))`, { ...process.env, ...transport.environment });
18116
- const result = await promisify3(execFile4)(command.command, command.arguments, {
18117
- cwd: transport.cwd,
18118
- env: { ...process.env, ...transport.environment },
18119
- timeout: 2e4,
18120
- maxBuffer: 1024 * 1024
18121
- });
18122
- const value = JSON.parse(result.stdout.trim());
18123
- if (!value || typeof value !== "object" || !("provider" in value) || !("model" in value) || typeof value.provider !== "string" || typeof value.model !== "string")
18124
- throw new Error("Hermes returned an invalid Model choice");
18125
- return { provider: value.provider, model: value.model };
18126
- }
18127
-
18128
18268
  // dist/gateway-session-transport.js
18129
18269
  var hermesGatewayThinkingOptions = [
18130
18270
  "none",
@@ -18156,14 +18296,14 @@ function gatewayDiff(text2) {
18156
18296
  if (!text2 || text2.length > 1024 * 1024)
18157
18297
  return [];
18158
18298
  const result = [];
18159
- let path9 = "";
18299
+ let path8 = "";
18160
18300
  let inHunk = false;
18161
18301
  let oldLines = [], newLines = [];
18162
18302
  const flush = () => {
18163
- if (path9)
18303
+ if (path8)
18164
18304
  result.push({
18165
18305
  type: "diff",
18166
- path: path9,
18306
+ path: path8,
18167
18307
  oldText: oldLines.join("\n"),
18168
18308
  newText: newLines.join("\n")
18169
18309
  });
@@ -18176,24 +18316,24 @@ function gatewayDiff(text2) {
18176
18316
  if (header) {
18177
18317
  flush();
18178
18318
  inHunk = false;
18179
- path9 = (header[2] === "/dev/null" ? header[1] ?? "" : header[2] ?? "").replace(/^[ab]\//u, "");
18319
+ path8 = (header[2] === "/dev/null" ? header[1] ?? "" : header[2] ?? "").replace(/^[ab]\//u, "");
18180
18320
  continue;
18181
18321
  }
18182
18322
  if (line.startsWith("--- ") && !inHunk) {
18183
18323
  flush();
18184
- path9 = line.slice(4).replace(/^a\//u, "");
18324
+ path8 = line.slice(4).replace(/^a\//u, "");
18185
18325
  } else if (line.startsWith("+++ ") && !inHunk) {
18186
18326
  const next = line.slice(4);
18187
18327
  if (next !== "/dev/null")
18188
- path9 = next.replace(/^b\//u, "");
18328
+ path8 = next.replace(/^b\//u, "");
18189
18329
  } else if (line.startsWith("@@")) {
18190
18330
  inHunk = true;
18191
18331
  continue;
18192
- } else if (path9 && line.startsWith("-"))
18332
+ } else if (path8 && line.startsWith("-"))
18193
18333
  oldLines.push(line.slice(1));
18194
- else if (path9 && line.startsWith("+"))
18334
+ else if (path8 && line.startsWith("+"))
18195
18335
  newLines.push(line.slice(1));
18196
- else if (path9 && line.startsWith(" ")) {
18336
+ else if (path8 && line.startsWith(" ")) {
18197
18337
  oldLines.push(line.slice(1));
18198
18338
  newLines.push(line.slice(1));
18199
18339
  }
@@ -18217,7 +18357,7 @@ var HermesGatewaySessionTransport = class {
18217
18357
  return this.#commands;
18218
18358
  }
18219
18359
  async getCommands() {
18220
- const params = { session_id: this.sessionId, cwd: this.transport.cwd };
18360
+ const params = { session_id: this.sessionId };
18221
18361
  const [response, catalog] = await Promise.all([
18222
18362
  this.transport.request("complete.slash", { ...params, text: "/" }, this.transport.timeoutMs, false),
18223
18363
  this.transport.request("commands.catalog", params, this.transport.timeoutMs, false)
@@ -18242,13 +18382,29 @@ var HermesGatewaySessionTransport = class {
18242
18382
  history;
18243
18383
  #active = null;
18244
18384
  #requests = /* @__PURE__ */ new Map();
18385
+ /** Open approval server requests by their payload request_id, for approval.cancelled. */
18386
+ #approvalRequests = /* @__PURE__ */ new Map();
18387
+ /** Pending approval prompts adopted from the create/resume snapshot. */
18388
+ #adoptedPrompts = [];
18245
18389
  #closed = false;
18246
18390
  #info;
18247
- constructor(transport, sessionId, nativeSessionId, info) {
18391
+ #revision = null;
18392
+ #generation = null;
18393
+ #subscriptionId = null;
18394
+ /** Thread's delegation guidance, injected as api_text on the first turn only. */
18395
+ #delegationGuidance;
18396
+ constructor(transport, sessionId, nativeSessionId, info, bookkeeping = {}, delegationGuidance = null) {
18248
18397
  this.transport = transport;
18249
18398
  this.sessionId = sessionId;
18250
18399
  this.nativeSessionId = nativeSessionId;
18251
18400
  this.#info = info;
18401
+ this.#delegationGuidance = delegationGuidance;
18402
+ if (typeof bookkeeping.revision === "number")
18403
+ this.#revision = bookkeeping.revision;
18404
+ if (typeof bookkeeping.generation === "number")
18405
+ this.#generation = bookkeeping.generation;
18406
+ if (typeof bookkeeping.subscriptionId === "string")
18407
+ this.#subscriptionId = bookkeeping.subscriptionId;
18252
18408
  this.history = new HermesGatewayHistory({
18253
18409
  python: transport.runtime,
18254
18410
  cwd: transport.cwd,
@@ -18309,17 +18465,24 @@ var HermesGatewaySessionTransport = class {
18309
18465
  return optionId;
18310
18466
  }
18311
18467
  async setModel(modelId) {
18312
- const choice = await resolveGatewayModel(this.transport, modelId);
18313
- const result = await this.transport.request("config.set", {
18314
- key: "model",
18315
- value: choice.provider ? `${choice.model} --provider ${choice.provider}` : modelId,
18316
- scope: "session",
18317
- session_id: this.sessionId
18468
+ const revision = this.#revision;
18469
+ const generation = this.#generation;
18470
+ if (revision === null || generation === null)
18471
+ throw new Error("Hermes gateway Session revision is not known yet");
18472
+ const result = await this.transport.request("session.mutate", {
18473
+ session_id: this.sessionId,
18474
+ request_id: `codexhost-model-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
18475
+ expected_revision: revision,
18476
+ expected_generation: generation,
18477
+ operation: "model",
18478
+ payload: { model: modelId }
18318
18479
  });
18319
- if (result.confirm_required === true)
18320
- throw new Error(gatewayString(result.confirm_message) || "Hermes requires confirmation for this Model");
18321
- const actualModel = gatewayString(this.#info.model);
18322
- const actualProvider = gatewayString(this.#info.provider);
18480
+ if (typeof result.revision === "number")
18481
+ this.#revision = result.revision;
18482
+ if (typeof result.execution_generation === "number")
18483
+ this.#generation = result.execution_generation;
18484
+ const actualModel = gatewayString(result.model);
18485
+ const actualProvider = gatewayString(result.provider);
18323
18486
  if (modelId !== actualModel && modelId !== `${actualProvider}:${actualModel}`)
18324
18487
  throw new Error("Hermes did not confirm the requested Model");
18325
18488
  }
@@ -18329,11 +18492,11 @@ var HermesGatewaySessionTransport = class {
18329
18492
  const result = await this.transport.request("config.set", {
18330
18493
  key: "yolo",
18331
18494
  value: modeId === "dont_ask" ? "on" : "off",
18332
- scope: "session",
18333
18495
  session_id: this.sessionId
18334
18496
  });
18335
- if (result.value !== (modeId === "dont_ask" ? "1" : "0") || this.#info.yolo !== (modeId === "dont_ask"))
18497
+ if (result.value !== (modeId === "dont_ask" ? "1" : "0"))
18336
18498
  throw new Error("Hermes effective approval policy differs from the requested mode; check native global approvals policy");
18499
+ this.#info = { ...this.#info, yolo: modeId === "dont_ask" };
18337
18500
  }
18338
18501
  nativeCommandName(text2) {
18339
18502
  return hermesGatewayCommandName(text2, this.#commands);
@@ -18356,9 +18519,11 @@ var HermesGatewaySessionTransport = class {
18356
18519
  reject: rejectResult,
18357
18520
  streamed: "",
18358
18521
  reasoning: "",
18359
- toolIds: /* @__PURE__ */ new Set()
18522
+ toolIds: /* @__PURE__ */ new Set(),
18523
+ admissionId: null
18360
18524
  };
18361
18525
  this.#active = active;
18526
+ void this.#adoptedPrompts.splice(0).map((prompt) => void this.#respondAdoptedApproval(prompt, active).catch(() => void 0));
18362
18527
  const commandText = text2.trim();
18363
18528
  const nativeCommand = this.nativeCommandName(text2);
18364
18529
  try {
@@ -18397,12 +18562,21 @@ var HermesGatewaySessionTransport = class {
18397
18562
  emit({ type: "agent.text", text: response.output });
18398
18563
  active.resolve({ stopReason: "end_turn" });
18399
18564
  } else {
18565
+ const guidance = this.#delegationGuidance;
18566
+ this.#delegationGuidance = null;
18400
18567
  const submitted = await this.transport.request("prompt.submit", {
18401
18568
  session_id: this.sessionId,
18402
- text: text2
18569
+ text: text2,
18570
+ submission_id: `codexhost-turn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
18571
+ clarify_batch: true,
18572
+ ...guidance ? { api_text: `${guidance}
18573
+
18574
+ ${text2}` } : {}
18403
18575
  });
18404
- if (submitted.status !== "streaming")
18405
- this.#fault(new Error(`Hermes did not start an exclusive turn (${String(submitted.status)})`));
18576
+ if (submitted.status !== "queued" && submitted.status !== "started")
18577
+ this.#fault(new Error(`Hermes did not accept the turn (${String(submitted.status)})`));
18578
+ else if (active.admissionId === null && typeof submitted.admission_id === "string")
18579
+ active.admissionId = submitted.admission_id;
18406
18580
  }
18407
18581
  } catch (error51) {
18408
18582
  active.reject(error51 instanceof Error ? error51 : new Error(String(error51)));
@@ -18418,7 +18592,12 @@ var HermesGatewaySessionTransport = class {
18418
18592
  }
18419
18593
  }
18420
18594
  async cancel() {
18421
- await this.transport.request("session.interrupt", { session_id: this.sessionId });
18595
+ if (this.#generation === null)
18596
+ return;
18597
+ await this.transport.request("session.interrupt", {
18598
+ session_id: this.sessionId,
18599
+ execution_generation: this.#generation
18600
+ });
18422
18601
  }
18423
18602
  async close() {
18424
18603
  if (this.#closed)
@@ -18429,10 +18608,13 @@ var HermesGatewaySessionTransport = class {
18429
18608
  this.#requests.clear();
18430
18609
  this.#active?.reject(new Error("Hermes gateway Session closed"));
18431
18610
  try {
18432
- await this.transport.request("session.close", { session_id: this.sessionId });
18611
+ if (this.#subscriptionId !== null)
18612
+ await this.transport.request("session.detach", {
18613
+ session_id: this.sessionId,
18614
+ subscription_id: this.#subscriptionId
18615
+ });
18433
18616
  } catch {
18434
18617
  }
18435
- await this.transport.close();
18436
18618
  }
18437
18619
  #fault(error51) {
18438
18620
  if (this.#closed)
@@ -18452,6 +18634,10 @@ var HermesGatewaySessionTransport = class {
18452
18634
  const payload = gatewayRecord(event.payload);
18453
18635
  if (event.type === "session.info") {
18454
18636
  this.#info = payload;
18637
+ if (typeof payload.revision === "number")
18638
+ this.#revision = payload.revision;
18639
+ if (typeof payload.execution_generation === "number")
18640
+ this.#generation = payload.execution_generation;
18455
18641
  return;
18456
18642
  }
18457
18643
  if (event.type === "session.usage") {
@@ -18463,12 +18649,26 @@ var HermesGatewaySessionTransport = class {
18463
18649
  controller?.abort(payload.reason === "timeout" ? "expired" : "cancelled");
18464
18650
  return;
18465
18651
  }
18652
+ if (event.type === "approval.cancelled") {
18653
+ const ids = Array.isArray(payload.request_ids) ? payload.request_ids.filter((id) => typeof id === "string") : [];
18654
+ for (const id of ids) {
18655
+ this.#approvalRequests.get(id)?.abort("cancelled");
18656
+ this.#approvalRequests.delete(id);
18657
+ }
18658
+ return;
18659
+ }
18466
18660
  const active = this.#active;
18467
18661
  if (!active) {
18468
18662
  if (event.type === "error")
18469
18663
  this.#fault(new Error(gatewayString(payload.message)));
18470
18664
  return;
18471
18665
  }
18666
+ const eventAdmission = gatewayString(event.admission_id);
18667
+ const eventType = gatewayString(event.type);
18668
+ if (eventAdmission && active.admissionId && eventAdmission !== active.admissionId) {
18669
+ if (eventType.startsWith("message.") || eventType.startsWith("reasoning."))
18670
+ return;
18671
+ }
18472
18672
  const text2 = gatewayString(payload.text);
18473
18673
  if (event.type === "message.delta") {
18474
18674
  active.streamed += text2;
@@ -18599,27 +18799,80 @@ var HermesGatewaySessionTransport = class {
18599
18799
  label: choice === "session" ? "Allow for this session" : "Always allow"
18600
18800
  }
18601
18801
  ] : choice === "deny" ? [{ id: "deny", effect: "deny", label: "Deny" }] : []);
18602
- const response = await active.permission({
18603
- signal: controller.signal,
18604
- title: gatewayString(params.description) || gatewayString(params.command),
18605
- description: gatewayString(params.command) || gatewayString(params.description),
18606
- options
18607
- });
18608
- if (!controller.signal.aborted)
18609
- this.transport.respond(id, {
18610
- choice: response ?? "deny"
18802
+ const payloadRequestId = gatewayString(params.request_id);
18803
+ if (payloadRequestId)
18804
+ this.#approvalRequests.set(payloadRequestId, controller);
18805
+ try {
18806
+ const response = await active.permission({
18807
+ signal: controller.signal,
18808
+ title: gatewayString(params.description) || gatewayString(params.command),
18809
+ description: gatewayString(params.command) || gatewayString(params.description),
18810
+ options
18611
18811
  });
18812
+ if (!controller.signal.aborted)
18813
+ this.transport.respond(id, {
18814
+ choice: response ?? "deny"
18815
+ });
18816
+ } finally {
18817
+ if (payloadRequestId)
18818
+ this.#approvalRequests.delete(payloadRequestId);
18819
+ }
18612
18820
  } else
18613
18821
  this.transport.rejectRequest(id);
18614
18822
  } finally {
18615
18823
  this.#requests.delete(id);
18616
18824
  }
18617
18825
  }
18826
+ /** Keep approval prompts from the create/resume snapshot for the next turn. */
18827
+ adoptPendingPrompts(prompts) {
18828
+ if (!Array.isArray(prompts))
18829
+ return;
18830
+ for (const entry of prompts) {
18831
+ const prompt = gatewayRecord(entry);
18832
+ if (gatewayString(prompt.kind) === "approval" && gatewayString(prompt.prompt_id))
18833
+ this.#adoptedPrompts.push(prompt);
18834
+ }
18835
+ }
18836
+ /**
18837
+ * Answer a snapshot-adopted approval through canonical `approval.respond`.
18838
+ * Clarify prompts are deliberately not answered here: a live clarify
18839
+ * question is served by the `clarify` server request (batch mode), and the
18840
+ * gateway re-issues still-open server requests to an attaching client.
18841
+ */
18842
+ async #respondAdoptedApproval(prompt, active) {
18843
+ if (this.#closed)
18844
+ return;
18845
+ const promptId = gatewayString(prompt.prompt_id);
18846
+ const generation = prompt.execution_generation;
18847
+ const choices = Array.isArray(prompt.choices) ? prompt.choices.filter((choice) => typeof choice === "string") : ["once", "deny"];
18848
+ const options = choices.flatMap((choice) => choice === "once" ? [{ id: "once", effect: "allowOnce", label: "Allow once" }] : choice === "session" || choice === "always" ? [
18849
+ {
18850
+ id: choice,
18851
+ effect: choice === "session" ? "allowForSession" : "allowAlways",
18852
+ label: choice === "session" ? "Allow for this session" : "Always allow"
18853
+ }
18854
+ ] : choice === "deny" ? [{ id: "deny", effect: "deny", label: "Deny" }] : []);
18855
+ const response = await active.permission({
18856
+ signal: new AbortController().signal,
18857
+ title: gatewayString(prompt.description) || gatewayString(prompt.command) || "Hermes approval requested before this Session attached",
18858
+ description: gatewayString(prompt.command) || gatewayString(prompt.description),
18859
+ options
18860
+ });
18861
+ if (this.#closed)
18862
+ return;
18863
+ if (typeof generation === "number")
18864
+ await this.transport.request("approval.respond", {
18865
+ session_id: this.sessionId,
18866
+ execution_generation: generation,
18867
+ prompt_id: promptId,
18868
+ choice: response ?? "deny"
18869
+ }, this.transport.timeoutMs, false);
18870
+ }
18618
18871
  };
18619
18872
 
18620
18873
  // dist/usage-statistics.js
18621
- import os3 from "node:os";
18622
- import path7 from "node:path";
18874
+ import os2 from "node:os";
18875
+ import path6 from "node:path";
18623
18876
  import { lstat } from "node:fs/promises";
18624
18877
 
18625
18878
  // ../../harness-adapter/dist/usage-statistics.js
@@ -18679,9 +18932,9 @@ function usageEntryFromRequest(request, occurredAtMs) {
18679
18932
 
18680
18933
  // dist/usage-statistics.js
18681
18934
  function hermesStateDatabasePath(environment) {
18682
- const home = environment.HOME || environment.USERPROFILE || os3.homedir();
18935
+ const home = environment.HOME || environment.USERPROFILE || os2.homedir();
18683
18936
  const configured = environment.HERMES_HOME;
18684
- return path7.resolve(configured ? path7.join(configured, "state.db") : path7.join(home, ".hermes", "state.db"));
18937
+ return path6.resolve(configured ? path6.join(configured, "state.db") : path6.join(home, ".hermes", "state.db"));
18685
18938
  }
18686
18939
  async function fingerprint(file2) {
18687
18940
  const info = await lstat(file2).catch(() => null);
@@ -18763,7 +19016,7 @@ function createHermesUsageStatistics(environment) {
18763
19016
  }
18764
19017
 
18765
19018
  // dist/gateway-open.js
18766
- import path8 from "node:path";
19019
+ import path7 from "node:path";
18767
19020
  import { realpath } from "node:fs/promises";
18768
19021
 
18769
19022
  // dist/hermes-session.js
@@ -18973,16 +19226,16 @@ var Diff = class {
18973
19226
  }
18974
19227
  }
18975
19228
  }
18976
- addToPath(path9, added, removed, oldPosInc, options) {
18977
- const last = path9.lastComponent;
19229
+ addToPath(path8, added, removed, oldPosInc, options) {
19230
+ const last = path8.lastComponent;
18978
19231
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
18979
19232
  return {
18980
- oldPos: path9.oldPos + oldPosInc,
19233
+ oldPos: path8.oldPos + oldPosInc,
18981
19234
  lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
18982
19235
  };
18983
19236
  } else {
18984
19237
  return {
18985
- oldPos: path9.oldPos + oldPosInc,
19238
+ oldPos: path8.oldPos + oldPosInc,
18986
19239
  lastComponent: { count: 1, added, removed, previousComponent: last }
18987
19240
  };
18988
19241
  }
@@ -19297,8 +19550,8 @@ function hermesFileChanges(update) {
19297
19550
  for (const block of update.content ?? []) {
19298
19551
  if (block.type !== "diff")
19299
19552
  continue;
19300
- const { path: path9, oldText, newText } = block;
19301
- if (!path9.trim() || /[\0\r\n]/u.test(path9) || typeof newText !== "string")
19553
+ const { path: path8, oldText, newText } = block;
19554
+ if (!path8.trim() || /[\0\r\n]/u.test(path8) || typeof newText !== "string")
19302
19555
  continue;
19303
19556
  if (oldText === newText)
19304
19557
  continue;
@@ -19306,10 +19559,10 @@ function hermesFileChanges(update) {
19306
19559
  if (bytes > MAX_DIFF_BYTES || changes.length === MAX_DIFFS)
19307
19560
  return [];
19308
19561
  changes.push({
19309
- path: path9,
19562
+ path: path8,
19310
19563
  kind: "update",
19311
19564
  diffScope: "fragment",
19312
- unifiedDiff: createTwoFilesPatch(path9, path9, oldText ?? "", newText, "", "", {
19565
+ unifiedDiff: createTwoFilesPatch(path8, path8, oldText ?? "", newText, "", "", {
19313
19566
  context: 3
19314
19567
  })
19315
19568
  });
@@ -20114,7 +20367,7 @@ ${reason}` }
20114
20367
 
20115
20368
  // dist/gateway-open.js
20116
20369
  async function sameDirectory(left, right) {
20117
- return await realpath(left).catch(() => path8.resolve(left)) === await realpath(right).catch(() => path8.resolve(right));
20370
+ return await realpath(left).catch(() => path7.resolve(left)) === await realpath(right).catch(() => path7.resolve(right));
20118
20371
  }
20119
20372
  var gatewayCapabilities = {
20120
20373
  configuration: {
@@ -20176,8 +20429,10 @@ async function openGatewaySession(input, transport, onSettle, inheritedPermissio
20176
20429
  let nativeRef = source;
20177
20430
  let sourceHistory;
20178
20431
  try {
20179
- await transport.prepareSession();
20180
- await transport.start();
20432
+ if (!transport.started) {
20433
+ await transport.prepareSession();
20434
+ await transport.start();
20435
+ }
20181
20436
  if (source && (input.kind === "fork" || input.kind === "rollbackLastTurn")) {
20182
20437
  sourceHistory = new HermesGatewayHistory({
20183
20438
  python: transport.runtime,
@@ -20187,34 +20442,33 @@ async function openGatewaySession(input, transport, onSettle, inheritedPermissio
20187
20442
  });
20188
20443
  nativeRef = await sourceHistory.derive(input.kind === "fork" ? { checkpoint: input.checkpoint } : { rollbackLastTurn: true });
20189
20444
  }
20190
- const choice = model2 ? await resolveGatewayModel(transport, model2) : null;
20191
- const response = await transport.request(nativeRef ? "session.resume" : "session.create", nativeRef ? { session_id: nativeRef.nativeSessionId, eager_build: true, omit_messages: true } : {
20192
- cwd: input.cwd,
20193
- close_on_disconnect: true,
20194
- ...choice ? choice : {},
20195
- ...input.kind === "create" && input.thinkingOptionId ? { reasoning_effort: input.thinkingOptionId } : {}
20196
- });
20445
+ const response = await transport.request(
20446
+ nativeRef ? "session.resume" : "session.create",
20447
+ // Canonical wire (hermes-gateway-v1, CanonicalCreateParams/CanonicalResumeParams):
20448
+ // no close_on_disconnect / eager_build keys, and the effort key is "reasoning".
20449
+ nativeRef ? { session_id: nativeRef.nativeSessionId, omit_messages: true } : {
20450
+ cwd: input.cwd,
20451
+ ...model2 ? { model: model2 } : {},
20452
+ ...input.kind === "create" && input.thinkingOptionId ? { reasoning: input.thinkingOptionId } : {}
20453
+ }
20454
+ );
20197
20455
  const runtimeId = gatewayString(response.session_id);
20198
20456
  const storedId = nativeRef?.nativeSessionId || gatewayString(response.stored_session_id);
20199
20457
  if (!runtimeId || !storedId)
20200
20458
  throw new Error("Hermes gateway did not return Session identity");
20201
- let info = gatewayRecord(response.info);
20202
- if (info.lazy === true)
20203
- info = await transport.waitForSession(runtimeId);
20459
+ const info = gatewayRecord(response.info);
20204
20460
  const nativeCwd = gatewayString(info.cwd);
20205
20461
  if (nativeCwd && !await sameDirectory(nativeCwd, input.cwd))
20206
20462
  throw new HermesGatewayHistoryError("unsupported", "Hermes resumed a different native working directory");
20207
- const bridge = new HermesGatewaySessionTransport(transport, runtimeId, storedId, info);
20208
- if (!nativeRef)
20209
- await bridge.history.ensureCreated({
20210
- cwd: input.cwd,
20211
- model: gatewayString(info.model),
20212
- provider: gatewayString(info.provider),
20213
- reasoningEffort: gatewayString(info.reasoning_effort)
20214
- });
20215
- const physicalId = await bridge.history.resolvePhysicalSessionId();
20216
- if (gatewayString(info.stored_session_id) !== physicalId)
20217
- throw new Error("Hermes live Session and persisted history identities differ");
20463
+ const bridge = new HermesGatewaySessionTransport(transport, runtimeId, storedId, info, {
20464
+ revision: response.revision,
20465
+ generation: response.execution_generation,
20466
+ subscriptionId: response.subscription_id
20467
+ }, transport.delegationGuidance);
20468
+ bridge.adoptPendingPrompts(response.prompts);
20469
+ if (nativeRef) {
20470
+ await bridge.history.resolvePhysicalSessionId();
20471
+ }
20218
20472
  if (model2)
20219
20473
  await bridge.setModel(model2);
20220
20474
  if (input.kind !== "fork" && input.thinkingOptionId)
@@ -20266,6 +20520,7 @@ var HermesAdapter = class {
20266
20520
  #importReads = /* @__PURE__ */ new Map();
20267
20521
  #closed = false;
20268
20522
  #gatewayTransports = /* @__PURE__ */ new Set();
20523
+ #gatewayPool = new GatewayTransportPool(2);
20269
20524
  #gatewayProbes = /* @__PURE__ */ new Map();
20270
20525
  #openingNativeIds = /* @__PURE__ */ new Set();
20271
20526
  usageStatistics;
@@ -20381,9 +20636,10 @@ var HermesAdapter = class {
20381
20636
  return failure("sessionBusy", "This Hermes Session already has an owner", true);
20382
20637
  if (ref)
20383
20638
  this.#openingNativeIds.add(ref.nativeSessionId);
20384
- const transport = new HermesGatewayTransport(runtime, input.cwd, environment, this.#options.commandTimeoutMs);
20385
- this.#gatewayTransports.add(transport);
20639
+ let transport;
20386
20640
  try {
20641
+ transport = this.#gatewayPool.acquire(runtime, input.cwd, environment) ?? await startGatewayTransport(runtime, input.cwd, environment, this.#options.commandTimeoutMs);
20642
+ this.#gatewayTransports.add(transport);
20387
20643
  let inheritedMode;
20388
20644
  if (input.kind === "fork") {
20389
20645
  const source = [...this.#sessions].find((s) => s.initialState.nativeRef?.nativeSessionId === input.sourceRef.nativeSessionId);
@@ -20394,6 +20650,7 @@ var HermesAdapter = class {
20394
20650
  const session = await openGatewaySession(input, transport, (settled) => {
20395
20651
  this.#sessions.delete(settled);
20396
20652
  this.#gatewayTransports.delete(transport);
20653
+ this.#gatewayPool.release(transport);
20397
20654
  }, inheritedMode);
20398
20655
  if (this.#closed) {
20399
20656
  await session.close();
@@ -20402,8 +20659,10 @@ var HermesAdapter = class {
20402
20659
  this.#sessions.add(session);
20403
20660
  return { ok: true, value: session };
20404
20661
  } catch (error51) {
20405
- await transport.close().catch(() => void 0);
20406
- this.#gatewayTransports.delete(transport);
20662
+ if (transport) {
20663
+ await transport.close().catch(() => void 0);
20664
+ this.#gatewayTransports.delete(transport);
20665
+ }
20407
20666
  return failure(error51 instanceof HermesGatewayHistoryError ? error51.code : "nativeFailure", error51 instanceof Error ? error51.message : "Hermes gateway open failed");
20408
20667
  } finally {
20409
20668
  if (ref)
@@ -20427,6 +20686,7 @@ var HermesAdapter = class {
20427
20686
  await Promise.all(sessions.map((session) => session.close().catch(() => void 0)));
20428
20687
  await Promise.all([...this.#gatewayTransports].map((transport) => transport.close()));
20429
20688
  this.#gatewayTransports.clear();
20689
+ await this.#gatewayPool.close();
20430
20690
  }
20431
20691
  #effectiveEnvironment(environment) {
20432
20692
  return { ...this.#options.environment ?? process.env, ...environment ?? {} };