@codexhost/cli-darwin-x64 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.
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.txt +6 -2
- package/app/codexhost-distribution.json +1 -1
- package/app/console-server.mjs +59 -21
- package/app/console-web.js +49 -18
- package/app/desktop-controller.mjs +554 -34
- package/app/host-runtime.mjs +3436 -1846
- package/app/plugins/antigravity/plugin.mjs +39 -3
- package/app/plugins/claude-code/plugin.mjs +16 -7
- package/app/plugins/codebuddy/plugin.mjs +14 -1
- package/app/plugins/codex-usage/plugin.mjs +4 -1
- package/app/plugins/copilot/plugin.mjs +4 -1
- package/app/plugins/cursor-cli/plugin.mjs +13 -6
- package/app/plugins/deepseek-harness/plugin.mjs +463 -47
- package/app/plugins/devin/assets/icon.svg +1 -0
- package/app/plugins/devin/manifest.json +30 -0
- package/app/plugins/devin/plugin.mjs +23363 -0
- package/app/plugins/enabled.json +2 -0
- package/app/plugins/grok/plugin.mjs +476 -392
- package/app/plugins/hermes/plugin.mjs +457 -197
- package/app/plugins/kimi-code/plugin.mjs +4 -1
- package/app/plugins/kiro-cli/plugin.mjs +4 -1
- package/app/plugins/omp/plugin.mjs +4 -1
- package/app/plugins/opencode/plugin.mjs +4 -1
- package/app/plugins/pi/plugin.mjs +1464 -412
- package/app/plugins/qoder/plugin.mjs +9528 -8655
- package/app/plugins/qoder-cn/plugin.mjs +9528 -8655
- package/app/plugins/qwen-code/assets/icon.svg +1 -0
- package/app/plugins/qwen-code/manifest.json +29 -0
- package/app/plugins/qwen-code/plugin.mjs +29298 -0
- package/app/plugins/workbuddy/plugin.mjs +14 -1
- package/app/plugins/zcode/plugin.mjs +4 -1
- package/app/renderer-extension.js +510 -114
- package/bin/codexhost +0 -0
- package/libexec/codexhost-shim +0 -0
- package/libexec/codexhost-updater +0 -0
- package/licenses/Qwen-Code-SDK-LICENSE.txt +203 -0
- 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,
|
|
1142
|
-
if (!
|
|
1141
|
+
function getElementAtPath(obj, path8) {
|
|
1142
|
+
if (!path8)
|
|
1143
1143
|
return obj;
|
|
1144
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 }, [...
|
|
1707
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
1708
1708
|
} else if (issue2.code === "invalid_key") {
|
|
1709
|
-
processError({ issues: issue2.issues }, [...
|
|
1709
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
1710
1710
|
} else if (issue2.code === "invalid_element") {
|
|
1711
|
-
processError({ issues: issue2.issues }, [...
|
|
1711
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
1712
1712
|
} else {
|
|
1713
|
-
const fullpath = [...
|
|
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,
|
|
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 }, [...
|
|
1744
|
+
issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
|
|
1745
1745
|
} else if (issue2.code === "invalid_key") {
|
|
1746
|
-
processError({ issues: issue2.issues }, [...
|
|
1746
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
1747
1747
|
} else if (issue2.code === "invalid_element") {
|
|
1748
|
-
processError({ issues: issue2.issues }, [...
|
|
1748
|
+
processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
|
|
1749
1749
|
} else {
|
|
1750
|
-
const fullpath = [...
|
|
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
|
|
1783
|
-
for (const seg of
|
|
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
|
|
14476
|
-
if (
|
|
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 (
|
|
14481
|
-
const key =
|
|
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
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
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
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
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
|
|
17072
|
-
|
|
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
|
|
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 :
|
|
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
|
-
|
|
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
|
|
18299
|
+
let path8 = "";
|
|
18160
18300
|
let inHunk = false;
|
|
18161
18301
|
let oldLines = [], newLines = [];
|
|
18162
18302
|
const flush = () => {
|
|
18163
|
-
if (
|
|
18303
|
+
if (path8)
|
|
18164
18304
|
result.push({
|
|
18165
18305
|
type: "diff",
|
|
18166
|
-
path:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18328
|
+
path8 = next.replace(/^b\//u, "");
|
|
18189
18329
|
} else if (line.startsWith("@@")) {
|
|
18190
18330
|
inHunk = true;
|
|
18191
18331
|
continue;
|
|
18192
|
-
} else if (
|
|
18332
|
+
} else if (path8 && line.startsWith("-"))
|
|
18193
18333
|
oldLines.push(line.slice(1));
|
|
18194
|
-
else if (
|
|
18334
|
+
else if (path8 && line.startsWith("+"))
|
|
18195
18335
|
newLines.push(line.slice(1));
|
|
18196
|
-
else if (
|
|
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
|
|
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
|
-
|
|
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
|
|
18313
|
-
const
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
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.
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
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")
|
|
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 !== "
|
|
18405
|
-
this.#fault(new Error(`Hermes did not
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
|
|
18607
|
-
|
|
18608
|
-
|
|
18609
|
-
|
|
18610
|
-
|
|
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
|
|
18622
|
-
import
|
|
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 ||
|
|
18935
|
+
const home = environment.HOME || environment.USERPROFILE || os2.homedir();
|
|
18683
18936
|
const configured = environment.HERMES_HOME;
|
|
18684
|
-
return
|
|
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
|
|
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(
|
|
18977
|
-
const last =
|
|
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:
|
|
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:
|
|
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:
|
|
19301
|
-
if (!
|
|
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:
|
|
19562
|
+
path: path8,
|
|
19310
19563
|
kind: "update",
|
|
19311
19564
|
diffScope: "fragment",
|
|
19312
|
-
unifiedDiff: createTwoFilesPatch(
|
|
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(() =>
|
|
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
|
-
|
|
20180
|
-
|
|
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
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
close_on_disconnect
|
|
20194
|
-
|
|
20195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20209
|
-
|
|
20210
|
-
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
|
|
20214
|
-
|
|
20215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20406
|
-
|
|
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 ?? {} };
|