@hermespilot/link 0.7.7 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import Router from "@koa/router";
5
5
  // src/conversations/conversation-service.ts
6
6
  import { EventEmitter } from "events";
7
7
  import { createHash as createHash6, randomUUID as randomUUID11 } from "crypto";
8
- import path24 from "path";
8
+ import path25 from "path";
9
9
 
10
10
  // src/database/link-database.ts
11
11
  import { mkdir } from "fs/promises";
@@ -2032,6 +2032,13 @@ function translateChineseKnownErrorToEnglish(message) {
2032
2032
  if (restartFailed?.groups) {
2033
2033
  return `Hermes Gateway restart failed.${restartFailed.groups.detail}`;
2034
2034
  }
2035
+ if (message.startsWith("Hermes Link \u5DF2\u8FDE\u63A5\uFF0C\u4F46\u672C\u673A\u6267\u884C\u9762\u4E0D\u53EF\u7528\uFF1A")) {
2036
+ return [
2037
+ "Hermes Link is reachable, but the local Hermes execution backend is unavailable because the background service cannot find the `hermes` command.",
2038
+ "First run `hermeslink restart` on this computer to try to recover. If that fixes the App, run `hermeslink autostart on` afterwards to refresh the boot autostart environment so this does not happen again after the next reboot.",
2039
+ "If it still fails, run `hermeslink doctor` for diagnostics. If Hermes Agent is installed in a custom location, set HERMES_BIN to the full path of the `hermes` executable."
2040
+ ].join("\n");
2041
+ }
2035
2042
  const cliMissing = /^没有找到可用的 Hermes Agent CLI。请先安装 Hermes Agent,或设置 HERMES_BIN。(?<detail>.*)$/u.exec(message);
2036
2043
  if (cliMissing?.groups) {
2037
2044
  return `No usable Hermes Agent CLI was found. Install Hermes Agent or set HERMES_BIN.${cliMissing.groups.detail}`;
@@ -6547,7 +6554,7 @@ async function listCronOutputFiles(profileName, jobId) {
6547
6554
  orderTimeMs: fileStat.mtimeMs
6548
6555
  });
6549
6556
  }
6550
- return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path: path34, mtime }) => ({ path: path34, mtime }));
6557
+ return files.sort((left, right) => left.orderTimeMs - right.orderTimeMs).map(({ path: path35, mtime }) => ({ path: path35, mtime }));
6551
6558
  }
6552
6559
  function readCronOutputTimestamp(fileName) {
6553
6560
  const match = fileName.match(
@@ -6636,7 +6643,7 @@ function isConversationMissingError(error) {
6636
6643
  }
6637
6644
 
6638
6645
  // src/constants.ts
6639
- var LINK_VERSION = "0.7.7";
6646
+ var LINK_VERSION = "0.7.8";
6640
6647
  var LINK_COMMAND = "hermeslink";
6641
6648
  var LINK_DEFAULT_PORT = 52379;
6642
6649
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -6669,6 +6676,7 @@ function resolveRuntimePaths(homeDir = resolveRuntimeHome()) {
6669
6676
  var defaultLinkConfig = {
6670
6677
  port: LINK_DEFAULT_PORT,
6671
6678
  lanHost: null,
6679
+ hermesBin: null,
6672
6680
  serverBaseUrl: "https://hermes-server.clawpilot.me",
6673
6681
  relayBaseUrl: "https://hermes-relay.clawpilot.me",
6674
6682
  appConnectTokenIssuer: "https://hermes-server.clawpilot.me",
@@ -6680,6 +6688,7 @@ async function loadConfig(paths = resolveRuntimePaths()) {
6680
6688
  const existing = await readJsonFile(paths.configFile);
6681
6689
  const language = normalizeConfiguredLanguage(existing?.language);
6682
6690
  const lanHost = normalizeLanHost(existing?.lanHost);
6691
+ const hermesBin = normalizeHermesBin(existing?.hermesBin);
6683
6692
  const logLevel = normalizeLogLevel(
6684
6693
  existing?.logLevel ?? process.env.HERMESLINK_LOG_LEVEL
6685
6694
  );
@@ -6688,6 +6697,7 @@ async function loadConfig(paths = resolveRuntimePaths()) {
6688
6697
  ...existing ?? {},
6689
6698
  language,
6690
6699
  lanHost,
6700
+ hermesBin,
6691
6701
  logLevel
6692
6702
  };
6693
6703
  }
@@ -6696,6 +6706,7 @@ async function saveConfig(patch, paths = resolveRuntimePaths()) {
6696
6706
  const next = {
6697
6707
  ...current,
6698
6708
  ...patch,
6709
+ hermesBin: patch.hermesBin === void 0 ? current.hermesBin : normalizeHermesBin(patch.hermesBin),
6699
6710
  logLevel: patch.logLevel === void 0 ? current.logLevel : normalizeLogLevel(patch.logLevel)
6700
6711
  };
6701
6712
  await writeJsonFile(paths.configFile, next);
@@ -6735,6 +6746,16 @@ function normalizeLanHost(value) {
6735
6746
  }
6736
6747
  return host;
6737
6748
  }
6749
+ function normalizeHermesBin(value) {
6750
+ if (value === null || value === void 0) {
6751
+ return null;
6752
+ }
6753
+ if (typeof value !== "string") {
6754
+ return null;
6755
+ }
6756
+ const trimmed = value.trim();
6757
+ return trimmed || null;
6758
+ }
6738
6759
  function isUsableLanIpv4(value) {
6739
6760
  const parts = value.split(".").map((part) => Number.parseInt(part, 10));
6740
6761
  if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
@@ -6904,9 +6925,8 @@ function readErrorMessage(payload) {
6904
6925
 
6905
6926
  // src/hermes/gateway.ts
6906
6927
  import { execFile as execFile2, spawn } from "child_process";
6907
- import { constants as fsConstants } from "fs";
6908
- import { access, readFile as readFile5, realpath, stat as stat4 } from "fs/promises";
6909
- import path7 from "path";
6928
+ import { access, readFile as readFile5, stat as stat4 } from "fs/promises";
6929
+ import path8 from "path";
6910
6930
  import { setTimeout as delay2 } from "timers/promises";
6911
6931
  import { promisify as promisify2 } from "util";
6912
6932
 
@@ -7287,6 +7307,15 @@ function rotatedLogFile(filePath, index) {
7287
7307
 
7288
7308
  // src/hermes/cli.ts
7289
7309
  import { execFile } from "child_process";
7310
+ import {
7311
+ accessSync,
7312
+ constants as fsConstants,
7313
+ readFileSync,
7314
+ realpathSync,
7315
+ statSync
7316
+ } from "fs";
7317
+ import os4 from "os";
7318
+ import path7 from "path";
7290
7319
  import { promisify } from "util";
7291
7320
  var execFileAsync = promisify(execFile);
7292
7321
  async function deleteHermesSession(sessionId, profileName = "default") {
@@ -7379,7 +7408,158 @@ async function renameHermesSession(sessionId, title, profileName = "default") {
7379
7408
  }
7380
7409
  }
7381
7410
  function resolveHermesBin() {
7382
- return process.env.HERMES_BIN?.trim() || "hermes";
7411
+ return resolveHermesBinResolution().bin;
7412
+ }
7413
+ function resolveHermesBinResolution(paths = resolveRuntimePaths()) {
7414
+ const envHermesBin = process.env.HERMES_BIN?.trim();
7415
+ if (envHermesBin) {
7416
+ return { bin: envHermesBin, source: "env" };
7417
+ }
7418
+ const configuredHermesBin = readConfiguredHermesBin(paths);
7419
+ if (configuredHermesBin) {
7420
+ const resolved = resolveExecutablePathSync(configuredHermesBin);
7421
+ if (resolved) {
7422
+ return { bin: resolved, source: "config" };
7423
+ }
7424
+ }
7425
+ const pathHermesBin = resolveExecutablePathSync("hermes", { pathOnly: true });
7426
+ if (pathHermesBin) {
7427
+ return { bin: pathHermesBin, source: "path" };
7428
+ }
7429
+ const commonHermesBin = commonHermesBinCandidates().map((candidate) => resolveExecutablePathSync(candidate)).find((candidate) => Boolean(candidate));
7430
+ if (commonHermesBin) {
7431
+ return { bin: commonHermesBin, source: "common" };
7432
+ }
7433
+ return { bin: "hermes", source: "fallback" };
7434
+ }
7435
+ async function rememberResolvedHermesBin(hermesBin, paths = resolveRuntimePaths()) {
7436
+ const resolved = resolveExecutablePathSync(hermesBin);
7437
+ if (!resolved || !path7.isAbsolute(resolved)) {
7438
+ return null;
7439
+ }
7440
+ if (readConfiguredHermesBin(paths) === resolved) {
7441
+ return resolved;
7442
+ }
7443
+ await saveConfig({ hermesBin: resolved }, paths);
7444
+ return resolved;
7445
+ }
7446
+ function resolveExecutablePathSync(command, options = {}) {
7447
+ const candidates = executablePathCandidates(command, options);
7448
+ for (const candidate of candidates) {
7449
+ if (!isExecutableFile(candidate)) {
7450
+ continue;
7451
+ }
7452
+ try {
7453
+ return realpathSync(candidate);
7454
+ } catch {
7455
+ return candidate;
7456
+ }
7457
+ }
7458
+ return null;
7459
+ }
7460
+ function isHermesExecutableMissingError(error) {
7461
+ if (typeof error === "object" && error !== null && "code" in error) {
7462
+ const code = error.code;
7463
+ if (code === "ENOENT") {
7464
+ return true;
7465
+ }
7466
+ }
7467
+ const message = error instanceof Error ? error.message : String(error);
7468
+ return /\bspawn\b[\s\S]*\bENOENT\b/u.test(message);
7469
+ }
7470
+ function createHermesCliMissingError(error, language = "zh-CN") {
7471
+ return new LinkHttpError(
7472
+ 503,
7473
+ "hermes_agent_cli_missing",
7474
+ buildHermesCliMissingMessage(resolveHermesBin(), error, language)
7475
+ );
7476
+ }
7477
+ function buildHermesCliMissingMessage(hermesBin, error, language = "zh-CN") {
7478
+ if (language === "en") {
7479
+ return buildEnglishHermesCliMissingMessage(hermesBin, error);
7480
+ }
7481
+ const detail = path7.isAbsolute(hermesBin) ? `\u914D\u7F6E\u7684 Hermes CLI \u8DEF\u5F84\u4E0D\u53EF\u7528\uFF1A${hermesBin}` : `\u540E\u53F0\u73AF\u5883\u627E\u4E0D\u5230 Hermes Agent \u7684 \`${hermesBin}\` \u547D\u4EE4`;
7482
+ const originalHint = isHermesExecutableMissingError(error) ? "" : formatHermesCliErrorHint(error);
7483
+ return [
7484
+ `Hermes Link \u5DF2\u8FDE\u63A5\uFF0C\u4F46\u672C\u673A\u6267\u884C\u9762\u4E0D\u53EF\u7528\uFF1A${detail}\uFF0C\u56E0\u6B64\u65E0\u6CD5\u542F\u52A8 Hermes Agent \u804A\u5929\u8FDB\u7A0B\u3002`,
7485
+ "\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `hermeslink restart` \u5C1D\u8BD5\u6062\u590D\u3002\u82E5\u91CD\u542F\u540E\u6062\u590D\u6B63\u5E38\uFF0C\u8BF7\u518D\u8FD0\u884C `hermeslink autostart on` \u5237\u65B0\u5F00\u673A\u81EA\u542F\u73AF\u5883\uFF0C\u907F\u514D\u4E0B\u6B21\u91CD\u542F\u7535\u8111\u540E\u518D\u6B21\u53D1\u751F\u3002",
7486
+ "\u5982\u679C\u4ECD\u7136\u5931\u8D25\uFF0C\u8BF7\u8FD0\u884C `hermeslink doctor` \u67E5\u770B\u8BCA\u65AD\uFF1B\u82E5 Hermes Agent \u5B89\u88C5\u5728\u81EA\u5B9A\u4E49\u8DEF\u5F84\uFF0C\u8BF7\u5C06 HERMES_BIN \u8BBE\u7F6E\u4E3A `hermes` \u53EF\u6267\u884C\u6587\u4EF6\u7684\u5B8C\u6574\u8DEF\u5F84\u3002",
7487
+ originalHint
7488
+ ].filter(Boolean).join("\n");
7489
+ }
7490
+ function buildEnglishHermesCliMissingMessage(hermesBin, error) {
7491
+ const detail = path7.isAbsolute(hermesBin) ? `the configured Hermes CLI path is not usable: ${hermesBin}` : `the background environment cannot find the \`${hermesBin}\` command`;
7492
+ const originalHint = isHermesExecutableMissingError(error) ? "" : formatHermesCliErrorHint(error, "en");
7493
+ return [
7494
+ `Hermes Link is reachable, but the local Hermes execution backend is unavailable because ${detail}. Hermes Link cannot start the Hermes Agent chat process.`,
7495
+ "First run `hermeslink restart` on this computer to try to recover. If that fixes the App, run `hermeslink autostart on` afterwards to refresh the boot autostart environment so this does not happen again after the next reboot.",
7496
+ "If it still fails, run `hermeslink doctor` for diagnostics. If Hermes Agent is installed in a custom location, set HERMES_BIN to the full path of the `hermes` executable.",
7497
+ originalHint
7498
+ ].filter(Boolean).join("\n");
7499
+ }
7500
+ function formatHermesCliErrorHint(error, language = "zh-CN") {
7501
+ if (error === void 0 || error === null) {
7502
+ return "";
7503
+ }
7504
+ const message = error instanceof Error ? error.message : String(error);
7505
+ if (!message.trim()) {
7506
+ return "";
7507
+ }
7508
+ return language === "en" ? `Raw diagnostic: ${message}` : `\u539F\u59CB\u8BCA\u65AD\uFF1A${message}`;
7509
+ }
7510
+ function readConfiguredHermesBin(paths) {
7511
+ try {
7512
+ const raw = readFileSync(paths.configFile, "utf8");
7513
+ const payload = JSON.parse(raw);
7514
+ return normalizeHermesBin(payload.hermesBin);
7515
+ } catch {
7516
+ return null;
7517
+ }
7518
+ }
7519
+ function executablePathCandidates(command, options) {
7520
+ const normalized = command.trim();
7521
+ if (!normalized) {
7522
+ return [];
7523
+ }
7524
+ if (/[\\/]/u.test(normalized)) {
7525
+ return options.pathOnly ? [] : withWindowsExecutableExtensions(normalized);
7526
+ }
7527
+ const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter(Boolean);
7528
+ return pathEntries.flatMap(
7529
+ (entry) => withWindowsExecutableExtensions(path7.join(entry, normalized))
7530
+ );
7531
+ }
7532
+ function commonHermesBinCandidates() {
7533
+ const home = os4.homedir();
7534
+ return [
7535
+ path7.join(home, ".hermes", "hermes-agent", "venv", "bin", "hermes"),
7536
+ path7.join(home, ".local", "bin", "hermes"),
7537
+ ...process.platform === "win32" ? [] : [
7538
+ "/opt/homebrew/bin/hermes",
7539
+ "/usr/local/bin/hermes",
7540
+ "/usr/bin/hermes",
7541
+ "/bin/hermes"
7542
+ ]
7543
+ ];
7544
+ }
7545
+ function withWindowsExecutableExtensions(filePath) {
7546
+ if (process.platform !== "win32" || path7.extname(filePath)) {
7547
+ return [filePath];
7548
+ }
7549
+ const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
7550
+ return [filePath, ...extensions.map((extension) => `${filePath}${extension}`)];
7551
+ }
7552
+ function isExecutableFile(filePath) {
7553
+ try {
7554
+ const stats = statSync(filePath);
7555
+ if (!stats.isFile()) {
7556
+ return false;
7557
+ }
7558
+ accessSync(filePath, fsConstants.X_OK);
7559
+ return true;
7560
+ } catch {
7561
+ return false;
7562
+ }
7383
7563
  }
7384
7564
  function readSessionDeleteStatus(stdout, stderr) {
7385
7565
  const output = `${stdout.toString()}
@@ -7486,7 +7666,8 @@ async function ensureHermesApiServerAvailable(options = {}) {
7486
7666
  const start = await startHermesGatewayOnce(
7487
7667
  options.paths ?? resolveRuntimePaths(),
7488
7668
  profileName,
7489
- options.logger
7669
+ options.logger,
7670
+ options.language
7490
7671
  );
7491
7672
  health = await waitForHermesApiHealth(
7492
7673
  configResult.apiServer,
@@ -7512,7 +7693,8 @@ async function ensureHermesApiServerAvailable(options = {}) {
7512
7693
  const repairedStart = await startHermesGatewayOnce(
7513
7694
  options.paths ?? resolveRuntimePaths(),
7514
7695
  profileName,
7515
- options.logger
7696
+ options.logger,
7697
+ options.language
7516
7698
  );
7517
7699
  health = await waitForHermesApiHealth(
7518
7700
  repairedConfigResult.apiServer,
@@ -7673,7 +7855,7 @@ async function execHermesVersion(hermesBin, logger) {
7673
7855
  throw new Error(summary);
7674
7856
  }
7675
7857
  async function readHermesPythonMetadataVersion(hermesBin, timeoutMs) {
7676
- const hermesPath = await resolveExecutablePath(hermesBin);
7858
+ const hermesPath = resolveExecutablePathSync(hermesBin);
7677
7859
  if (!hermesPath) {
7678
7860
  return null;
7679
7861
  }
@@ -7865,22 +8047,25 @@ function assertHermesRunsApiSupported(version, status, endpoint = "/v1/runs", la
7865
8047
  language === "en" ? `${versionText}The current Hermes Agent API Server does not support the ${endpoint} endpoint required by HermesPilot. Run \`hermes update\` to upgrade Hermes Agent.` : `${versionText}\u5F53\u524D Hermes Agent API Server \u4E0D\u652F\u6301 HermesPilot \u9700\u8981\u7684 ${endpoint} \u63A5\u53E3\uFF0C\u8BF7\u5148\u8FD0\u884C \`hermes update\` \u5347\u7EA7 Hermes Agent\u3002`
7866
8048
  );
7867
8049
  }
7868
- async function startHermesGatewayOnce(paths, profileName, logger) {
8050
+ async function startHermesGatewayOnce(paths, profileName, logger, language = "zh-CN") {
7869
8051
  if (!gatewayStartInFlightByProfile.has(profileName)) {
7870
8052
  gatewayStartInFlightByProfile.set(
7871
8053
  profileName,
7872
- startHermesGateway(paths, profileName, logger).finally(() => {
8054
+ startHermesGateway(paths, profileName, logger, language).finally(() => {
7873
8055
  gatewayStartInFlightByProfile.delete(profileName);
7874
8056
  })
7875
8057
  );
7876
8058
  }
7877
8059
  return await gatewayStartInFlightByProfile.get(profileName);
7878
8060
  }
7879
- async function startHermesGateway(paths, profileName, logger) {
8061
+ async function startHermesGateway(paths, profileName, logger, language = "zh-CN") {
7880
8062
  const version = await readHermesVersion({ logger }).catch((error) => {
7881
8063
  void logger?.error("gateway_hermes_cli_unavailable", {
7882
8064
  error: error instanceof Error ? error.message : String(error)
7883
8065
  });
8066
+ if (isHermesExecutableMissingError(error)) {
8067
+ throw createHermesCliMissingError(error, language);
8068
+ }
7884
8069
  throw new LinkHttpError(
7885
8070
  503,
7886
8071
  "hermes_agent_not_available",
@@ -7899,11 +8084,12 @@ async function startHermesGateway(paths, profileName, logger) {
7899
8084
  }
7900
8085
  const gatewayLog = createRotatingTextLogWriter({
7901
8086
  paths,
7902
- fileName: path7.basename(getGatewayRuntimeLogFile(paths))
8087
+ fileName: path8.basename(getGatewayRuntimeLogFile(paths))
7903
8088
  });
7904
8089
  const logPath = gatewayLog.filePath;
7905
8090
  try {
7906
- const child = spawn(resolveHermesBin(), gatewayRunArgs(profileName), {
8091
+ const hermesBin = resolveHermesBin();
8092
+ const child = spawn(hermesBin, gatewayRunArgs(profileName), {
7907
8093
  detached: true,
7908
8094
  env: buildHermesGatewayChildEnv(),
7909
8095
  stdio: ["ignore", "pipe", "pipe"],
@@ -7934,7 +8120,20 @@ async function startHermesGateway(paths, profileName, logger) {
7934
8120
  callback();
7935
8121
  };
7936
8122
  child.once("spawn", () => finish(resolve));
7937
- child.once("error", (error) => finish(() => reject(error)));
8123
+ child.once(
8124
+ "error",
8125
+ (error) => finish(() => {
8126
+ if (isHermesExecutableMissingError(error)) {
8127
+ void logger?.error("gateway_hermes_cli_spawn_missing", {
8128
+ hermes_bin: hermesBin,
8129
+ error: error.message
8130
+ });
8131
+ reject(createHermesCliMissingError(error, language));
8132
+ return;
8133
+ }
8134
+ reject(error);
8135
+ })
8136
+ );
7938
8137
  });
7939
8138
  unrefDetachedGatewayProcess(child);
7940
8139
  void logger?.info("gateway_process_spawned", {
@@ -7969,7 +8168,7 @@ async function restartHermesGatewayServiceIfAvailable(options) {
7969
8168
  if (!home) {
7970
8169
  return null;
7971
8170
  }
7972
- const launchdPlistPath = path7.join(
8171
+ const launchdPlistPath = path8.join(
7973
8172
  home,
7974
8173
  "Library",
7975
8174
  "LaunchAgents",
@@ -7988,13 +8187,16 @@ async function restartHermesGatewayServiceIfAvailable(options) {
7988
8187
  void options.logger?.error("gateway_service_restart_failed", {
7989
8188
  error: error instanceof Error ? error.message : String(error)
7990
8189
  });
8190
+ if (isHermesExecutableMissingError(error)) {
8191
+ throw createHermesCliMissingError(error, options.language);
8192
+ }
7991
8193
  throw new LinkHttpError(
7992
8194
  503,
7993
8195
  "hermes_gateway_restart_failed",
7994
8196
  `Hermes Gateway \u670D\u52A1\u91CD\u542F\u5931\u8D25\u3002${formatErrorSuffix(error)}`
7995
8197
  );
7996
8198
  }
7997
- const logPath = path7.join(home, ".hermes", "logs", "gateway.error.log");
8199
+ const logPath = path8.join(home, ".hermes", "logs", "gateway.error.log");
7998
8200
  const logHint = await readRecentGatewayLogHint(logPath);
7999
8201
  return {
8000
8202
  pid: null,
@@ -8414,6 +8616,12 @@ async function readHermesDashboardVersion(options = {}) {
8414
8616
  async function probeHermesVersion(hermesBin, options) {
8415
8617
  try {
8416
8618
  const { stdout } = await execHermesVersion(hermesBin, options.logger);
8619
+ await rememberResolvedHermesBin(hermesBin).catch((error) => {
8620
+ void options.logger?.debug("hermes_bin_remember_failed", {
8621
+ hermes_bin: hermesBin,
8622
+ error: error instanceof Error ? error.message : String(error)
8623
+ });
8624
+ });
8417
8625
  const raw = stdout.trim();
8418
8626
  const version = parseHermesVersion(raw);
8419
8627
  return buildHermesVersionInfo(raw, version);
@@ -8523,37 +8731,6 @@ function readExecErrorDetails(error, message) {
8523
8731
  function truncateVersionLogOutput(value) {
8524
8732
  return value.length > MAX_VERSION_LOG_OUTPUT_LENGTH ? `${value.slice(0, MAX_VERSION_LOG_OUTPUT_LENGTH)}...` : value;
8525
8733
  }
8526
- async function resolveExecutablePath(command) {
8527
- const candidates = executablePathCandidates(command);
8528
- for (const candidate of candidates) {
8529
- try {
8530
- await access(candidate, fsConstants.X_OK);
8531
- return await realpath(candidate).catch(() => candidate);
8532
- } catch {
8533
- }
8534
- }
8535
- return null;
8536
- }
8537
- function executablePathCandidates(command) {
8538
- const normalized = command.trim();
8539
- if (!normalized) {
8540
- return [];
8541
- }
8542
- if (/[\\/]/u.test(normalized)) {
8543
- return withWindowsExecutableExtensions(normalized);
8544
- }
8545
- const pathEntries = (process.env.PATH ?? "").split(path7.delimiter).filter(Boolean);
8546
- return pathEntries.flatMap(
8547
- (entry) => withWindowsExecutableExtensions(path7.join(entry, normalized))
8548
- );
8549
- }
8550
- function withWindowsExecutableExtensions(filePath) {
8551
- if (process.platform !== "win32" || path7.extname(filePath)) {
8552
- return [filePath];
8553
- }
8554
- const extensions = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
8555
- return [filePath, ...extensions.map((extension) => `${filePath}${extension}`)];
8556
- }
8557
8734
  async function readFirstLine(filePath) {
8558
8735
  const raw = await readFile5(filePath, "utf8");
8559
8736
  return raw.split(/\r?\n/u, 1)[0] ?? "";
@@ -8566,7 +8743,7 @@ function parsePythonShebang(line) {
8566
8743
  if (parts.length === 0) {
8567
8744
  return null;
8568
8745
  }
8569
- const commandName = path7.basename(parts[0] ?? "").toLowerCase();
8746
+ const commandName = path8.basename(parts[0] ?? "").toLowerCase();
8570
8747
  if (commandName === "env") {
8571
8748
  const args = expandEnvShebangArgs(parts.slice(1));
8572
8749
  const pythonIndex = args.findIndex((part) => isPythonCommandName(part));
@@ -8587,7 +8764,7 @@ function parsePythonShebang(line) {
8587
8764
  };
8588
8765
  }
8589
8766
  function isPythonCommandName(command) {
8590
- return /^python(?:\d+(?:\.\d+)?)?$/iu.test(path7.basename(command));
8767
+ return /^python(?:\d+(?:\.\d+)?)?$/iu.test(path8.basename(command));
8591
8768
  }
8592
8769
  function expandEnvShebangArgs(args) {
8593
8770
  const expanded = [];
@@ -8631,11 +8808,12 @@ import { spawn as spawn2 } from "child_process";
8631
8808
  import { randomBytes as randomBytes2 } from "crypto";
8632
8809
  import { readFile as readFile6, stat as stat5 } from "fs/promises";
8633
8810
  import net2 from "net";
8634
- import path8 from "path";
8811
+ import path9 from "path";
8635
8812
  import { setTimeout as delay3 } from "timers/promises";
8636
8813
  import WebSocket from "ws";
8637
8814
  var CONNECT_TIMEOUT_MS = 15e3;
8638
8815
  var REQUEST_TIMEOUT_MS = 12e4;
8816
+ var USAGE_REQUEST_TIMEOUT_MS = 1e4;
8639
8817
  var PROMPT_SUBMIT_TIMEOUT_MS = 0;
8640
8818
  var READY_TIMEOUT_MS = 1e4;
8641
8819
  var BACKEND_START_TIMEOUT_MS = 45e3;
@@ -8656,19 +8834,31 @@ async function streamTuiGatewayRun(input) {
8656
8834
  profileName,
8657
8835
  paths: input.paths,
8658
8836
  logger: input.logger,
8659
- signal: input.signal
8837
+ signal: input.signal,
8838
+ language: input.language
8660
8839
  });
8661
8840
  const started = await ensureRpcSession(client, {
8662
8841
  ...input,
8663
8842
  profileName
8664
8843
  });
8665
8844
  await client.waitForSessionDrain(started.runtimeSessionId, input.signal);
8845
+ const usageBaseline = await client.readSessionUsage(started.runtimeSessionId).catch((error) => {
8846
+ void input.logger?.debug("tui_gateway_usage_baseline_read_failed", {
8847
+ profile: profileName,
8848
+ session_id: started.runtimeSessionId,
8849
+ error: error instanceof Error ? error.message : String(error)
8850
+ });
8851
+ return started.resumed ? null : {};
8852
+ });
8666
8853
  const events = client.submitPrompt({
8667
8854
  sessionId: started.runtimeSessionId,
8668
8855
  text: buildTuiGatewayPromptText(input),
8669
8856
  signal: input.signal
8670
8857
  });
8671
- return { started, events };
8858
+ return {
8859
+ started,
8860
+ events: normalizeTuiGatewayRunUsageDeltas(events, usageBaseline)
8861
+ };
8672
8862
  }
8673
8863
  function buildTuiGatewayPromptText(input) {
8674
8864
  return input.input;
@@ -8949,9 +9139,10 @@ async function startTuiGatewayBackend(input) {
8949
9139
  ];
8950
9140
  const logWriter = createRotatingTextLogWriter({
8951
9141
  paths,
8952
- fileName: path8.basename(getGatewayRuntimeLogFile(paths))
9142
+ fileName: path9.basename(getGatewayRuntimeLogFile(paths))
8953
9143
  });
8954
- const child = spawn2(resolveHermesBin(), args, {
9144
+ const hermesBin = resolveHermesBin();
9145
+ const child = spawn2(hermesBin, args, {
8955
9146
  env: {
8956
9147
  ...process.env,
8957
9148
  HERMES_DASHBOARD_SESSION_TOKEN: token
@@ -8976,7 +9167,13 @@ async function startTuiGatewayBackend(input) {
8976
9167
  signal
8977
9168
  });
8978
9169
  });
8979
- await waitForChildSpawn(child, input.signal);
9170
+ await waitForChildSpawn(
9171
+ child,
9172
+ input.signal,
9173
+ input.logger,
9174
+ hermesBin,
9175
+ input.language
9176
+ );
8980
9177
  const baseUrl = `http://127.0.0.1:${port}`;
8981
9178
  const wsUrl = `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(token)}`;
8982
9179
  try {
@@ -9180,6 +9377,13 @@ var TuiGatewayClient = class {
9180
9377
  queue.closed.finally(cleanup).catch(() => void 0);
9181
9378
  return queue;
9182
9379
  }
9380
+ async readSessionUsage(sessionId) {
9381
+ return await this.request(
9382
+ "session.usage",
9383
+ { session_id: sessionId },
9384
+ USAGE_REQUEST_TIMEOUT_MS
9385
+ );
9386
+ }
9183
9387
  async interruptSession(sessionId) {
9184
9388
  const queue = new GatewayEventQueue(sessionId);
9185
9389
  this.eventQueues.add(queue);
@@ -9477,6 +9681,94 @@ var GatewayEventQueue = class {
9477
9681
  });
9478
9682
  }
9479
9683
  };
9684
+ async function* normalizeTuiGatewayRunUsageDeltas(events, baseline) {
9685
+ for await (const event of events) {
9686
+ yield normalizeTuiGatewayRunUsageDelta(event, baseline);
9687
+ }
9688
+ }
9689
+ function normalizeTuiGatewayRunUsageDelta(event, baseline) {
9690
+ if (baseline === null || event.payloadType !== "run.completed" && event.payloadType !== "run.failed" && event.payloadType !== "run.cancelled") {
9691
+ return event;
9692
+ }
9693
+ const usage = normalizeTuiGatewayUsageDelta(event.payload.usage, baseline);
9694
+ if (!usage) {
9695
+ return event;
9696
+ }
9697
+ return {
9698
+ ...event,
9699
+ payload: {
9700
+ ...event.payload,
9701
+ usage
9702
+ }
9703
+ };
9704
+ }
9705
+ function normalizeTuiGatewayUsageDelta(usageValue, baseline) {
9706
+ const usage = toRecord3(usageValue);
9707
+ if (Object.keys(usage).length === 0) {
9708
+ return null;
9709
+ }
9710
+ const inputTokens = usageDelta(usage, baseline, [
9711
+ "input",
9712
+ "input_tokens",
9713
+ "prompt",
9714
+ "prompt_tokens"
9715
+ ]);
9716
+ const outputTokens = usageDelta(usage, baseline, [
9717
+ "output",
9718
+ "output_tokens",
9719
+ "completion",
9720
+ "completion_tokens"
9721
+ ]);
9722
+ const totalTokens = totalUsageDelta(
9723
+ usage,
9724
+ baseline,
9725
+ inputTokens,
9726
+ outputTokens
9727
+ );
9728
+ const contextTokens = readNumber2(usage, "context_used") ?? readNumber2(usage, "context_tokens") ?? readNumber2(usage, "current_context_tokens") ?? readNumber2(usage, "last_prompt_tokens");
9729
+ const contextWindow = readNumber2(usage, "context_max") ?? readNumber2(usage, "context_window") ?? readNumber2(usage, "context_length");
9730
+ const usagePercent = readNumber2(usage, "context_percent") ?? readNumber2(usage, "usage_percent");
9731
+ if (inputTokens === void 0 && outputTokens === void 0 && totalTokens === void 0 && contextTokens === void 0) {
9732
+ return null;
9733
+ }
9734
+ return {
9735
+ ...usage,
9736
+ ...inputTokens !== void 0 ? { input_tokens: inputTokens } : {},
9737
+ ...outputTokens !== void 0 ? { output_tokens: outputTokens } : {},
9738
+ ...totalTokens !== void 0 ? { total_tokens: totalTokens } : {},
9739
+ ...contextTokens !== void 0 ? { context_tokens: contextTokens } : {},
9740
+ ...contextWindow !== void 0 ? { context_window: contextWindow } : {},
9741
+ ...usagePercent !== void 0 ? { usage_percent: usagePercent } : {}
9742
+ };
9743
+ }
9744
+ function usageDelta(current, baseline, keys) {
9745
+ const currentValue = readFirstNumber(current, keys);
9746
+ if (currentValue === void 0) {
9747
+ return void 0;
9748
+ }
9749
+ const baselineValue = readFirstNumber(baseline, keys) ?? 0;
9750
+ return Math.max(0, currentValue - baselineValue);
9751
+ }
9752
+ function totalUsageDelta(current, baseline, inputTokens, outputTokens) {
9753
+ const currentTotal = readFirstNumber(current, ["total", "total_tokens"]);
9754
+ const baselineTotal = readFirstNumber(baseline, ["total", "total_tokens"]);
9755
+ if (currentTotal !== void 0 && (baselineTotal !== void 0 || Object.keys(baseline).length === 0)) {
9756
+ return Math.max(0, currentTotal - (baselineTotal ?? 0));
9757
+ }
9758
+ if (inputTokens !== void 0 || outputTokens !== void 0) {
9759
+ return (inputTokens ?? 0) + (outputTokens ?? 0);
9760
+ }
9761
+ return void 0;
9762
+ }
9763
+ function readFirstNumber(payload, keys) {
9764
+ for (const key of keys) {
9765
+ const value = readNumber2(payload, key);
9766
+ if (value !== void 0) {
9767
+ return value;
9768
+ }
9769
+ }
9770
+ return void 0;
9771
+ }
9480
9772
  function normalizeGatewayEvent(event) {
9481
9773
  const payload = toRecord3(event.payload);
9482
9774
  const base = {
@@ -9657,7 +9949,7 @@ async function readLogTailSummary(logPath) {
9657
9949
  return "";
9658
9950
  }
9659
9951
  }
9660
- async function waitForChildSpawn(child, signal) {
9952
+ async function waitForChildSpawn(child, signal, logger, hermesBin = resolveHermesBin(), language = "zh-CN") {
9661
9953
  await new Promise((resolve, reject) => {
9662
9954
  const abort = () => {
9663
9955
  child.kill();
@@ -9680,6 +9972,14 @@ async function waitForChildSpawn(child, signal) {
9680
9972
  };
9681
9973
  const onError = (error) => {
9682
9974
  cleanup();
9975
+ if (isHermesExecutableMissingError(error)) {
9976
+ void logger?.error("tui_gateway_hermes_cli_spawn_missing", {
9977
+ hermes_bin: hermesBin,
9978
+ error: error.message
9979
+ });
9980
+ reject(createHermesCliMissingError(error, language));
9981
+ return;
9982
+ }
9683
9983
  reject(error);
9684
9984
  };
9685
9985
  signal?.addEventListener("abort", abort, { once: true });
@@ -9762,7 +10062,14 @@ function readString5(payload, key) {
9762
10062
  }
9763
10063
  function readNumber2(payload, key) {
9764
10064
  const value = payload[key];
9765
- return typeof value === "number" && Number.isFinite(value) ? value : null;
10065
+ if (typeof value === "number" && Number.isFinite(value)) {
10066
+ return Math.max(0, Math.floor(value));
10067
+ }
10068
+ if (typeof value === "string" && value.trim()) {
10069
+ const parsed = Number.parseInt(value.replaceAll(",", ""), 10);
10070
+ return Number.isFinite(parsed) ? Math.max(0, parsed) : void 0;
10071
+ }
10072
+ return void 0;
9766
10073
  }
9767
10074
  function readText(payload, key) {
9768
10075
  const value = payload[key];
@@ -9996,6 +10303,25 @@ function latestUsageRun(snapshot) {
9996
10303
  }
9997
10304
  return void 0;
9998
10305
  }
10306
+ function latestContextUsageRun(snapshot) {
10307
+ let latestEstimatedRun;
10308
+ let latestUsageRun2;
10309
+ for (const run of [...snapshot.runs].reverse()) {
10310
+ if (!isAgentRun(run) || !run.usage) {
10311
+ continue;
10312
+ }
10313
+ latestUsageRun2 ??= run;
10314
+ if (run.usage.context_tokens === void 0) {
10315
+ continue;
10316
+ }
10317
+ if (run.usage.context_source === "estimated") {
10318
+ latestEstimatedRun ??= run;
10319
+ continue;
10320
+ }
10321
+ return run;
10322
+ }
10323
+ return latestEstimatedRun ?? latestUsageRun2;
10324
+ }
9999
10325
  function latestRuntimeRun(snapshot) {
10000
10326
  for (const run of [...snapshot.runs].reverse()) {
10001
10327
  if (isAgentRun(run) && (run.model || run.profile_uid || run.profile_name_snapshot || run.profile || run.usage)) {
@@ -10319,11 +10645,11 @@ function workspaceNotFound() {
10319
10645
  // src/conversations/blob-store.ts
10320
10646
  import { randomUUID as randomUUID5 } from "crypto";
10321
10647
  import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3, stat as stat6, writeFile } from "fs/promises";
10322
- import path10 from "path";
10648
+ import path11 from "path";
10323
10649
 
10324
10650
  // src/conversations/media.ts
10325
10651
  import { createHash as createHash2 } from "crypto";
10326
- import path9 from "path";
10652
+ import path10 from "path";
10327
10653
 
10328
10654
  // src/conversations/delivery-contract.ts
10329
10655
  var HERMES_LINK_DELIVERY_TAG_PATTERN = /<hermes_link_delivery>\s*(\{[\s\S]*?\})\s*<\/hermes_link_delivery>/giu;
@@ -10413,7 +10739,7 @@ function cleanMessageTextParts(message) {
10413
10739
  }
10414
10740
  }
10415
10741
  function inferMimeType(filePath) {
10416
- const extension = path9.extname(filePath).toLowerCase();
10742
+ const extension = path10.extname(filePath).toLowerCase();
10417
10743
  return {
10418
10744
  ".png": "image/png",
10419
10745
  ".jpg": "image/jpeg",
@@ -10537,7 +10863,7 @@ function mediaSourceKey(sourcePath) {
10537
10863
  return createHash2("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
10538
10864
  }
10539
10865
  function sanitizeFilename(value, fallback) {
10540
- const base = path9.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
10866
+ const base = path10.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
10541
10867
  const safe = base.replace(/[/:\\]/gu, "_").slice(0, 200).trim();
10542
10868
  return safe || fallback;
10543
10869
  }
@@ -10566,9 +10892,9 @@ function isBlobReferencedByMessages(snapshot, blobId) {
10566
10892
  }
10567
10893
  function resolveMediaSourcePath(sourcePath) {
10568
10894
  const trimmed = sourcePath.trim();
10569
- const expanded = trimmed.startsWith("~/") ? path9.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
10570
- const resolved = path9.resolve(expanded);
10571
- if (!path9.isAbsolute(expanded)) {
10895
+ const expanded = trimmed.startsWith("~/") ? path10.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
10896
+ const resolved = path10.resolve(expanded);
10897
+ if (!path10.isAbsolute(expanded)) {
10572
10898
  throw new LinkHttpError(
10573
10899
  400,
10574
10900
  "media_source_path_not_absolute",
@@ -10763,7 +11089,7 @@ function normalizeConversationIds(conversationIds) {
10763
11089
  }
10764
11090
  function blobPath(paths, blobId) {
10765
11091
  assertValidBlobId(blobId);
10766
- return path10.join(paths.blobsDir, `${blobId}.bin`);
11092
+ return path11.join(paths.blobsDir, `${blobId}.bin`);
10767
11093
  }
10768
11094
  async function writeConversationBlob(paths, conversationId, input, options) {
10769
11095
  assertValidConversationId(conversationId);
@@ -10772,7 +11098,7 @@ async function writeConversationBlob(paths, conversationId, input, options) {
10772
11098
  }
10773
11099
  const id = `blob_${randomUUID5().replaceAll("-", "")}`;
10774
11100
  const filePath = blobPath(paths, id);
10775
- await mkdir6(path10.dirname(filePath), { recursive: true, mode: 448 });
11101
+ await mkdir6(path11.dirname(filePath), { recursive: true, mode: 448 });
10776
11102
  await writeFile(filePath, input.bytes, { mode: 384 });
10777
11103
  const blob = {
10778
11104
  id,
@@ -10848,7 +11174,7 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
10848
11174
  }
10849
11175
  }
10850
11176
  const targetDir = conversationAttachmentsDir(paths, conversationId);
10851
- const targetPath = path10.join(
11177
+ const targetPath = path11.join(
10852
11178
  targetDir,
10853
11179
  materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
10854
11180
  );
@@ -10901,7 +11227,7 @@ async function listConversationBlobIds(paths, conversationId) {
10901
11227
  continue;
10902
11228
  }
10903
11229
  const manifest = await readJsonFile(
10904
- path10.join(paths.blobsDir, entry.name)
11230
+ path11.join(paths.blobsDir, entry.name)
10905
11231
  ).catch(() => null);
10906
11232
  if (manifest?.conversation_ids?.includes(conversationId)) {
10907
11233
  blobIds.push(blobId);
@@ -10911,7 +11237,7 @@ async function listConversationBlobIds(paths, conversationId) {
10911
11237
  }
10912
11238
  function conversationAttachmentsDir(paths, conversationId) {
10913
11239
  assertValidConversationId(conversationId);
10914
- return path10.join(paths.conversationsDir, conversationId, "attachments");
11240
+ return path11.join(paths.conversationsDir, conversationId, "attachments");
10915
11241
  }
10916
11242
  function isNodeError6(error, code) {
10917
11243
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
@@ -10991,7 +11317,7 @@ async function readConversationProfileSummary(paths, manifest) {
10991
11317
  }
10992
11318
  async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
10993
11319
  const current = await readCurrentConversationRuntime(paths, manifest);
10994
- const usageRun = latestUsageRun(snapshot);
11320
+ const usageRun = latestContextUsageRun(snapshot);
10995
11321
  const profileUid = current.profileUid;
10996
11322
  const profileName = current.profileName;
10997
11323
  const profilePresentation = {
@@ -11946,7 +12272,7 @@ function commandText(language, zh, en) {
11946
12272
 
11947
12273
  // src/conversations/delivery-staging.ts
11948
12274
  import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
11949
- import path11 from "path";
12275
+ import path12 from "path";
11950
12276
  async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
11951
12277
  const directory = deliveryStagingRunDir(paths, conversationId, runId);
11952
12278
  await mkdir7(directory, { recursive: true, mode: 448 });
@@ -11959,14 +12285,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
11959
12285
  });
11960
12286
  }
11961
12287
  function deliveryStagingRunDir(paths, conversationId, runId) {
11962
- return path11.join(
12288
+ return path12.join(
11963
12289
  deliveryStagingConversationDir(paths, conversationId),
11964
12290
  safePathSegment(runId, "run")
11965
12291
  );
11966
12292
  }
11967
12293
  function deliveryStagingConversationDir(paths, conversationId) {
11968
12294
  assertValidConversationId(conversationId);
11969
- return path11.join(paths.conversationsDir, conversationId, "delivery-staging");
12295
+ return path12.join(paths.conversationsDir, conversationId, "delivery-staging");
11970
12296
  }
11971
12297
  function safePathSegment(value, fallback) {
11972
12298
  const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
@@ -11976,7 +12302,7 @@ function safePathSegment(value, fallback) {
11976
12302
  // src/conversations/conversation-archive-plans.ts
11977
12303
  import { randomUUID as randomUUID7 } from "crypto";
11978
12304
  import { mkdir as mkdir8 } from "fs/promises";
11979
- import path12 from "path";
12305
+ import path13 from "path";
11980
12306
  var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
11981
12307
  var ConversationArchivePlanStore = class {
11982
12308
  constructor(paths) {
@@ -12019,10 +12345,10 @@ var ConversationArchivePlanStore = class {
12019
12345
  await writeJsonFile(this.planPath(normalizedPlanId), plan);
12020
12346
  }
12021
12347
  plansDir() {
12022
- return path12.join(this.paths.indexesDir, "conversation-archive-plans");
12348
+ return path13.join(this.paths.indexesDir, "conversation-archive-plans");
12023
12349
  }
12024
12350
  planPath(planId) {
12025
- return path12.join(this.plansDir(), `${planId}.json`);
12351
+ return path13.join(this.plansDir(), `${planId}.json`);
12026
12352
  }
12027
12353
  };
12028
12354
  function normalizePlanId(planId) {
@@ -12040,7 +12366,7 @@ function normalizePlanId(planId) {
12040
12366
  // src/conversations/conversation-clear-plans.ts
12041
12367
  import { randomUUID as randomUUID8 } from "crypto";
12042
12368
  import { mkdir as mkdir9 } from "fs/promises";
12043
- import path13 from "path";
12369
+ import path14 from "path";
12044
12370
  var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
12045
12371
  var ConversationClearPlanStore = class {
12046
12372
  constructor(paths) {
@@ -12084,10 +12410,10 @@ var ConversationClearPlanStore = class {
12084
12410
  await writeJsonFile(this.planPath(normalizedPlanId), plan);
12085
12411
  }
12086
12412
  plansDir() {
12087
- return path13.join(this.paths.indexesDir, "conversation-clear-plans");
12413
+ return path14.join(this.paths.indexesDir, "conversation-clear-plans");
12088
12414
  }
12089
12415
  planPath(planId) {
12090
- return path13.join(this.plansDir(), `${planId}.json`);
12416
+ return path14.join(this.plansDir(), `${planId}.json`);
12091
12417
  }
12092
12418
  };
12093
12419
  function normalizePlanId2(planId) {
@@ -12926,14 +13252,14 @@ function readAttachmentWaveform(attachment) {
12926
13252
 
12927
13253
  // src/hermes/session-title.ts
12928
13254
  import { stat as stat8 } from "fs/promises";
12929
- import path14 from "path";
13255
+ import path15 from "path";
12930
13256
  async function readHermesSessionTitle(sessionId, paths, profileName) {
12931
13257
  const trimmedSessionId = sessionId.trim();
12932
13258
  if (!trimmedSessionId) {
12933
13259
  return void 0;
12934
13260
  }
12935
13261
  const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
12936
- const dbPath = path14.join(
13262
+ const dbPath = path15.join(
12937
13263
  resolveHermesProfileDir(resolvedProfileName),
12938
13264
  "state.db"
12939
13265
  );
@@ -12954,7 +13280,7 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
12954
13280
  return void 0;
12955
13281
  }
12956
13282
  const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
12957
- const dbPath = path14.join(
13283
+ const dbPath = path15.join(
12958
13284
  resolveHermesProfileDir(resolvedProfileName),
12959
13285
  "state.db"
12960
13286
  );
@@ -13730,10 +14056,15 @@ var ConversationOrchestrationCoordinator = class {
13730
14056
  return;
13731
14057
  }
13732
14058
  try {
14059
+ const failureMessage = await this.formatRunWorkerFailureMessage(
14060
+ conversationId,
14061
+ runId,
14062
+ error
14063
+ );
13733
14064
  await this.deps.runLifecycle.failRun(
13734
14065
  conversationId,
13735
14066
  runId,
13736
- error instanceof Error ? error.message : String(error)
14067
+ failureMessage
13737
14068
  );
13738
14069
  } catch (failError) {
13739
14070
  if (isConversationNotFoundError(failError)) {
@@ -13760,6 +14091,26 @@ var ConversationOrchestrationCoordinator = class {
13760
14091
  });
13761
14092
  });
13762
14093
  }
14094
+ async formatRunWorkerFailureMessage(conversationId, runId, error) {
14095
+ if (error instanceof LinkHttpError && error.code === "hermes_agent_cli_missing") {
14096
+ return createHermesCliMissingError(
14097
+ void 0,
14098
+ await this.readRunLanguage(conversationId, runId)
14099
+ ).message;
14100
+ }
14101
+ if (isHermesExecutableMissingError(error)) {
14102
+ return createHermesCliMissingError(
14103
+ error,
14104
+ await this.readRunLanguage(conversationId, runId)
14105
+ ).message;
14106
+ }
14107
+ return error instanceof Error ? error.message : String(error);
14108
+ }
14109
+ async readRunLanguage(conversationId, runId) {
14110
+ const snapshot = await this.deps.store.readSnapshot(conversationId).catch(() => null);
14111
+ const run = snapshot?.runs.find((item) => item.id === runId);
14112
+ return normalizeLanguage(run?.language);
14113
+ }
13763
14114
  async startNextQueuedRunLocked(conversationId) {
13764
14115
  const manifest = await this.deps.store.readRunnableManifest(conversationId);
13765
14116
  const snapshot = await this.deps.store.readSnapshot(conversationId);
@@ -15297,7 +15648,7 @@ import {
15297
15648
  rm as rm5,
15298
15649
  writeFile as writeFile2
15299
15650
  } from "fs/promises";
15300
- import path15 from "path";
15651
+ import path16 from "path";
15301
15652
  var ConversationStore = class {
15302
15653
  constructor(paths) {
15303
15654
  this.paths = paths;
@@ -15428,23 +15779,23 @@ var ConversationStore = class {
15428
15779
  return manifest != null && manifest.status !== "deleted_soft";
15429
15780
  }
15430
15781
  removeConversationAttachments(conversationId) {
15431
- return rm5(path15.join(this.conversationDir(conversationId), "attachments"), {
15782
+ return rm5(path16.join(this.conversationDir(conversationId), "attachments"), {
15432
15783
  recursive: true,
15433
15784
  force: true
15434
15785
  });
15435
15786
  }
15436
15787
  conversationDir(conversationId) {
15437
15788
  assertValidConversationId(conversationId);
15438
- return path15.join(this.paths.conversationsDir, conversationId);
15789
+ return path16.join(this.paths.conversationsDir, conversationId);
15439
15790
  }
15440
15791
  manifestPath(conversationId) {
15441
- return path15.join(this.conversationDir(conversationId), "manifest.json");
15792
+ return path16.join(this.conversationDir(conversationId), "manifest.json");
15442
15793
  }
15443
15794
  snapshotPath(conversationId) {
15444
- return path15.join(this.conversationDir(conversationId), "snapshot.json");
15795
+ return path16.join(this.conversationDir(conversationId), "snapshot.json");
15445
15796
  }
15446
15797
  eventsPath(conversationId) {
15447
- return path15.join(this.conversationDir(conversationId), "events.ndjson");
15798
+ return path16.join(this.conversationDir(conversationId), "events.ndjson");
15448
15799
  }
15449
15800
  };
15450
15801
  function createEmptySnapshot2() {
@@ -15457,11 +15808,11 @@ function isNodeError9(error, code) {
15457
15808
  // src/conversations/hermes-session-sync.ts
15458
15809
  import { randomUUID as randomUUID10 } from "crypto";
15459
15810
  import { readdir as readdir7, readFile as readFile10, stat as stat10 } from "fs/promises";
15460
- import path17 from "path";
15811
+ import path18 from "path";
15461
15812
 
15462
15813
  // src/conversations/delivery-import.ts
15463
15814
  import { lstat as lstat2, readFile as readFile9, readdir as readdir6, stat as stat9 } from "fs/promises";
15464
- import path16 from "path";
15815
+ import path17 from "path";
15465
15816
  var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
15466
15817
  var MAX_MEDIA_IMPORT_FAILURES = 20;
15467
15818
  var MAX_DELIVERY_FILES = 50;
@@ -15532,16 +15883,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
15532
15883
  ".m4a"
15533
15884
  ]);
15534
15885
  function resolveDeliveryStagingTarget(paths, stagingDir) {
15535
- const resolvedDir = path16.resolve(stagingDir);
15536
- const relative = path16.relative(path16.resolve(paths.conversationsDir), resolvedDir);
15537
- if (!relative || relative.startsWith("..") || path16.isAbsolute(relative)) {
15886
+ const resolvedDir = path17.resolve(stagingDir);
15887
+ const relative = path17.relative(path17.resolve(paths.conversationsDir), resolvedDir);
15888
+ if (!relative || relative.startsWith("..") || path17.isAbsolute(relative)) {
15538
15889
  throw new LinkHttpError(
15539
15890
  400,
15540
15891
  "delivery_staging_invalid",
15541
15892
  "delivery staging directory must be inside Hermes Link conversations"
15542
15893
  );
15543
15894
  }
15544
- const segments = relative.split(path16.sep);
15895
+ const segments = relative.split(path17.sep);
15545
15896
  if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
15546
15897
  throw new LinkHttpError(
15547
15898
  400,
@@ -15577,7 +15928,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
15577
15928
  return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
15578
15929
  (left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
15579
15930
  ).slice(0, MAX_DELIVERY_FILES).map((entry) => {
15580
- const sourcePath = path16.join(stagingDir, entry.name);
15931
+ const sourcePath = path17.join(stagingDir, entry.name);
15581
15932
  const mime = inferMimeType(sourcePath);
15582
15933
  return {
15583
15934
  path: sourcePath,
@@ -15763,7 +16114,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
15763
16114
  }
15764
16115
  return deps.writeBlob(conversationId, {
15765
16116
  bytes: await readFile9(sourcePath),
15766
- filename: path16.basename(sourcePath),
16117
+ filename: path17.basename(sourcePath),
15767
16118
  mime: source.mime ?? inferMimeType(sourcePath)
15768
16119
  });
15769
16120
  }
@@ -15776,7 +16127,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
15776
16127
  };
15777
16128
  }
15778
16129
  function isSupportedDeliveryFilename(filename) {
15779
- return SUPPORTED_DELIVERY_EXTENSIONS.has(path16.extname(filename).toLowerCase());
16130
+ return SUPPORTED_DELIVERY_EXTENSIONS.has(path17.extname(filename).toLowerCase());
15780
16131
  }
15781
16132
  function readString9(payload, key) {
15782
16133
  const value = payload[key];
@@ -15834,7 +16185,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
15834
16185
  const candidates = [];
15835
16186
  for (const profileName of profileNames) {
15836
16187
  const profileDir = resolveHermesProfileDir(profileName);
15837
- const dbPath = path17.join(profileDir, "state.db");
16188
+ const dbPath = path18.join(profileDir, "state.db");
15838
16189
  const sessions = await listProfileSessions(dbPath).catch((error) => {
15839
16190
  result.errors.push({
15840
16191
  profile: profileName,
@@ -15950,7 +16301,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
15950
16301
  const knownHermesSessions = await readKnownHermesSessions(store);
15951
16302
  const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
15952
16303
  const profileDir = resolveHermesProfileDir(profileName);
15953
- const dbPath = path17.join(profileDir, "state.db");
16304
+ const dbPath = path18.join(profileDir, "state.db");
15954
16305
  const sessions = await listProfileSessionsByIdPrefix(
15955
16306
  dbPath,
15956
16307
  `cron_${jobId}_`
@@ -17261,8 +17612,8 @@ async function readJsonlMessages(profileName, sessionId) {
17261
17612
  return [];
17262
17613
  }
17263
17614
  const profileDir = resolveHermesProfileDir(profileName);
17264
- const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path17.join(profileDir, "sessions"));
17265
- const transcriptPath = path17.join(sessionsDir, `${sessionId}.jsonl`);
17615
+ const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path18.join(profileDir, "sessions"));
17616
+ const transcriptPath = path18.join(sessionsDir, `${sessionId}.jsonl`);
17266
17617
  const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
17267
17618
  if (isNodeError11(error, "ENOENT")) {
17268
17619
  return "";
@@ -17632,7 +17983,7 @@ function isNodeError11(error, code) {
17632
17983
 
17633
17984
  // src/conversations/delivery-context.ts
17634
17985
  import { copyFile, mkdir as mkdir11, stat as stat11 } from "fs/promises";
17635
- import path18 from "path";
17986
+ import path19 from "path";
17636
17987
  var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
17637
17988
  var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
17638
17989
  var MAX_DELIVERY_TOOL_FILES = 50;
@@ -17704,10 +18055,10 @@ function findDeliveryContext(input) {
17704
18055
  );
17705
18056
  }
17706
18057
  async function copyFilesIntoDeliveryContext(paths, context, files) {
17707
- const stagingDir = path18.resolve(context.stagingDir);
17708
- const conversationsRoot = path18.resolve(paths.conversationsDir);
17709
- const relative = path18.relative(conversationsRoot, stagingDir);
17710
- if (!relative || relative.startsWith("..") || path18.isAbsolute(relative)) {
18058
+ const stagingDir = path19.resolve(context.stagingDir);
18059
+ const conversationsRoot = path19.resolve(paths.conversationsDir);
18060
+ const relative = path19.relative(conversationsRoot, stagingDir);
18061
+ if (!relative || relative.startsWith("..") || path19.isAbsolute(relative)) {
17711
18062
  throw new LinkHttpError(
17712
18063
  400,
17713
18064
  "delivery_staging_invalid",
@@ -17718,13 +18069,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
17718
18069
  const result = { staged: [], skipped: [] };
17719
18070
  const usedNames = /* @__PURE__ */ new Set();
17720
18071
  for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
17721
- const sourcePath = path18.resolve(file.path);
18072
+ const sourcePath = path19.resolve(file.path);
17722
18073
  let baseName = sanitizeFilename(
17723
- file.caption || path18.basename(sourcePath),
18074
+ file.caption || path19.basename(sourcePath),
17724
18075
  `attachment-${index + 1}`
17725
18076
  );
17726
- const sourceExtension = path18.extname(sourcePath);
17727
- if (!path18.extname(baseName) && sourceExtension) {
18077
+ const sourceExtension = path19.extname(sourcePath);
18078
+ if (!path19.extname(baseName) && sourceExtension) {
17728
18079
  baseName = `${baseName}${sourceExtension}`;
17729
18080
  }
17730
18081
  const filename = uniqueStagingFilename(
@@ -17751,7 +18102,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
17751
18102
  });
17752
18103
  continue;
17753
18104
  }
17754
- const targetPath = path18.join(stagingDir, filename);
18105
+ const targetPath = path19.join(stagingDir, filename);
17755
18106
  await copyFile(sourcePath, targetPath);
17756
18107
  result.staged.push({
17757
18108
  source_path: sourcePath,
@@ -17794,7 +18145,7 @@ function normalizeDeliveryFileInputs(value) {
17794
18145
  });
17795
18146
  }
17796
18147
  function uniqueStagingFilename(filename, usedNames) {
17797
- const extension = path18.extname(filename);
18148
+ const extension = path19.extname(filename);
17798
18149
  const stem = filename.slice(0, filename.length - extension.length);
17799
18150
  let candidate = filename;
17800
18151
  let suffix = 2;
@@ -18291,10 +18642,10 @@ function parseHermesApiCapabilities(payload) {
18291
18642
  sessionKeyHeader: readString12(features, "session_key_header")
18292
18643
  };
18293
18644
  }
18294
- async function callHermesApi(path34, init, options) {
18645
+ async function callHermesApi(path35, init, options) {
18295
18646
  const method = init.method ?? "GET";
18296
18647
  const startedAt = Date.now();
18297
- void options.logger?.debug("hermes_api_request_started", { method, path: path34 });
18648
+ void options.logger?.debug("hermes_api_request_started", { method, path: path35 });
18298
18649
  const availability = await ensureHermesApiServerAvailable({
18299
18650
  fetchImpl: options.fetchImpl,
18300
18651
  logger: options.logger,
@@ -18303,7 +18654,7 @@ async function callHermesApi(path34, init, options) {
18303
18654
  });
18304
18655
  let config = availability.configResult.apiServer;
18305
18656
  const fetcher = options.fetchImpl ?? fetch;
18306
- const request = () => fetchHermesApi(fetcher, config, path34, init, options);
18657
+ const request = () => fetchHermesApi(fetcher, config, path35, init, options);
18307
18658
  let response;
18308
18659
  try {
18309
18660
  response = await request();
@@ -18311,7 +18662,7 @@ async function callHermesApi(path34, init, options) {
18311
18662
  logHermesApiError(
18312
18663
  options.logger,
18313
18664
  method,
18314
- path34,
18665
+ path35,
18315
18666
  options.profileName,
18316
18667
  startedAt,
18317
18668
  error
@@ -18322,7 +18673,7 @@ async function callHermesApi(path34, init, options) {
18322
18673
  logHermesApiResponse(
18323
18674
  options.logger,
18324
18675
  method,
18325
- path34,
18676
+ path35,
18326
18677
  options.profileName,
18327
18678
  startedAt,
18328
18679
  response
@@ -18331,7 +18682,7 @@ async function callHermesApi(path34, init, options) {
18331
18682
  }
18332
18683
  void options.logger?.warn("hermes_api_request_retrying_after_401", {
18333
18684
  method,
18334
- path: path34,
18685
+ path: path35,
18335
18686
  profile: options.profileName ?? "default",
18336
18687
  port: config.port ?? null,
18337
18688
  duration_ms: Date.now() - startedAt
@@ -18350,7 +18701,7 @@ async function callHermesApi(path34, init, options) {
18350
18701
  logHermesApiError(
18351
18702
  options.logger,
18352
18703
  method,
18353
- path34,
18704
+ path35,
18354
18705
  options.profileName,
18355
18706
  startedAt,
18356
18707
  error
@@ -18360,7 +18711,7 @@ async function callHermesApi(path34, init, options) {
18360
18711
  logHermesApiResponse(
18361
18712
  options.logger,
18362
18713
  method,
18363
- path34,
18714
+ path35,
18364
18715
  options.profileName,
18365
18716
  startedAt,
18366
18717
  response
@@ -18370,7 +18721,7 @@ async function callHermesApi(path34, init, options) {
18370
18721
  }
18371
18722
  void options.logger?.warn("hermes_api_request_repairing_after_401", {
18372
18723
  method,
18373
- path: path34,
18724
+ path: path35,
18374
18725
  profile: options.profileName ?? "default",
18375
18726
  port: config.port ?? null,
18376
18727
  duration_ms: Date.now() - startedAt
@@ -18391,7 +18742,7 @@ async function callHermesApi(path34, init, options) {
18391
18742
  logHermesApiError(
18392
18743
  options.logger,
18393
18744
  method,
18394
- path34,
18745
+ path35,
18395
18746
  options.profileName,
18396
18747
  startedAt,
18397
18748
  error
@@ -18401,21 +18752,21 @@ async function callHermesApi(path34, init, options) {
18401
18752
  logHermesApiResponse(
18402
18753
  options.logger,
18403
18754
  method,
18404
- path34,
18755
+ path35,
18405
18756
  options.profileName,
18406
18757
  startedAt,
18407
18758
  response
18408
18759
  );
18409
18760
  return response;
18410
18761
  }
18411
- async function fetchHermesApi(fetcher, config, path34, init, options) {
18762
+ async function fetchHermesApi(fetcher, config, path35, init, options) {
18412
18763
  const headers = new Headers(init.headers);
18413
18764
  headers.set("accept", headers.get("accept") ?? "application/json");
18414
18765
  if (config.key) {
18415
18766
  headers.set("x-api-key", config.key);
18416
18767
  headers.set("authorization", `Bearer ${config.key}`);
18417
18768
  }
18418
- return await fetcher(`http://127.0.0.1:${config.port}${path34}`, {
18769
+ return await fetcher(`http://127.0.0.1:${config.port}${path35}`, {
18419
18770
  ...init,
18420
18771
  headers
18421
18772
  }).catch((error) => {
@@ -18424,10 +18775,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
18424
18775
  }
18425
18776
  void options.logger?.warn("hermes_api_server_connect_failed", {
18426
18777
  method: String(init.method ?? "GET").toUpperCase(),
18427
- path: path34,
18778
+ path: path35,
18428
18779
  profile: options.profileName ?? "default",
18429
18780
  port: config.port ?? null,
18430
- url: `http://127.0.0.1:${config.port}${path34}`,
18781
+ url: `http://127.0.0.1:${config.port}${path35}`,
18431
18782
  error: error instanceof Error ? error.message : String(error)
18432
18783
  });
18433
18784
  throw new LinkHttpError(
@@ -18437,10 +18788,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
18437
18788
  );
18438
18789
  });
18439
18790
  }
18440
- function logHermesApiResponse(logger, method, path34, profileName, startedAt, response) {
18791
+ function logHermesApiResponse(logger, method, path35, profileName, startedAt, response) {
18441
18792
  const fields = {
18442
18793
  method,
18443
- path: path34,
18794
+ path: path35,
18444
18795
  profile: profileName ?? "default",
18445
18796
  status: response.status,
18446
18797
  duration_ms: Date.now() - startedAt
@@ -18461,10 +18812,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
18461
18812
  ...upstreamError ? { upstream_error: upstreamError } : {}
18462
18813
  });
18463
18814
  }
18464
- function logHermesApiError(logger, method, path34, profileName, startedAt, error) {
18815
+ function logHermesApiError(logger, method, path35, profileName, startedAt, error) {
18465
18816
  void logger?.warn("hermes_api_request_failed", {
18466
18817
  method,
18467
- path: path34,
18818
+ path: path35,
18468
18819
  profile: profileName ?? "default",
18469
18820
  duration_ms: Date.now() - startedAt,
18470
18821
  ...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
@@ -18532,7 +18883,7 @@ function readBoolean2(payload, key) {
18532
18883
 
18533
18884
  // src/conversations/history-builder.ts
18534
18885
  import { readFile as readFile11, stat as stat12 } from "fs/promises";
18535
- import path19 from "path";
18886
+ import path20 from "path";
18536
18887
  var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
18537
18888
  var HERMES_HISTORY_COLUMNS = [
18538
18889
  "role",
@@ -18604,13 +18955,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
18604
18955
  }
18605
18956
  const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
18606
18957
  const profileDir = resolveHermesProfileDir(normalizedProfileName);
18607
- const dbPath = path19.join(profileDir, "state.db");
18958
+ const dbPath = path20.join(profileDir, "state.db");
18608
18959
  const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
18609
18960
  sessionsDir: value.sessionsDir,
18610
18961
  configured: value.configured,
18611
18962
  configError: false
18612
18963
  })).catch(() => ({
18613
- sessionsDir: path19.join(profileDir, "sessions"),
18964
+ sessionsDir: path20.join(profileDir, "sessions"),
18614
18965
  configured: false,
18615
18966
  configError: true
18616
18967
  }));
@@ -18786,8 +19137,8 @@ async function readFirstExistingFile(paths) {
18786
19137
  }
18787
19138
  function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
18788
19139
  return [
18789
- path19.join(sessionsDir, `session_${sessionId}.${extension}`),
18790
- path19.join(sessionsDir, `${sessionId}.${extension}`)
19140
+ path20.join(sessionsDir, `session_${sessionId}.${extension}`),
19141
+ path20.join(sessionsDir, `${sessionId}.${extension}`)
18791
19142
  ];
18792
19143
  }
18793
19144
  function readHistoryRows(dbPath, sessionId) {
@@ -19006,8 +19357,8 @@ function normalizeProfileForCompare(value) {
19006
19357
  // src/hermes/stt.ts
19007
19358
  import { execFile as execFile3 } from "child_process";
19008
19359
  import { access as access2, readFile as readFile12, stat as stat13 } from "fs/promises";
19009
- import os4 from "os";
19010
- import path20 from "path";
19360
+ import os5 from "os";
19361
+ import path21 from "path";
19011
19362
  import { promisify as promisify3 } from "util";
19012
19363
  var execFileAsync3 = promisify3(execFile3);
19013
19364
  var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
@@ -19102,7 +19453,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
19102
19453
  };
19103
19454
  const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
19104
19455
  if (sourceRoot) {
19105
- env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path20.delimiter);
19456
+ env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
19106
19457
  }
19107
19458
  return env;
19108
19459
  }
@@ -19139,7 +19490,7 @@ async function resolveHermesPythonRuntime() {
19139
19490
  sourceRoot: await findExplicitHermesSourceRoot() ?? await findHermesSourceRoot(explicit)
19140
19491
  };
19141
19492
  }
19142
- const hermesBin = await resolveExecutablePath2(resolveHermesBin());
19493
+ const hermesBin = await resolveExecutablePath(resolveHermesBin());
19143
19494
  let shebangRuntime = null;
19144
19495
  if (hermesBin) {
19145
19496
  const launcherTarget = await readHermesLauncherTarget(hermesBin);
@@ -19196,23 +19547,23 @@ async function resolveHermesPythonRuntime() {
19196
19547
  sourceRoot: await findExplicitHermesSourceRoot() ?? await findDevHermesAgentSource()
19197
19548
  };
19198
19549
  }
19199
- async function resolveExecutablePath2(command) {
19200
- if (path20.isAbsolute(command)) {
19201
- return await isExecutableFile(command) ? command : null;
19550
+ async function resolveExecutablePath(command) {
19551
+ if (path21.isAbsolute(command)) {
19552
+ return await isExecutableFile2(command) ? command : null;
19202
19553
  }
19203
19554
  const pathEnv = process.env.PATH ?? "";
19204
19555
  const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
19205
- for (const dir of pathEnv.split(path20.delimiter)) {
19556
+ for (const dir of pathEnv.split(path21.delimiter)) {
19206
19557
  for (const extension of extensions) {
19207
- const candidate = path20.join(dir, `${command}${extension}`);
19208
- if (await isExecutableFile(candidate)) {
19558
+ const candidate = path21.join(dir, `${command}${extension}`);
19559
+ if (await isExecutableFile2(candidate)) {
19209
19560
  return candidate;
19210
19561
  }
19211
19562
  }
19212
19563
  }
19213
19564
  return null;
19214
19565
  }
19215
- async function isExecutableFile(filePath) {
19566
+ async function isExecutableFile2(filePath) {
19216
19567
  try {
19217
19568
  const info = await stat13(filePath);
19218
19569
  if (!info.isFile()) {
@@ -19226,14 +19577,14 @@ async function isExecutableFile(filePath) {
19226
19577
  }
19227
19578
  async function findHermesVenvPython(sourceRoot) {
19228
19579
  const candidates = process.platform === "win32" ? [
19229
- path20.join(sourceRoot, "venv", "Scripts", "python.exe"),
19230
- path20.join(sourceRoot, ".venv", "Scripts", "python.exe")
19580
+ path21.join(sourceRoot, "venv", "Scripts", "python.exe"),
19581
+ path21.join(sourceRoot, ".venv", "Scripts", "python.exe")
19231
19582
  ] : [
19232
- path20.join(sourceRoot, "venv", "bin", "python"),
19233
- path20.join(sourceRoot, ".venv", "bin", "python")
19583
+ path21.join(sourceRoot, "venv", "bin", "python"),
19584
+ path21.join(sourceRoot, ".venv", "bin", "python")
19234
19585
  ];
19235
19586
  for (const candidate of candidates) {
19236
- if (await isExecutableFile(candidate)) {
19587
+ if (await isExecutableFile2(candidate)) {
19237
19588
  return candidate;
19238
19589
  }
19239
19590
  }
@@ -19259,8 +19610,8 @@ function shebangToPythonCommand(shebang) {
19259
19610
  }
19260
19611
  async function findDevHermesAgentSource() {
19261
19612
  const candidates = [
19262
- path20.resolve(process.cwd(), "reference/hermes-agent"),
19263
- path20.resolve(process.cwd(), "../../reference/hermes-agent")
19613
+ path21.resolve(process.cwd(), "reference/hermes-agent"),
19614
+ path21.resolve(process.cwd(), "../../reference/hermes-agent")
19264
19615
  ];
19265
19616
  for (const candidate of candidates) {
19266
19617
  if (await isHermesAgentSourceRoot(candidate)) {
@@ -19276,10 +19627,10 @@ async function readHermesLauncherTarget(filePath) {
19276
19627
  line
19277
19628
  );
19278
19629
  const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
19279
- if (!rawTarget || !path20.isAbsolute(rawTarget)) {
19630
+ if (!rawTarget || !path21.isAbsolute(rawTarget)) {
19280
19631
  continue;
19281
19632
  }
19282
- if (await isExecutableFile(rawTarget)) {
19633
+ if (await isExecutableFile2(rawTarget)) {
19283
19634
  return rawTarget;
19284
19635
  }
19285
19636
  }
@@ -19306,12 +19657,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
19306
19657
  return null;
19307
19658
  }
19308
19659
  async function findHermesSourceRoot(executablePath) {
19309
- let cursor = path20.dirname(path20.resolve(executablePath));
19660
+ let cursor = path21.dirname(path21.resolve(executablePath));
19310
19661
  for (let index = 0; index < 6; index += 1) {
19311
19662
  if (await isHermesAgentSourceRoot(cursor)) {
19312
19663
  return cursor;
19313
19664
  }
19314
- const parent = path20.dirname(cursor);
19665
+ const parent = path21.dirname(cursor);
19315
19666
  if (parent === cursor) {
19316
19667
  break;
19317
19668
  }
@@ -19322,14 +19673,14 @@ async function findHermesSourceRoot(executablePath) {
19322
19673
  function hermesSourceRootCandidates() {
19323
19674
  const candidates = [
19324
19675
  process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
19325
- path20.join(os4.homedir(), ".hermes", "hermes-agent"),
19676
+ path21.join(os5.homedir(), ".hermes", "hermes-agent"),
19326
19677
  "/usr/local/lib/hermes-agent",
19327
19678
  "/opt/hermes"
19328
19679
  ].filter((candidate) => Boolean(candidate));
19329
19680
  if (process.platform === "win32") {
19330
19681
  const localAppData = process.env.LOCALAPPDATA?.trim();
19331
19682
  if (localAppData) {
19332
- candidates.unshift(path20.join(localAppData, "hermes", "hermes-agent"));
19683
+ candidates.unshift(path21.join(localAppData, "hermes", "hermes-agent"));
19333
19684
  }
19334
19685
  }
19335
19686
  return candidates;
@@ -19339,7 +19690,7 @@ async function findExplicitHermesSourceRoot() {
19339
19690
  return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
19340
19691
  }
19341
19692
  async function isHermesAgentSourceRoot(candidate) {
19342
- return await isDirectory(candidate) && await isDirectory(path20.join(candidate, "tools")) && await isDirectory(path20.join(candidate, "hermes_cli"));
19693
+ return await isDirectory(candidate) && await isDirectory(path21.join(candidate, "tools")) && await isDirectory(path21.join(candidate, "hermes_cli"));
19343
19694
  }
19344
19695
  async function isDirectory(candidate) {
19345
19696
  return stat13(candidate).then((info) => info.isDirectory()).catch(() => false);
@@ -19351,13 +19702,13 @@ function compactProcessOutput(value) {
19351
19702
 
19352
19703
  // src/hermes/usage-probe.ts
19353
19704
  import { open as open3, readFile as readFile14, rm as rm6, stat as stat15 } from "fs/promises";
19354
- import path22 from "path";
19705
+ import path23 from "path";
19355
19706
  import YAML3 from "yaml";
19356
19707
 
19357
19708
  // src/hermes/profiles.ts
19358
19709
  import { execFile as execFile4 } from "child_process";
19359
19710
  import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat14 } from "fs/promises";
19360
- import path21 from "path";
19711
+ import path22 from "path";
19361
19712
  import { setTimeout as delay4 } from "timers/promises";
19362
19713
  import { promisify as promisify4 } from "util";
19363
19714
  import YAML2 from "yaml";
@@ -19500,7 +19851,7 @@ async function readHermesProfileCapabilities(name) {
19500
19851
  return {
19501
19852
  defaultModel: listedModels?.defaultModel ?? null,
19502
19853
  modelCount: listedModels?.models.length ?? 0,
19503
- skillCount: await countSkills(path21.join(profileDir, "skills")).catch(
19854
+ skillCount: await countSkills(path22.join(profileDir, "skills")).catch(
19504
19855
  () => 0
19505
19856
  ),
19506
19857
  toolCount: await countConfiguredTools(name).catch(() => 0)
@@ -19561,7 +19912,7 @@ async function hasDefaultProfileConfigSource() {
19561
19912
  if (!profileStat?.isDirectory()) {
19562
19913
  return false;
19563
19914
  }
19564
- return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path21.join(profilePath, ".env"));
19915
+ return await pathExists(resolveHermesConfigPath(DEFAULT_PROFILE)) || await pathExists(path22.join(profilePath, ".env"));
19565
19916
  }
19566
19917
  async function deleteHermesProfileWithCli(name) {
19567
19918
  try {
@@ -19731,7 +20082,7 @@ async function countSkills(root) {
19731
20082
  );
19732
20083
  let count = 0;
19733
20084
  for (const entry of entries) {
19734
- const entryPath = path21.join(root, entry.name);
20085
+ const entryPath = path22.join(root, entry.name);
19735
20086
  if (entry.name === ".git" || entry.name === ".hub") {
19736
20087
  continue;
19737
20088
  }
@@ -19824,7 +20175,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
19824
20175
  const normalizedProfile = normalizeProfileName4(profileName);
19825
20176
  const profilePath = resolveHermesProfileDir(normalizedProfile);
19826
20177
  const configPath = resolveHermesConfigPath(normalizedProfile);
19827
- const pluginPath = path22.join(
20178
+ const pluginPath = path23.join(
19828
20179
  profilePath,
19829
20180
  "plugins",
19830
20181
  HERMES_USAGE_PROBE_PLUGIN_KEY
@@ -19850,7 +20201,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
19850
20201
  return { ...base(), skipped: true };
19851
20202
  }
19852
20203
  try {
19853
- await ensureDirectoryWithInheritedMetadata(path22.dirname(eventsPath), 448);
20204
+ await ensureDirectoryWithInheritedMetadata(path23.dirname(eventsPath), 448);
19854
20205
  const state = await readUsageProbeState(paths);
19855
20206
  const pluginState = state.profiles[normalizedProfile];
19856
20207
  const currentConfig = await readUsageProbeConfigStatus({
@@ -19983,7 +20334,7 @@ async function findHermesUsageProbeEventForRun(input) {
19983
20334
  return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
19984
20335
  }
19985
20336
  function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
19986
- return path22.join(
20337
+ return path23.join(
19987
20338
  paths.homeDir,
19988
20339
  USAGE_PROBE_DIR,
19989
20340
  safeProfileSegment(profileName),
@@ -20007,8 +20358,8 @@ function summarizeUsageProbeEnsure(result) {
20007
20358
  };
20008
20359
  }
20009
20360
  async function writeUsageProbePlugin(input) {
20010
- const manifestPath = path22.join(input.pluginPath, "plugin.yaml");
20011
- const initPath = path22.join(input.pluginPath, "__init__.py");
20361
+ const manifestPath = path23.join(input.pluginPath, "plugin.yaml");
20362
+ const initPath = path23.join(input.pluginPath, "__init__.py");
20012
20363
  const manifest = usageProbeManifest();
20013
20364
  const source = usageProbePythonSource(
20014
20365
  input.profileName,
@@ -20355,9 +20706,9 @@ async function disableUsageProbeAfterActivationFailure(input) {
20355
20706
  });
20356
20707
  }
20357
20708
  async function cleanupLegacyUsageProbePlugin(profilePath) {
20358
- const legacyPath = path22.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
20709
+ const legacyPath = path23.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
20359
20710
  const [manifest, init] = await Promise.all([
20360
- readFile14(path22.join(legacyPath, "plugin.yaml"), "utf8").catch(
20711
+ readFile14(path23.join(legacyPath, "plugin.yaml"), "utf8").catch(
20361
20712
  (error) => {
20362
20713
  if (isNodeError15(error, "ENOENT")) {
20363
20714
  return null;
@@ -20365,7 +20716,7 @@ async function cleanupLegacyUsageProbePlugin(profilePath) {
20365
20716
  throw error;
20366
20717
  }
20367
20718
  ),
20368
- readFile14(path22.join(legacyPath, "__init__.py"), "utf8").catch(
20719
+ readFile14(path23.join(legacyPath, "__init__.py"), "utf8").catch(
20369
20720
  (error) => {
20370
20721
  if (isNodeError15(error, "ENOENT")) {
20371
20722
  return null;
@@ -20414,7 +20765,7 @@ async function rememberUsageProbeState(paths, profileName, patch) {
20414
20765
  }));
20415
20766
  }
20416
20767
  function usageProbeStatePath(paths) {
20417
- return path22.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
20768
+ return path23.join(paths.homeDir, USAGE_PROBE_DIR, USAGE_PROBE_STATE_FILE);
20418
20769
  }
20419
20770
  async function readHermesConfigDocument2(configPath, language) {
20420
20771
  const existingRaw = await readFile14(configPath, "utf8").catch(
@@ -21017,7 +21368,7 @@ function isRunToolResultCompensationEnabled(env = process.env) {
21017
21368
 
21018
21369
  // src/conversations/run-transcript-enrichment.ts
21019
21370
  import { readFile as readFile15, stat as stat16 } from "fs/promises";
21020
- import path23 from "path";
21371
+ import path24 from "path";
21021
21372
  var MESSAGE_COLUMNS2 = [
21022
21373
  "id",
21023
21374
  "session_id",
@@ -21093,8 +21444,8 @@ async function readRunFinalAssistantText(input) {
21093
21444
  }
21094
21445
  async function readHermesTranscriptRows(profileName, sessionId) {
21095
21446
  const profileDir = resolveHermesProfileDir(profileName);
21096
- const dbPath = path23.join(profileDir, "state.db");
21097
- const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path23.join(profileDir, "sessions"));
21447
+ const dbPath = path24.join(profileDir, "state.db");
21448
+ const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path24.join(profileDir, "sessions"));
21098
21449
  const [dbRows, jsonlRows] = await Promise.all([
21099
21450
  readStateDbMessages2(dbPath, sessionId),
21100
21451
  readJsonlMessages2(sessionsDir, sessionId)
@@ -21157,7 +21508,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
21157
21508
  if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
21158
21509
  return [];
21159
21510
  }
21160
- const transcriptPath = path23.join(sessionsDir, `${sessionId}.jsonl`);
21511
+ const transcriptPath = path24.join(sessionsDir, `${sessionId}.jsonl`);
21161
21512
  const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
21162
21513
  if (isNodeError16(error, "ENOENT")) {
21163
21514
  return "";
@@ -22150,6 +22501,7 @@ var ConversationRunLifecycle = class {
22150
22501
  profileName: run.profile,
22151
22502
  signal: controller.signal,
22152
22503
  logger: this.deps.logger,
22504
+ language: run.language === "en" ? "en" : "zh-CN",
22153
22505
  paths: this.deps.paths
22154
22506
  });
22155
22507
  await this.updateRun(conversationId, runId, {
@@ -23489,7 +23841,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
23489
23841
  payload: { message: assistant }
23490
23842
  });
23491
23843
  }
23492
- const contextUsage = contextUsagePayload(run);
23844
+ const contextUsage = contextUsagePayloadForSnapshot(snapshot);
23493
23845
  await this.deps.appendEvent(conversationId, {
23494
23846
  type: "run.completed",
23495
23847
  message_id: assistant?.id,
@@ -23557,7 +23909,7 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
23557
23909
  assistant?.id,
23558
23910
  expiredApprovals
23559
23911
  );
23560
- const contextUsage = contextUsagePayload(run);
23912
+ const contextUsage = contextUsagePayloadForSnapshot(snapshot);
23561
23913
  await this.deps.appendEvent(conversationId, {
23562
23914
  type: "run.failed",
23563
23915
  message_id: assistant?.id,
@@ -23760,11 +24112,16 @@ ${details.join("\n")}` : localizedEmptyHermesResponseMessage(language);
23760
24112
  payload: { message: assistant, cancelled: true }
23761
24113
  });
23762
24114
  }
24115
+ const contextUsage = contextUsagePayloadForSnapshot(snapshot);
23763
24116
  const event = await this.deps.appendEvent(conversationId, {
23764
24117
  type: "run.cancelled",
23765
24118
  message_id: assistant?.id,
23766
24119
  run_id: runId,
23767
- payload: { run, reason: options.reason }
24120
+ payload: {
24121
+ run,
24122
+ reason: options.reason,
24123
+ ...contextUsage ? { context: contextUsage } : {}
24124
+ }
23768
24125
  });
23769
24126
  await this.deps.persistConversationStats(conversationId, snapshot);
23770
24127
  void this.deps.logger.info("conversation_run_cancelled", {
@@ -24126,6 +24483,10 @@ function appendAgentEventBlock2(message, event, updatedAt) {
24126
24483
  }
24127
24484
  message.blocks = blocks;
24128
24485
  }
24486
+ function contextUsagePayloadForSnapshot(snapshot) {
24487
+ const run = latestContextUsageRun(snapshot);
24488
+ return run ? contextUsagePayload(run) : null;
24489
+ }
24129
24490
  function contextUsagePayload(run) {
24130
24491
  const usage = run.usage;
24131
24492
  const runtimeContext = resolveRuntimeContextUsage({
@@ -25612,7 +25973,7 @@ var ConversationService = class {
25612
25973
  }
25613
25974
  }
25614
25975
  hermesArchiveStateSyncMarkerPath() {
25615
- return path24.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
25976
+ return path25.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
25616
25977
  }
25617
25978
  prepareClearAllConversationPlan(targetStatus) {
25618
25979
  return this.maintenance.prepareClearAllConversationPlan(targetStatus);
@@ -27498,11 +27859,11 @@ function isSseRequestContext(ctx) {
27498
27859
  }
27499
27860
  return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
27500
27861
  }
27501
- function isSseRequestPath(path34) {
27502
- if (!path34) {
27862
+ function isSseRequestPath(path35) {
27863
+ if (!path35) {
27503
27864
  return false;
27504
27865
  }
27505
- return path34 === "/api/v1/conversations/events" || path34 === "/api/v1/profile-creation/events" || path34 === "/api/v1/hermes/update/events" || path34 === "/api/v1/link/update/events" || /^\/api\/v1\/conversations\/[^/]+\/events$/u.test(path34) || /^\/api\/v1\/runs\/[^/]+\/events$/u.test(path34);
27866
+ return path35 === "/api/v1/conversations/events" || path35 === "/api/v1/profile-creation/events" || path35 === "/api/v1/hermes/update/events" || path35 === "/api/v1/link/update/events" || /^\/api\/v1\/conversations\/[^/]+\/events$/u.test(path35) || /^\/api\/v1\/runs\/[^/]+\/events$/u.test(path35);
27506
27867
  }
27507
27868
  function isExpectedClientDisconnectError2(error, options = {}) {
27508
27869
  if (!(error instanceof Error)) {
@@ -28334,7 +28695,7 @@ function errorMessage3(error) {
28334
28695
 
28335
28696
  // src/hermes/profile-identity.ts
28336
28697
  import { readFile as readFile16, stat as stat17 } from "fs/promises";
28337
- import path25 from "path";
28698
+ import path26 from "path";
28338
28699
  var MAX_SOUL_MD_LENGTH = 2e4;
28339
28700
  async function readHermesProfileIdentity(profileName, paths) {
28340
28701
  await assertProfileExists3(profileName, paths);
@@ -28391,7 +28752,7 @@ async function assertProfileExists3(profileName, paths) {
28391
28752
  }
28392
28753
  }
28393
28754
  function resolveSoulPath(profileName) {
28394
- return path25.join(resolveHermesProfileDir(profileName), "SOUL.md");
28755
+ return path26.join(resolveHermesProfileDir(profileName), "SOUL.md");
28395
28756
  }
28396
28757
  function isNodeError18(error, code) {
28397
28758
  return error instanceof Error && "code" in error && error.code === code;
@@ -28407,13 +28768,13 @@ import {
28407
28768
  rm as rm7,
28408
28769
  stat as stat19
28409
28770
  } from "fs/promises";
28410
- import path27 from "path";
28771
+ import path28 from "path";
28411
28772
  import YAML5 from "yaml";
28412
28773
 
28413
28774
  // src/hermes/link-skill.ts
28414
28775
  import { readFile as readFile17, stat as stat18 } from "fs/promises";
28415
- import os5 from "os";
28416
- import path26 from "path";
28776
+ import os6 from "os";
28777
+ import path27 from "path";
28417
28778
  import YAML4 from "yaml";
28418
28779
  var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
28419
28780
  var HERMES_LINK_SKILL_DIR = "hermes-link";
@@ -28498,7 +28859,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
28498
28859
  async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
28499
28860
  const paths = options.paths ?? resolveRuntimePaths();
28500
28861
  const externalDir = resolveHermesLinkSkillExternalDir(paths);
28501
- const skillPath = path26.join(
28862
+ const skillPath = path27.join(
28502
28863
  externalDir,
28503
28864
  HERMES_LINK_SKILL_DIR,
28504
28865
  HERMES_LINK_SKILL_FILE
@@ -28580,7 +28941,7 @@ function withDefaultProfilePlaceholder2(profiles) {
28580
28941
  ];
28581
28942
  }
28582
28943
  function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
28583
- return path26.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
28944
+ return path27.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
28584
28945
  }
28585
28946
  async function writeHermesLinkSkill(skillPath) {
28586
28947
  const existing = await readFile17(skillPath, "utf8").catch((error) => {
@@ -28675,11 +29036,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
28675
29036
  const seen = new Set(
28676
29037
  entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
28677
29038
  );
28678
- const normalizedExternalDir = path26.resolve(externalDir);
29039
+ const normalizedExternalDir = path27.resolve(externalDir);
28679
29040
  return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
28680
29041
  }
28681
29042
  function externalDirsInclude(current, externalDir, hermesHome) {
28682
- const normalizedExternalDir = path26.resolve(externalDir);
29043
+ const normalizedExternalDir = path27.resolve(externalDir);
28683
29044
  return readExternalDirEntries(current).some(
28684
29045
  (entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
28685
29046
  );
@@ -28690,14 +29051,14 @@ function readExternalDirEntries(value) {
28690
29051
  }
28691
29052
  function resolveExternalDirEntry(entry, hermesHome) {
28692
29053
  const expanded = expandHome(expandEnvVars(entry));
28693
- return path26.resolve(path26.isAbsolute(expanded) ? expanded : path26.join(hermesHome, expanded));
29054
+ return path27.resolve(path27.isAbsolute(expanded) ? expanded : path27.join(hermesHome, expanded));
28694
29055
  }
28695
29056
  function expandHome(value) {
28696
29057
  if (value === "~") {
28697
- return os5.homedir();
29058
+ return os6.homedir();
28698
29059
  }
28699
- if (value.startsWith(`~${path26.sep}`) || value.startsWith("~/")) {
28700
- return path26.join(os5.homedir(), value.slice(2));
29060
+ if (value.startsWith(`~${path27.sep}`) || value.startsWith("~/")) {
29061
+ return path27.join(os6.homedir(), value.slice(2));
28701
29062
  }
28702
29063
  return value;
28703
29064
  }
@@ -29249,7 +29610,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
29249
29610
  return keys;
29250
29611
  }
29251
29612
  async function writeEnvValues(profileName, values) {
29252
- const envPath = path27.join(resolveHermesProfileDir(profileName), ".env");
29613
+ const envPath = path28.join(resolveHermesProfileDir(profileName), ".env");
29253
29614
  const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
29254
29615
  if (isNodeError20(error, "ENOENT")) {
29255
29616
  return "";
@@ -29286,8 +29647,8 @@ async function writeEnvValues(profileName, values) {
29286
29647
  await atomicWriteFilePreservingMetadata(envPath, nextRaw);
29287
29648
  }
29288
29649
  async function copySkills(sourceProfile, targetProfile) {
29289
- const sourceSkills = path27.join(resolveHermesProfileDir(sourceProfile), "skills");
29290
- const targetSkills = path27.join(resolveHermesProfileDir(targetProfile), "skills");
29650
+ const sourceSkills = path28.join(resolveHermesProfileDir(sourceProfile), "skills");
29651
+ const targetSkills = path28.join(resolveHermesProfileDir(targetProfile), "skills");
29291
29652
  if (!await pathExists2(sourceSkills)) {
29292
29653
  return;
29293
29654
  }
@@ -29382,10 +29743,10 @@ async function readProfileCreationLogLines(paths) {
29382
29743
  );
29383
29744
  }
29384
29745
  function profileCreationStatePath(paths) {
29385
- return path27.join(paths.runDir, "profile-create-state.json");
29746
+ return path28.join(paths.runDir, "profile-create-state.json");
29386
29747
  }
29387
29748
  function profileCreationLogPath(paths) {
29388
- return path27.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
29749
+ return path28.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
29389
29750
  }
29390
29751
  async function clearProfileCreationLogFiles(paths) {
29391
29752
  const primary = profileCreationLogPath(paths);
@@ -29674,7 +30035,7 @@ import {
29674
30035
  readFile as readFile19,
29675
30036
  stat as stat20
29676
30037
  } from "fs/promises";
29677
- import path28 from "path";
30038
+ import path29 from "path";
29678
30039
  import YAML6 from "yaml";
29679
30040
  var ENTRY_DELIMITER = "\n\xA7\n";
29680
30041
  var DEFAULT_MEMORY_LIMIT = 2200;
@@ -30005,7 +30366,7 @@ async function saveProviderSettings(profileName, provider, patch) {
30005
30366
  });
30006
30367
  await patchJsonProviderConfig(
30007
30368
  profileName,
30008
- path28.join("hindsight", "config.json"),
30369
+ path29.join("hindsight", "config.json"),
30009
30370
  {
30010
30371
  mode: patch.mode,
30011
30372
  api_url: patch.apiUrl,
@@ -30208,7 +30569,7 @@ async function patchHermesMemoryLimits(profileName, patch) {
30208
30569
  await atomicWriteFilePreservingMetadata(configPath, document.toString());
30209
30570
  }
30210
30571
  function resolveMemoryDir(profileName) {
30211
- return path28.join(resolveHermesProfileDir(profileName), "memories");
30572
+ return path29.join(resolveHermesProfileDir(profileName), "memories");
30212
30573
  }
30213
30574
  async function readMemoryStore(profileName, target, limits) {
30214
30575
  const filePath = memoryFilePath(profileName, target);
@@ -30269,7 +30630,7 @@ async function writeMemoryEntries(profileName, target, entries) {
30269
30630
  );
30270
30631
  }
30271
30632
  function memoryFilePath(profileName, target) {
30272
- return path28.join(
30633
+ return path29.join(
30273
30634
  resolveMemoryDir(profileName),
30274
30635
  target === "user" ? "USER.md" : "MEMORY.md"
30275
30636
  );
@@ -30329,7 +30690,7 @@ async function readCustomProviderSetupSummary(profileName) {
30329
30690
  configurable: true,
30330
30691
  configured: true,
30331
30692
  configurationIssue: null,
30332
- providerConfigPath: path28.join(
30693
+ providerConfigPath: path29.join(
30333
30694
  resolveHermesProfileDir(profileName),
30334
30695
  "<provider>.json"
30335
30696
  ),
@@ -30723,7 +31084,7 @@ async function readProviderSettings(profileName, provider) {
30723
31084
  stringSetting(
30724
31085
  "dbPath",
30725
31086
  "SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
30726
- config.db_path ?? path28.join(resolveHermesProfileDir(profileName), "memory_store.db")
31087
+ config.db_path ?? path29.join(resolveHermesProfileDir(profileName), "memory_store.db")
30727
31088
  ),
30728
31089
  booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
30729
31090
  numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
@@ -30759,7 +31120,7 @@ async function readProviderSettings(profileName, provider) {
30759
31120
  stringSetting(
30760
31121
  "workingDirectory",
30761
31122
  "\u5DE5\u4F5C\u76EE\u5F55",
30762
- path28.join(resolveHermesProfileDir(profileName), "byterover"),
31123
+ path29.join(resolveHermesProfileDir(profileName), "byterover"),
30763
31124
  false
30764
31125
  )
30765
31126
  ];
@@ -30768,16 +31129,16 @@ async function readProviderSettings(profileName, provider) {
30768
31129
  }
30769
31130
  function memoryProviderConfigPath(profileName, provider) {
30770
31131
  if (provider === "honcho") {
30771
- return path28.join(resolveHermesProfileDir(profileName), "honcho.json");
31132
+ return path29.join(resolveHermesProfileDir(profileName), "honcho.json");
30772
31133
  }
30773
31134
  if (provider === "mem0") {
30774
- return path28.join(resolveHermesProfileDir(profileName), "mem0.json");
31135
+ return path29.join(resolveHermesProfileDir(profileName), "mem0.json");
30775
31136
  }
30776
31137
  if (provider === "supermemory") {
30777
- return path28.join(resolveHermesProfileDir(profileName), "supermemory.json");
31138
+ return path29.join(resolveHermesProfileDir(profileName), "supermemory.json");
30778
31139
  }
30779
31140
  if (provider === "hindsight") {
30780
- return path28.join(
31141
+ return path29.join(
30781
31142
  resolveHermesProfileDir(profileName),
30782
31143
  "hindsight",
30783
31144
  "config.json"
@@ -30786,13 +31147,13 @@ function memoryProviderConfigPath(profileName, provider) {
30786
31147
  return null;
30787
31148
  }
30788
31149
  function customProviderConfigPath(profileName, provider) {
30789
- return path28.join(
31150
+ return path29.join(
30790
31151
  resolveHermesProfileDir(profileName),
30791
31152
  `${normalizeCustomProviderId(provider)}.json`
30792
31153
  );
30793
31154
  }
30794
31155
  function customProviderRegistryPath(profileName) {
30795
- return path28.join(
31156
+ return path29.join(
30796
31157
  resolveHermesProfileDir(profileName),
30797
31158
  CUSTOM_PROVIDER_REGISTRY_FILE
30798
31159
  );
@@ -30844,7 +31205,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
30844
31205
  );
30845
31206
  }
30846
31207
  async function discoverUserMemoryProviderDescriptors(profileName) {
30847
- const pluginsDir = path28.join(resolveHermesProfileDir(profileName), "plugins");
31208
+ const pluginsDir = path29.join(resolveHermesProfileDir(profileName), "plugins");
30848
31209
  const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
30849
31210
  (error) => {
30850
31211
  if (isNodeError21(error, "ENOENT")) {
@@ -30864,7 +31225,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
30864
31225
  } catch {
30865
31226
  continue;
30866
31227
  }
30867
- const providerDir = path28.join(pluginsDir, entry.name);
31228
+ const providerDir = path29.join(pluginsDir, entry.name);
30868
31229
  if (!await isMemoryProviderPluginDir(providerDir)) {
30869
31230
  continue;
30870
31231
  }
@@ -30878,7 +31239,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
30878
31239
  return descriptors;
30879
31240
  }
30880
31241
  async function isUserMemoryProviderInstalled(profileName, provider) {
30881
- const providerDir = path28.join(
31242
+ const providerDir = path29.join(
30882
31243
  resolveHermesProfileDir(profileName),
30883
31244
  "plugins",
30884
31245
  normalizeCustomProviderId(provider)
@@ -30886,7 +31247,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
30886
31247
  return isMemoryProviderPluginDir(providerDir);
30887
31248
  }
30888
31249
  async function isMemoryProviderPluginDir(providerDir) {
30889
- const source = await readFile19(path28.join(providerDir, "__init__.py"), "utf8").catch(
31250
+ const source = await readFile19(path29.join(providerDir, "__init__.py"), "utf8").catch(
30890
31251
  (error) => {
30891
31252
  if (isNodeError21(error, "ENOENT")) {
30892
31253
  return "";
@@ -30898,7 +31259,7 @@ async function isMemoryProviderPluginDir(providerDir) {
30898
31259
  return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
30899
31260
  }
30900
31261
  async function readPluginMetadata(providerDir) {
30901
- const raw = await readFile19(path28.join(providerDir, "plugin.yaml"), "utf8").catch(
31262
+ const raw = await readFile19(path29.join(providerDir, "plugin.yaml"), "utf8").catch(
30902
31263
  (error) => {
30903
31264
  if (isNodeError21(error, "ENOENT")) {
30904
31265
  return "";
@@ -30910,10 +31271,10 @@ async function readPluginMetadata(providerDir) {
30910
31271
  }
30911
31272
  async function resolveByteRoverCli() {
30912
31273
  const candidates = [
30913
- ...(process.env.PATH ?? "").split(path28.delimiter).filter(Boolean).map((dir) => path28.join(dir, "brv")),
30914
- path28.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
31274
+ ...(process.env.PATH ?? "").split(path29.delimiter).filter(Boolean).map((dir) => path29.join(dir, "brv")),
31275
+ path29.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
30915
31276
  "/usr/local/bin/brv",
30916
- path28.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
31277
+ path29.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
30917
31278
  ].filter(Boolean);
30918
31279
  for (const candidate of candidates) {
30919
31280
  const found = await access3(candidate).then(() => true).catch(() => false);
@@ -30974,7 +31335,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
30974
31335
  if (entries.length === 0) {
30975
31336
  return;
30976
31337
  }
30977
- const envPath = path28.join(resolveHermesProfileDir(profileName), ".env");
31338
+ const envPath = path29.join(resolveHermesProfileDir(profileName), ".env");
30978
31339
  const existingRaw = await readFile19(envPath, "utf8").catch((error) => {
30979
31340
  if (isNodeError21(error, "ENOENT")) {
30980
31341
  return "";
@@ -31145,7 +31506,7 @@ async function readActiveMemoryProvider(profileName) {
31145
31506
  return provider;
31146
31507
  }
31147
31508
  async function patchJsonProviderConfig(profileName, relativePath, patch) {
31148
- const configPath = path28.join(
31509
+ const configPath = path29.join(
31149
31510
  resolveHermesProfileDir(profileName),
31150
31511
  relativePath
31151
31512
  );
@@ -31174,7 +31535,7 @@ async function readJsonObject(filePath) {
31174
31535
  } catch {
31175
31536
  throw new HermesMemoryError(
31176
31537
  "memory_provider_config_invalid",
31177
- `${path28.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
31538
+ `${path29.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
31178
31539
  );
31179
31540
  }
31180
31541
  }
@@ -31798,7 +32159,7 @@ function toMemoryHttpError(error) {
31798
32159
 
31799
32160
  // src/hermes/skills.ts
31800
32161
  import { readFile as readFile20, readdir as readdir11 } from "fs/promises";
31801
- import path29 from "path";
32162
+ import path30 from "path";
31802
32163
  import YAML7 from "yaml";
31803
32164
  var HermesSkillNotFoundError = class extends Error {
31804
32165
  constructor(skillName) {
@@ -31812,7 +32173,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
31812
32173
  async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
31813
32174
  const profile = await readExistingProfile(profileName, paths);
31814
32175
  const profileDir = resolveHermesProfileDir(profile.name);
31815
- const skillsRoot = path29.join(profileDir, "skills");
32176
+ const skillsRoot = path30.join(profileDir, "skills");
31816
32177
  const [skillFiles, disabled, provenance] = await Promise.all([
31817
32178
  findSkillFiles(skillsRoot),
31818
32179
  readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
@@ -31903,7 +32264,7 @@ async function collectSkillFiles(directory, results) {
31903
32264
  if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
31904
32265
  continue;
31905
32266
  }
31906
- const entryPath = path29.join(directory, entry.name);
32267
+ const entryPath = path30.join(directory, entry.name);
31907
32268
  if (entry.isDirectory()) {
31908
32269
  await collectSkillFiles(entryPath, results);
31909
32270
  continue;
@@ -31925,10 +32286,10 @@ async function readSkillMetadata(input) {
31925
32286
  if (raw === null) {
31926
32287
  return null;
31927
32288
  }
31928
- const skillDir = path29.dirname(input.skillFile);
32289
+ const skillDir = path30.dirname(input.skillFile);
31929
32290
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
31930
32291
  const name = normalizeSkillName(
31931
- readString21(frontmatter.name) ?? path29.basename(skillDir)
32292
+ readString21(frontmatter.name) ?? path30.basename(skillDir)
31932
32293
  );
31933
32294
  if (!name) {
31934
32295
  return null;
@@ -31947,7 +32308,7 @@ async function readSkillMetadata(input) {
31947
32308
  enabled: !input.disabled.has(name),
31948
32309
  source: provenance.source,
31949
32310
  trust: provenance.trust,
31950
- relativePath: path29.relative(input.skillsRoot, skillDir)
32311
+ relativePath: path30.relative(input.skillsRoot, skillDir)
31951
32312
  };
31952
32313
  }
31953
32314
  function parseSkillDocument(raw) {
@@ -31968,8 +32329,8 @@ function parseSkillDocument(raw) {
31968
32329
  }
31969
32330
  }
31970
32331
  function categoryFromPath(skillsRoot, skillFile) {
31971
- const relative = path29.relative(skillsRoot, skillFile);
31972
- const parts = relative.split(path29.sep).filter(Boolean);
32332
+ const relative = path30.relative(skillsRoot, skillFile);
32333
+ const parts = relative.split(path30.sep).filter(Boolean);
31973
32334
  return parts.length >= 3 ? parts[0] : null;
31974
32335
  }
31975
32336
  function firstBodyDescription(body) {
@@ -32016,7 +32377,7 @@ async function readSkillProvenance(root) {
32016
32377
  return provenance;
32017
32378
  }
32018
32379
  async function readBundledSkillNames(root) {
32019
- const raw = await readFile20(path29.join(root, ".bundled_manifest"), "utf8").catch(
32380
+ const raw = await readFile20(path30.join(root, ".bundled_manifest"), "utf8").catch(
32020
32381
  (error) => {
32021
32382
  if (isNodeError22(error, "ENOENT")) {
32022
32383
  return "";
@@ -32039,7 +32400,7 @@ async function readBundledSkillNames(root) {
32039
32400
  return names;
32040
32401
  }
32041
32402
  async function readHubInstalledSkills(root) {
32042
- const raw = await readFile20(path29.join(root, ".hub", "lock.json"), "utf8").catch(
32403
+ const raw = await readFile20(path30.join(root, ".hub", "lock.json"), "utf8").catch(
32043
32404
  (error) => {
32044
32405
  if (isNodeError22(error, "ENOENT")) {
32045
32406
  return "";
@@ -32516,6 +32877,7 @@ function registerRunRoutes(router, options) {
32516
32877
  const { paths, logger, conversations } = options;
32517
32878
  router.post("/api/v1/runs", async (ctx) => {
32518
32879
  await authenticateRequest(ctx, paths);
32880
+ const language = readPreferredLanguage(ctx);
32519
32881
  const body = await readJsonBody(ctx.req);
32520
32882
  const input = readString19(body, "input");
32521
32883
  if (!input) {
@@ -32532,14 +32894,16 @@ function registerRunRoutes(router, options) {
32532
32894
  session_id: readString19(body, "session_id") ?? readString19(body, "sessionId") ?? void 0,
32533
32895
  session_key: readString19(body, "session_key") ?? readString19(body, "sessionKey") ?? void 0
32534
32896
  },
32535
- { logger, profileName: readOptionalProfileName(body) }
32897
+ { logger, profileName: readOptionalProfileName(body), language }
32536
32898
  );
32537
32899
  });
32538
32900
  router.get("/api/v1/runs/:runId/events", async (ctx) => {
32539
32901
  await authenticateRequest(ctx, paths);
32902
+ const language = readPreferredLanguage(ctx);
32540
32903
  const response = await streamHermesRunEvents(ctx.params.runId, {
32541
32904
  logger,
32542
- profileName: readQueryString(ctx.query.profile)
32905
+ profileName: readQueryString(ctx.query.profile),
32906
+ language
32543
32907
  });
32544
32908
  ctx.status = response.status;
32545
32909
  for (const [key, value] of response.headers.entries()) {
@@ -32561,6 +32925,7 @@ function registerRunRoutes(router, options) {
32561
32925
  });
32562
32926
  router.post("/api/v1/runs/:runId/cancel", async (ctx) => {
32563
32927
  await authenticateRequest(ctx, paths);
32928
+ const language = readPreferredLanguage(ctx);
32564
32929
  try {
32565
32930
  ctx.body = {
32566
32931
  ok: true,
@@ -32572,7 +32937,8 @@ function registerRunRoutes(router, options) {
32572
32937
  }
32573
32938
  await cancelHermesRun(ctx.params.runId, {
32574
32939
  logger,
32575
- profileName: readQueryString(ctx.query.profile)
32940
+ profileName: readQueryString(ctx.query.profile),
32941
+ language
32576
32942
  });
32577
32943
  ctx.body = { ok: true };
32578
32944
  }
@@ -32702,7 +33068,7 @@ function readModelList(payload) {
32702
33068
  import { EventEmitter as EventEmitter3 } from "events";
32703
33069
  import { spawn as spawn4 } from "child_process";
32704
33070
  import { mkdir as mkdir13, readFile as readFile21, rm as rm8 } from "fs/promises";
32705
- import path30 from "path";
33071
+ import path31 from "path";
32706
33072
  var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
32707
33073
  var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
32708
33074
  var RELEASE_FETCH_TIMEOUT_MS = 5e3;
@@ -32980,13 +33346,13 @@ async function readUpdateLogLines(paths) {
32980
33346
  );
32981
33347
  }
32982
33348
  function releaseCachePath(paths) {
32983
- return path30.join(paths.indexesDir, "hermes-release-check.json");
33349
+ return path31.join(paths.indexesDir, "hermes-release-check.json");
32984
33350
  }
32985
33351
  function updateStatePath(paths) {
32986
- return path30.join(paths.runDir, "hermes-update-state.json");
33352
+ return path31.join(paths.runDir, "hermes-update-state.json");
32987
33353
  }
32988
33354
  function updateLogPath(paths) {
32989
- return path30.join(paths.logsDir, UPDATE_LOG_FILE);
33355
+ return path31.join(paths.logsDir, UPDATE_LOG_FILE);
32990
33356
  }
32991
33357
  async function clearUpdateLogFiles(paths) {
32992
33358
  const primary = updateLogPath(paths);
@@ -33087,12 +33453,12 @@ function readString22(payload, key) {
33087
33453
  import { spawn as spawn6 } from "child_process";
33088
33454
  import { EventEmitter as EventEmitter4 } from "events";
33089
33455
  import { mkdir as mkdir16, readFile as readFile23, rm as rm11 } from "fs/promises";
33090
- import path32 from "path";
33456
+ import path33 from "path";
33091
33457
 
33092
33458
  // src/daemon/process.ts
33093
33459
  import { spawn as spawn5 } from "child_process";
33094
33460
  import { mkdir as mkdir15, readFile as readFile22, rm as rm10, writeFile as writeFile4 } from "fs/promises";
33095
- import path31 from "path";
33461
+ import path32 from "path";
33096
33462
 
33097
33463
  // src/daemon/service.ts
33098
33464
  import { createServer } from "http";
@@ -33768,8 +34134,8 @@ function normalizeMessage(value) {
33768
34134
 
33769
34135
  // src/runtime/system-info.ts
33770
34136
  import { execFileSync } from "child_process";
33771
- import { readFileSync } from "fs";
33772
- import os6 from "os";
34137
+ import { readFileSync as readFileSync2 } from "fs";
34138
+ import os7 from "os";
33773
34139
  function readLinkSystemInfo() {
33774
34140
  const platform = process.platform;
33775
34141
  const hostname = readHostname(platform);
@@ -33808,7 +34174,7 @@ function readHostname(platform) {
33808
34174
  return computerName;
33809
34175
  }
33810
34176
  }
33811
- return normalizeText(os6.hostname());
34177
+ return normalizeText(os7.hostname());
33812
34178
  }
33813
34179
  function readOsLabel(platform) {
33814
34180
  if (platform === "darwin") {
@@ -33816,17 +34182,17 @@ function readOsLabel(platform) {
33816
34182
  return version ? `macOS ${version}` : "macOS";
33817
34183
  }
33818
34184
  if (platform === "linux") {
33819
- return readLinuxOsRelease() ?? `Linux ${os6.release()}`;
34185
+ return readLinuxOsRelease() ?? `Linux ${os7.release()}`;
33820
34186
  }
33821
34187
  if (platform === "win32") {
33822
- return `Windows ${os6.release()}`;
34188
+ return `Windows ${os7.release()}`;
33823
34189
  }
33824
- return `${os6.type()} ${os6.release()}`.trim();
34190
+ return `${os7.type()} ${os7.release()}`.trim();
33825
34191
  }
33826
34192
  function readLinuxOsRelease() {
33827
34193
  for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
33828
34194
  try {
33829
- return parseLinuxOsRelease(readFileSync(file, "utf8"));
34195
+ return parseLinuxOsRelease(readFileSync2(file, "utf8"));
33830
34196
  } catch {
33831
34197
  }
33832
34198
  }
@@ -33868,11 +34234,11 @@ function truncateText(value, maxLength) {
33868
34234
  }
33869
34235
 
33870
34236
  // src/topology/network.ts
33871
- import os8 from "os";
34237
+ import os9 from "os";
33872
34238
 
33873
34239
  // src/topology/environment.ts
33874
- import { existsSync, readFileSync as readFileSync2 } from "fs";
33875
- import os7 from "os";
34240
+ import { existsSync, readFileSync as readFileSync3 } from "fs";
34241
+ import os8 from "os";
33876
34242
  function detectRuntimeEnvironment(env = process.env) {
33877
34243
  if (isWsl(env)) {
33878
34244
  return {
@@ -33901,7 +34267,7 @@ function isWsl(env) {
33901
34267
  if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
33902
34268
  return true;
33903
34269
  }
33904
- const release = os7.release().toLowerCase();
34270
+ const release = os8.release().toLowerCase();
33905
34271
  return release.includes("microsoft") || release.includes("wsl");
33906
34272
  }
33907
34273
  function isContainer(env) {
@@ -33912,7 +34278,7 @@ function isContainer(env) {
33912
34278
  return true;
33913
34279
  }
33914
34280
  try {
33915
- const cgroup = readFileSync2("/proc/1/cgroup", "utf8").toLowerCase();
34281
+ const cgroup = readFileSync3("/proc/1/cgroup", "utf8").toLowerCase();
33916
34282
  return /docker|containerd|kubepods|libpod|podman/u.test(cgroup);
33917
34283
  } catch {
33918
34284
  return false;
@@ -33946,7 +34312,7 @@ async function discoverRouteCandidates(options) {
33946
34312
  };
33947
34313
  }
33948
34314
  function discoverLanIps() {
33949
- return discoverLanIpsFromInterfaces(os8.networkInterfaces());
34315
+ return discoverLanIpsFromInterfaces(os9.networkInterfaces());
33950
34316
  }
33951
34317
  function discoverLanIpsFromInterfaces(interfaces) {
33952
34318
  const result = /* @__PURE__ */ new Set();
@@ -35039,7 +35405,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
35039
35405
  await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
35040
35406
  const log = createRotatingTextLogWriter({
35041
35407
  paths,
35042
- fileName: path31.basename(daemonLogFile(paths))
35408
+ fileName: path32.basename(daemonLogFile(paths))
35043
35409
  });
35044
35410
  const scriptPath = currentCliScriptPath();
35045
35411
  const write = (chunk) => {
@@ -35337,7 +35703,7 @@ function terminateChild(child, previousForceKillTimer) {
35337
35703
  }
35338
35704
  }
35339
35705
  function supervisorStopIntentPath(paths) {
35340
- return path31.join(paths.runDir, "supervisor-stop-intent.json");
35706
+ return path32.join(paths.runDir, "supervisor-stop-intent.json");
35341
35707
  }
35342
35708
  async function writeSupervisorStopIntent(paths, pid) {
35343
35709
  await mkdir15(paths.runDir, { recursive: true, mode: 448 });
@@ -35900,7 +36266,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
35900
36266
  };
35901
36267
  }
35902
36268
  function buildUnixInstallCommand(installerUrl) {
35903
- const nodeBinDir = path32.dirname(process.execPath);
36269
+ const nodeBinDir = path33.dirname(process.execPath);
35904
36270
  const fetchScript = [
35905
36271
  quoteShellToken(process.execPath),
35906
36272
  "--input-type=module",
@@ -36170,10 +36536,10 @@ async function readUpdateLogLines2(paths) {
36170
36536
  );
36171
36537
  }
36172
36538
  function updateStatePath2(paths) {
36173
- return path32.join(paths.runDir, "link-update-state.json");
36539
+ return path33.join(paths.runDir, "link-update-state.json");
36174
36540
  }
36175
36541
  function updateLogPath2(paths) {
36176
- return path32.join(paths.logsDir, UPDATE_LOG_FILE2);
36542
+ return path33.join(paths.logsDir, UPDATE_LOG_FILE2);
36177
36543
  }
36178
36544
  async function clearUpdateLogFiles2(paths) {
36179
36545
  const primary = updateLogPath2(paths);
@@ -36257,7 +36623,7 @@ function readString23(payload, key) {
36257
36623
  }
36258
36624
 
36259
36625
  // src/pairing/pairing.ts
36260
- import path33 from "path";
36626
+ import path34 from "path";
36261
36627
  import { rm as rm12 } from "fs/promises";
36262
36628
 
36263
36629
  // src/relay/bootstrap.ts
@@ -36597,10 +36963,10 @@ async function loadRequiredIdentity2(paths) {
36597
36963
  }
36598
36964
  return identity;
36599
36965
  }
36600
- async function postServerJson(serverBaseUrl, path34, body, options) {
36966
+ async function postServerJson(serverBaseUrl, path35, body, options) {
36601
36967
  let response;
36602
36968
  try {
36603
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path34}`, {
36969
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
36604
36970
  method: "POST",
36605
36971
  headers: {
36606
36972
  accept: "application/json",
@@ -36648,10 +37014,10 @@ function pairingErrorSnapshot(stage, error) {
36648
37014
  occurred_at: (/* @__PURE__ */ new Date()).toISOString()
36649
37015
  };
36650
37016
  }
36651
- async function patchServerJson(serverBaseUrl, path34, token, body, options) {
37017
+ async function patchServerJson(serverBaseUrl, path35, token, body, options) {
36652
37018
  let response;
36653
37019
  try {
36654
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path34}`, {
37020
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
36655
37021
  method: "PATCH",
36656
37022
  headers: {
36657
37023
  accept: "application/json",
@@ -36699,10 +37065,10 @@ function createPairingNetworkError(input) {
36699
37065
  );
36700
37066
  }
36701
37067
  function pairingClaimPath(sessionId, paths) {
36702
- return path33.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
37068
+ return path34.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
36703
37069
  }
36704
37070
  function pairingSessionPath(sessionId, paths) {
36705
- return path33.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
37071
+ return path34.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
36706
37072
  }
36707
37073
  function qrPreferredUrls(routes) {
36708
37074
  return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
@@ -38029,6 +38395,7 @@ export {
38029
38395
  getGatewayLogFiles,
38030
38396
  readRecentGatewayLogEntries,
38031
38397
  flushLogFiles,
38398
+ resolveHermesBin,
38032
38399
  HermesApiServerUnavailableError,
38033
38400
  ensureHermesApiServerAvailable,
38034
38401
  readHermesVersion,