@hermespilot/link 0.7.8-beta.0 → 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.8-beta.0";
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,7 +8808,7 @@ 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;
@@ -8657,7 +8834,8 @@ async function streamTuiGatewayRun(input) {
8657
8834
  profileName,
8658
8835
  paths: input.paths,
8659
8836
  logger: input.logger,
8660
- signal: input.signal
8837
+ signal: input.signal,
8838
+ language: input.language
8661
8839
  });
8662
8840
  const started = await ensureRpcSession(client, {
8663
8841
  ...input,
@@ -8961,9 +9139,10 @@ async function startTuiGatewayBackend(input) {
8961
9139
  ];
8962
9140
  const logWriter = createRotatingTextLogWriter({
8963
9141
  paths,
8964
- fileName: path8.basename(getGatewayRuntimeLogFile(paths))
9142
+ fileName: path9.basename(getGatewayRuntimeLogFile(paths))
8965
9143
  });
8966
- const child = spawn2(resolveHermesBin(), args, {
9144
+ const hermesBin = resolveHermesBin();
9145
+ const child = spawn2(hermesBin, args, {
8967
9146
  env: {
8968
9147
  ...process.env,
8969
9148
  HERMES_DASHBOARD_SESSION_TOKEN: token
@@ -8988,7 +9167,13 @@ async function startTuiGatewayBackend(input) {
8988
9167
  signal
8989
9168
  });
8990
9169
  });
8991
- await waitForChildSpawn(child, input.signal);
9170
+ await waitForChildSpawn(
9171
+ child,
9172
+ input.signal,
9173
+ input.logger,
9174
+ hermesBin,
9175
+ input.language
9176
+ );
8992
9177
  const baseUrl = `http://127.0.0.1:${port}`;
8993
9178
  const wsUrl = `ws://127.0.0.1:${port}/api/ws?token=${encodeURIComponent(token)}`;
8994
9179
  try {
@@ -9764,7 +9949,7 @@ async function readLogTailSummary(logPath) {
9764
9949
  return "";
9765
9950
  }
9766
9951
  }
9767
- async function waitForChildSpawn(child, signal) {
9952
+ async function waitForChildSpawn(child, signal, logger, hermesBin = resolveHermesBin(), language = "zh-CN") {
9768
9953
  await new Promise((resolve, reject) => {
9769
9954
  const abort = () => {
9770
9955
  child.kill();
@@ -9787,6 +9972,14 @@ async function waitForChildSpawn(child, signal) {
9787
9972
  };
9788
9973
  const onError = (error) => {
9789
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
+ }
9790
9983
  reject(error);
9791
9984
  };
9792
9985
  signal?.addEventListener("abort", abort, { once: true });
@@ -10452,11 +10645,11 @@ function workspaceNotFound() {
10452
10645
  // src/conversations/blob-store.ts
10453
10646
  import { randomUUID as randomUUID5 } from "crypto";
10454
10647
  import { mkdir as mkdir6, readFile as readFile7, readdir as readdir4, rm as rm3, stat as stat6, writeFile } from "fs/promises";
10455
- import path10 from "path";
10648
+ import path11 from "path";
10456
10649
 
10457
10650
  // src/conversations/media.ts
10458
10651
  import { createHash as createHash2 } from "crypto";
10459
- import path9 from "path";
10652
+ import path10 from "path";
10460
10653
 
10461
10654
  // src/conversations/delivery-contract.ts
10462
10655
  var HERMES_LINK_DELIVERY_TAG_PATTERN = /<hermes_link_delivery>\s*(\{[\s\S]*?\})\s*<\/hermes_link_delivery>/giu;
@@ -10546,7 +10739,7 @@ function cleanMessageTextParts(message) {
10546
10739
  }
10547
10740
  }
10548
10741
  function inferMimeType(filePath) {
10549
- const extension = path9.extname(filePath).toLowerCase();
10742
+ const extension = path10.extname(filePath).toLowerCase();
10550
10743
  return {
10551
10744
  ".png": "image/png",
10552
10745
  ".jpg": "image/jpeg",
@@ -10670,7 +10863,7 @@ function mediaSourceKey(sourcePath) {
10670
10863
  return createHash2("sha256").update(resolveMediaSourcePath(sourcePath)).digest("hex").slice(0, 32);
10671
10864
  }
10672
10865
  function sanitizeFilename(value, fallback) {
10673
- const base = path9.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
10866
+ const base = path10.basename((value ?? "").replace(/[\r\n\t]/gu, " ").trim());
10674
10867
  const safe = base.replace(/[/:\\]/gu, "_").slice(0, 200).trim();
10675
10868
  return safe || fallback;
10676
10869
  }
@@ -10699,9 +10892,9 @@ function isBlobReferencedByMessages(snapshot, blobId) {
10699
10892
  }
10700
10893
  function resolveMediaSourcePath(sourcePath) {
10701
10894
  const trimmed = sourcePath.trim();
10702
- const expanded = trimmed.startsWith("~/") ? path9.join(process.env.HOME ?? "", trimmed.slice(2)) : trimmed;
10703
- const resolved = path9.resolve(expanded);
10704
- 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)) {
10705
10898
  throw new LinkHttpError(
10706
10899
  400,
10707
10900
  "media_source_path_not_absolute",
@@ -10896,7 +11089,7 @@ function normalizeConversationIds(conversationIds) {
10896
11089
  }
10897
11090
  function blobPath(paths, blobId) {
10898
11091
  assertValidBlobId(blobId);
10899
- return path10.join(paths.blobsDir, `${blobId}.bin`);
11092
+ return path11.join(paths.blobsDir, `${blobId}.bin`);
10900
11093
  }
10901
11094
  async function writeConversationBlob(paths, conversationId, input, options) {
10902
11095
  assertValidConversationId(conversationId);
@@ -10905,7 +11098,7 @@ async function writeConversationBlob(paths, conversationId, input, options) {
10905
11098
  }
10906
11099
  const id = `blob_${randomUUID5().replaceAll("-", "")}`;
10907
11100
  const filePath = blobPath(paths, id);
10908
- await mkdir6(path10.dirname(filePath), { recursive: true, mode: 448 });
11101
+ await mkdir6(path11.dirname(filePath), { recursive: true, mode: 448 });
10909
11102
  await writeFile(filePath, input.bytes, { mode: 384 });
10910
11103
  const blob = {
10911
11104
  id,
@@ -10981,7 +11174,7 @@ async function materializeConversationBlob(paths, conversationId, blobId, manife
10981
11174
  }
10982
11175
  }
10983
11176
  const targetDir = conversationAttachmentsDir(paths, conversationId);
10984
- const targetPath = path10.join(
11177
+ const targetPath = path11.join(
10985
11178
  targetDir,
10986
11179
  materializedAttachmentFilename(blobId, manifest.filename ?? blobId)
10987
11180
  );
@@ -11034,7 +11227,7 @@ async function listConversationBlobIds(paths, conversationId) {
11034
11227
  continue;
11035
11228
  }
11036
11229
  const manifest = await readJsonFile(
11037
- path10.join(paths.blobsDir, entry.name)
11230
+ path11.join(paths.blobsDir, entry.name)
11038
11231
  ).catch(() => null);
11039
11232
  if (manifest?.conversation_ids?.includes(conversationId)) {
11040
11233
  blobIds.push(blobId);
@@ -11044,7 +11237,7 @@ async function listConversationBlobIds(paths, conversationId) {
11044
11237
  }
11045
11238
  function conversationAttachmentsDir(paths, conversationId) {
11046
11239
  assertValidConversationId(conversationId);
11047
- return path10.join(paths.conversationsDir, conversationId, "attachments");
11240
+ return path11.join(paths.conversationsDir, conversationId, "attachments");
11048
11241
  }
11049
11242
  function isNodeError6(error, code) {
11050
11243
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
@@ -12079,7 +12272,7 @@ function commandText(language, zh, en) {
12079
12272
 
12080
12273
  // src/conversations/delivery-staging.ts
12081
12274
  import { mkdir as mkdir7, rm as rm4 } from "fs/promises";
12082
- import path11 from "path";
12275
+ import path12 from "path";
12083
12276
  async function prepareDeliveryStagingRunDir(paths, conversationId, runId) {
12084
12277
  const directory = deliveryStagingRunDir(paths, conversationId, runId);
12085
12278
  await mkdir7(directory, { recursive: true, mode: 448 });
@@ -12092,14 +12285,14 @@ async function removeConversationDeliveryStaging(paths, conversationId) {
12092
12285
  });
12093
12286
  }
12094
12287
  function deliveryStagingRunDir(paths, conversationId, runId) {
12095
- return path11.join(
12288
+ return path12.join(
12096
12289
  deliveryStagingConversationDir(paths, conversationId),
12097
12290
  safePathSegment(runId, "run")
12098
12291
  );
12099
12292
  }
12100
12293
  function deliveryStagingConversationDir(paths, conversationId) {
12101
12294
  assertValidConversationId(conversationId);
12102
- return path11.join(paths.conversationsDir, conversationId, "delivery-staging");
12295
+ return path12.join(paths.conversationsDir, conversationId, "delivery-staging");
12103
12296
  }
12104
12297
  function safePathSegment(value, fallback) {
12105
12298
  const safe = value.trim().replaceAll(/[^a-zA-Z0-9._-]/gu, "_");
@@ -12109,7 +12302,7 @@ function safePathSegment(value, fallback) {
12109
12302
  // src/conversations/conversation-archive-plans.ts
12110
12303
  import { randomUUID as randomUUID7 } from "crypto";
12111
12304
  import { mkdir as mkdir8 } from "fs/promises";
12112
- import path12 from "path";
12305
+ import path13 from "path";
12113
12306
  var PLAN_ID_PATTERN = /^archive_[a-f0-9]{32}$/u;
12114
12307
  var ConversationArchivePlanStore = class {
12115
12308
  constructor(paths) {
@@ -12152,10 +12345,10 @@ var ConversationArchivePlanStore = class {
12152
12345
  await writeJsonFile(this.planPath(normalizedPlanId), plan);
12153
12346
  }
12154
12347
  plansDir() {
12155
- return path12.join(this.paths.indexesDir, "conversation-archive-plans");
12348
+ return path13.join(this.paths.indexesDir, "conversation-archive-plans");
12156
12349
  }
12157
12350
  planPath(planId) {
12158
- return path12.join(this.plansDir(), `${planId}.json`);
12351
+ return path13.join(this.plansDir(), `${planId}.json`);
12159
12352
  }
12160
12353
  };
12161
12354
  function normalizePlanId(planId) {
@@ -12173,7 +12366,7 @@ function normalizePlanId(planId) {
12173
12366
  // src/conversations/conversation-clear-plans.ts
12174
12367
  import { randomUUID as randomUUID8 } from "crypto";
12175
12368
  import { mkdir as mkdir9 } from "fs/promises";
12176
- import path13 from "path";
12369
+ import path14 from "path";
12177
12370
  var PLAN_ID_PATTERN2 = /^clear_[a-f0-9]{32}$/u;
12178
12371
  var ConversationClearPlanStore = class {
12179
12372
  constructor(paths) {
@@ -12217,10 +12410,10 @@ var ConversationClearPlanStore = class {
12217
12410
  await writeJsonFile(this.planPath(normalizedPlanId), plan);
12218
12411
  }
12219
12412
  plansDir() {
12220
- return path13.join(this.paths.indexesDir, "conversation-clear-plans");
12413
+ return path14.join(this.paths.indexesDir, "conversation-clear-plans");
12221
12414
  }
12222
12415
  planPath(planId) {
12223
- return path13.join(this.plansDir(), `${planId}.json`);
12416
+ return path14.join(this.plansDir(), `${planId}.json`);
12224
12417
  }
12225
12418
  };
12226
12419
  function normalizePlanId2(planId) {
@@ -13059,14 +13252,14 @@ function readAttachmentWaveform(attachment) {
13059
13252
 
13060
13253
  // src/hermes/session-title.ts
13061
13254
  import { stat as stat8 } from "fs/promises";
13062
- import path14 from "path";
13255
+ import path15 from "path";
13063
13256
  async function readHermesSessionTitle(sessionId, paths, profileName) {
13064
13257
  const trimmedSessionId = sessionId.trim();
13065
13258
  if (!trimmedSessionId) {
13066
13259
  return void 0;
13067
13260
  }
13068
13261
  const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
13069
- const dbPath = path14.join(
13262
+ const dbPath = path15.join(
13070
13263
  resolveHermesProfileDir(resolvedProfileName),
13071
13264
  "state.db"
13072
13265
  );
@@ -13087,7 +13280,7 @@ async function readHermesCompressionTip(sessionId, paths, profileName) {
13087
13280
  return void 0;
13088
13281
  }
13089
13282
  const resolvedProfileName = isValidProfileName(profileName) ? profileName : "default";
13090
- const dbPath = path14.join(
13283
+ const dbPath = path15.join(
13091
13284
  resolveHermesProfileDir(resolvedProfileName),
13092
13285
  "state.db"
13093
13286
  );
@@ -13863,10 +14056,15 @@ var ConversationOrchestrationCoordinator = class {
13863
14056
  return;
13864
14057
  }
13865
14058
  try {
14059
+ const failureMessage = await this.formatRunWorkerFailureMessage(
14060
+ conversationId,
14061
+ runId,
14062
+ error
14063
+ );
13866
14064
  await this.deps.runLifecycle.failRun(
13867
14065
  conversationId,
13868
14066
  runId,
13869
- error instanceof Error ? error.message : String(error)
14067
+ failureMessage
13870
14068
  );
13871
14069
  } catch (failError) {
13872
14070
  if (isConversationNotFoundError(failError)) {
@@ -13893,6 +14091,26 @@ var ConversationOrchestrationCoordinator = class {
13893
14091
  });
13894
14092
  });
13895
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
+ }
13896
14114
  async startNextQueuedRunLocked(conversationId) {
13897
14115
  const manifest = await this.deps.store.readRunnableManifest(conversationId);
13898
14116
  const snapshot = await this.deps.store.readSnapshot(conversationId);
@@ -15430,7 +15648,7 @@ import {
15430
15648
  rm as rm5,
15431
15649
  writeFile as writeFile2
15432
15650
  } from "fs/promises";
15433
- import path15 from "path";
15651
+ import path16 from "path";
15434
15652
  var ConversationStore = class {
15435
15653
  constructor(paths) {
15436
15654
  this.paths = paths;
@@ -15561,23 +15779,23 @@ var ConversationStore = class {
15561
15779
  return manifest != null && manifest.status !== "deleted_soft";
15562
15780
  }
15563
15781
  removeConversationAttachments(conversationId) {
15564
- return rm5(path15.join(this.conversationDir(conversationId), "attachments"), {
15782
+ return rm5(path16.join(this.conversationDir(conversationId), "attachments"), {
15565
15783
  recursive: true,
15566
15784
  force: true
15567
15785
  });
15568
15786
  }
15569
15787
  conversationDir(conversationId) {
15570
15788
  assertValidConversationId(conversationId);
15571
- return path15.join(this.paths.conversationsDir, conversationId);
15789
+ return path16.join(this.paths.conversationsDir, conversationId);
15572
15790
  }
15573
15791
  manifestPath(conversationId) {
15574
- return path15.join(this.conversationDir(conversationId), "manifest.json");
15792
+ return path16.join(this.conversationDir(conversationId), "manifest.json");
15575
15793
  }
15576
15794
  snapshotPath(conversationId) {
15577
- return path15.join(this.conversationDir(conversationId), "snapshot.json");
15795
+ return path16.join(this.conversationDir(conversationId), "snapshot.json");
15578
15796
  }
15579
15797
  eventsPath(conversationId) {
15580
- return path15.join(this.conversationDir(conversationId), "events.ndjson");
15798
+ return path16.join(this.conversationDir(conversationId), "events.ndjson");
15581
15799
  }
15582
15800
  };
15583
15801
  function createEmptySnapshot2() {
@@ -15590,11 +15808,11 @@ function isNodeError9(error, code) {
15590
15808
  // src/conversations/hermes-session-sync.ts
15591
15809
  import { randomUUID as randomUUID10 } from "crypto";
15592
15810
  import { readdir as readdir7, readFile as readFile10, stat as stat10 } from "fs/promises";
15593
- import path17 from "path";
15811
+ import path18 from "path";
15594
15812
 
15595
15813
  // src/conversations/delivery-import.ts
15596
15814
  import { lstat as lstat2, readFile as readFile9, readdir as readdir6, stat as stat9 } from "fs/promises";
15597
- import path16 from "path";
15815
+ import path17 from "path";
15598
15816
  var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
15599
15817
  var MAX_MEDIA_IMPORT_FAILURES = 20;
15600
15818
  var MAX_DELIVERY_FILES = 50;
@@ -15665,16 +15883,16 @@ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
15665
15883
  ".m4a"
15666
15884
  ]);
15667
15885
  function resolveDeliveryStagingTarget(paths, stagingDir) {
15668
- const resolvedDir = path16.resolve(stagingDir);
15669
- const relative = path16.relative(path16.resolve(paths.conversationsDir), resolvedDir);
15670
- 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)) {
15671
15889
  throw new LinkHttpError(
15672
15890
  400,
15673
15891
  "delivery_staging_invalid",
15674
15892
  "delivery staging directory must be inside Hermes Link conversations"
15675
15893
  );
15676
15894
  }
15677
- const segments = relative.split(path16.sep);
15895
+ const segments = relative.split(path17.sep);
15678
15896
  if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
15679
15897
  throw new LinkHttpError(
15680
15898
  400,
@@ -15710,7 +15928,7 @@ async function collectStagedDeliveryReferences(stagingDir) {
15710
15928
  return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
15711
15929
  (left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
15712
15930
  ).slice(0, MAX_DELIVERY_FILES).map((entry) => {
15713
- const sourcePath = path16.join(stagingDir, entry.name);
15931
+ const sourcePath = path17.join(stagingDir, entry.name);
15714
15932
  const mime = inferMimeType(sourcePath);
15715
15933
  return {
15716
15934
  path: sourcePath,
@@ -15896,7 +16114,7 @@ async function writeBlobFromFile(deps, conversationId, source) {
15896
16114
  }
15897
16115
  return deps.writeBlob(conversationId, {
15898
16116
  bytes: await readFile9(sourcePath),
15899
- filename: path16.basename(sourcePath),
16117
+ filename: path17.basename(sourcePath),
15900
16118
  mime: source.mime ?? inferMimeType(sourcePath)
15901
16119
  });
15902
16120
  }
@@ -15909,7 +16127,7 @@ function describeMediaImportFailure(reference, sourceKey, error) {
15909
16127
  };
15910
16128
  }
15911
16129
  function isSupportedDeliveryFilename(filename) {
15912
- return SUPPORTED_DELIVERY_EXTENSIONS.has(path16.extname(filename).toLowerCase());
16130
+ return SUPPORTED_DELIVERY_EXTENSIONS.has(path17.extname(filename).toLowerCase());
15913
16131
  }
15914
16132
  function readString9(payload, key) {
15915
16133
  const value = payload[key];
@@ -15967,7 +16185,7 @@ async function syncHermesSessionsIntoConversations(paths, logger, options = {})
15967
16185
  const candidates = [];
15968
16186
  for (const profileName of profileNames) {
15969
16187
  const profileDir = resolveHermesProfileDir(profileName);
15970
- const dbPath = path17.join(profileDir, "state.db");
16188
+ const dbPath = path18.join(profileDir, "state.db");
15971
16189
  const sessions = await listProfileSessions(dbPath).catch((error) => {
15972
16190
  result.errors.push({
15973
16191
  profile: profileName,
@@ -16083,7 +16301,7 @@ async function syncHermesCronSessionIntoConversations(paths, logger, input) {
16083
16301
  const knownHermesSessions = await readKnownHermesSessions(store);
16084
16302
  const profileName = input.profileName.trim() || DEFAULT_PROFILE_NAME;
16085
16303
  const profileDir = resolveHermesProfileDir(profileName);
16086
- const dbPath = path17.join(profileDir, "state.db");
16304
+ const dbPath = path18.join(profileDir, "state.db");
16087
16305
  const sessions = await listProfileSessionsByIdPrefix(
16088
16306
  dbPath,
16089
16307
  `cron_${jobId}_`
@@ -17394,8 +17612,8 @@ async function readJsonlMessages(profileName, sessionId) {
17394
17612
  return [];
17395
17613
  }
17396
17614
  const profileDir = resolveHermesProfileDir(profileName);
17397
- const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path17.join(profileDir, "sessions"));
17398
- 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`);
17399
17617
  const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
17400
17618
  if (isNodeError11(error, "ENOENT")) {
17401
17619
  return "";
@@ -17765,7 +17983,7 @@ function isNodeError11(error, code) {
17765
17983
 
17766
17984
  // src/conversations/delivery-context.ts
17767
17985
  import { copyFile, mkdir as mkdir11, stat as stat11 } from "fs/promises";
17768
- import path18 from "path";
17986
+ import path19 from "path";
17769
17987
  var ACTIVE_CONTEXTS = /* @__PURE__ */ new Map();
17770
17988
  var CONTEXT_TTL_MS = 2 * 60 * 60 * 1e3;
17771
17989
  var MAX_DELIVERY_TOOL_FILES = 50;
@@ -17837,10 +18055,10 @@ function findDeliveryContext(input) {
17837
18055
  );
17838
18056
  }
17839
18057
  async function copyFilesIntoDeliveryContext(paths, context, files) {
17840
- const stagingDir = path18.resolve(context.stagingDir);
17841
- const conversationsRoot = path18.resolve(paths.conversationsDir);
17842
- const relative = path18.relative(conversationsRoot, stagingDir);
17843
- 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)) {
17844
18062
  throw new LinkHttpError(
17845
18063
  400,
17846
18064
  "delivery_staging_invalid",
@@ -17851,13 +18069,13 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
17851
18069
  const result = { staged: [], skipped: [] };
17852
18070
  const usedNames = /* @__PURE__ */ new Set();
17853
18071
  for (const [index, file] of files.slice(0, MAX_DELIVERY_TOOL_FILES).entries()) {
17854
- const sourcePath = path18.resolve(file.path);
18072
+ const sourcePath = path19.resolve(file.path);
17855
18073
  let baseName = sanitizeFilename(
17856
- file.caption || path18.basename(sourcePath),
18074
+ file.caption || path19.basename(sourcePath),
17857
18075
  `attachment-${index + 1}`
17858
18076
  );
17859
- const sourceExtension = path18.extname(sourcePath);
17860
- if (!path18.extname(baseName) && sourceExtension) {
18077
+ const sourceExtension = path19.extname(sourcePath);
18078
+ if (!path19.extname(baseName) && sourceExtension) {
17861
18079
  baseName = `${baseName}${sourceExtension}`;
17862
18080
  }
17863
18081
  const filename = uniqueStagingFilename(
@@ -17884,7 +18102,7 @@ async function copyFilesIntoDeliveryContext(paths, context, files) {
17884
18102
  });
17885
18103
  continue;
17886
18104
  }
17887
- const targetPath = path18.join(stagingDir, filename);
18105
+ const targetPath = path19.join(stagingDir, filename);
17888
18106
  await copyFile(sourcePath, targetPath);
17889
18107
  result.staged.push({
17890
18108
  source_path: sourcePath,
@@ -17927,7 +18145,7 @@ function normalizeDeliveryFileInputs(value) {
17927
18145
  });
17928
18146
  }
17929
18147
  function uniqueStagingFilename(filename, usedNames) {
17930
- const extension = path18.extname(filename);
18148
+ const extension = path19.extname(filename);
17931
18149
  const stem = filename.slice(0, filename.length - extension.length);
17932
18150
  let candidate = filename;
17933
18151
  let suffix = 2;
@@ -18424,10 +18642,10 @@ function parseHermesApiCapabilities(payload) {
18424
18642
  sessionKeyHeader: readString12(features, "session_key_header")
18425
18643
  };
18426
18644
  }
18427
- async function callHermesApi(path34, init, options) {
18645
+ async function callHermesApi(path35, init, options) {
18428
18646
  const method = init.method ?? "GET";
18429
18647
  const startedAt = Date.now();
18430
- void options.logger?.debug("hermes_api_request_started", { method, path: path34 });
18648
+ void options.logger?.debug("hermes_api_request_started", { method, path: path35 });
18431
18649
  const availability = await ensureHermesApiServerAvailable({
18432
18650
  fetchImpl: options.fetchImpl,
18433
18651
  logger: options.logger,
@@ -18436,7 +18654,7 @@ async function callHermesApi(path34, init, options) {
18436
18654
  });
18437
18655
  let config = availability.configResult.apiServer;
18438
18656
  const fetcher = options.fetchImpl ?? fetch;
18439
- const request = () => fetchHermesApi(fetcher, config, path34, init, options);
18657
+ const request = () => fetchHermesApi(fetcher, config, path35, init, options);
18440
18658
  let response;
18441
18659
  try {
18442
18660
  response = await request();
@@ -18444,7 +18662,7 @@ async function callHermesApi(path34, init, options) {
18444
18662
  logHermesApiError(
18445
18663
  options.logger,
18446
18664
  method,
18447
- path34,
18665
+ path35,
18448
18666
  options.profileName,
18449
18667
  startedAt,
18450
18668
  error
@@ -18455,7 +18673,7 @@ async function callHermesApi(path34, init, options) {
18455
18673
  logHermesApiResponse(
18456
18674
  options.logger,
18457
18675
  method,
18458
- path34,
18676
+ path35,
18459
18677
  options.profileName,
18460
18678
  startedAt,
18461
18679
  response
@@ -18464,7 +18682,7 @@ async function callHermesApi(path34, init, options) {
18464
18682
  }
18465
18683
  void options.logger?.warn("hermes_api_request_retrying_after_401", {
18466
18684
  method,
18467
- path: path34,
18685
+ path: path35,
18468
18686
  profile: options.profileName ?? "default",
18469
18687
  port: config.port ?? null,
18470
18688
  duration_ms: Date.now() - startedAt
@@ -18483,7 +18701,7 @@ async function callHermesApi(path34, init, options) {
18483
18701
  logHermesApiError(
18484
18702
  options.logger,
18485
18703
  method,
18486
- path34,
18704
+ path35,
18487
18705
  options.profileName,
18488
18706
  startedAt,
18489
18707
  error
@@ -18493,7 +18711,7 @@ async function callHermesApi(path34, init, options) {
18493
18711
  logHermesApiResponse(
18494
18712
  options.logger,
18495
18713
  method,
18496
- path34,
18714
+ path35,
18497
18715
  options.profileName,
18498
18716
  startedAt,
18499
18717
  response
@@ -18503,7 +18721,7 @@ async function callHermesApi(path34, init, options) {
18503
18721
  }
18504
18722
  void options.logger?.warn("hermes_api_request_repairing_after_401", {
18505
18723
  method,
18506
- path: path34,
18724
+ path: path35,
18507
18725
  profile: options.profileName ?? "default",
18508
18726
  port: config.port ?? null,
18509
18727
  duration_ms: Date.now() - startedAt
@@ -18524,7 +18742,7 @@ async function callHermesApi(path34, init, options) {
18524
18742
  logHermesApiError(
18525
18743
  options.logger,
18526
18744
  method,
18527
- path34,
18745
+ path35,
18528
18746
  options.profileName,
18529
18747
  startedAt,
18530
18748
  error
@@ -18534,21 +18752,21 @@ async function callHermesApi(path34, init, options) {
18534
18752
  logHermesApiResponse(
18535
18753
  options.logger,
18536
18754
  method,
18537
- path34,
18755
+ path35,
18538
18756
  options.profileName,
18539
18757
  startedAt,
18540
18758
  response
18541
18759
  );
18542
18760
  return response;
18543
18761
  }
18544
- async function fetchHermesApi(fetcher, config, path34, init, options) {
18762
+ async function fetchHermesApi(fetcher, config, path35, init, options) {
18545
18763
  const headers = new Headers(init.headers);
18546
18764
  headers.set("accept", headers.get("accept") ?? "application/json");
18547
18765
  if (config.key) {
18548
18766
  headers.set("x-api-key", config.key);
18549
18767
  headers.set("authorization", `Bearer ${config.key}`);
18550
18768
  }
18551
- return await fetcher(`http://127.0.0.1:${config.port}${path34}`, {
18769
+ return await fetcher(`http://127.0.0.1:${config.port}${path35}`, {
18552
18770
  ...init,
18553
18771
  headers
18554
18772
  }).catch((error) => {
@@ -18557,10 +18775,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
18557
18775
  }
18558
18776
  void options.logger?.warn("hermes_api_server_connect_failed", {
18559
18777
  method: String(init.method ?? "GET").toUpperCase(),
18560
- path: path34,
18778
+ path: path35,
18561
18779
  profile: options.profileName ?? "default",
18562
18780
  port: config.port ?? null,
18563
- url: `http://127.0.0.1:${config.port}${path34}`,
18781
+ url: `http://127.0.0.1:${config.port}${path35}`,
18564
18782
  error: error instanceof Error ? error.message : String(error)
18565
18783
  });
18566
18784
  throw new LinkHttpError(
@@ -18570,10 +18788,10 @@ async function fetchHermesApi(fetcher, config, path34, init, options) {
18570
18788
  );
18571
18789
  });
18572
18790
  }
18573
- function logHermesApiResponse(logger, method, path34, profileName, startedAt, response) {
18791
+ function logHermesApiResponse(logger, method, path35, profileName, startedAt, response) {
18574
18792
  const fields = {
18575
18793
  method,
18576
- path: path34,
18794
+ path: path35,
18577
18795
  profile: profileName ?? "default",
18578
18796
  status: response.status,
18579
18797
  duration_ms: Date.now() - startedAt
@@ -18594,10 +18812,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
18594
18812
  ...upstreamError ? { upstream_error: upstreamError } : {}
18595
18813
  });
18596
18814
  }
18597
- function logHermesApiError(logger, method, path34, profileName, startedAt, error) {
18815
+ function logHermesApiError(logger, method, path35, profileName, startedAt, error) {
18598
18816
  void logger?.warn("hermes_api_request_failed", {
18599
18817
  method,
18600
- path: path34,
18818
+ path: path35,
18601
18819
  profile: profileName ?? "default",
18602
18820
  duration_ms: Date.now() - startedAt,
18603
18821
  ...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
@@ -18665,7 +18883,7 @@ function readBoolean2(payload, key) {
18665
18883
 
18666
18884
  // src/conversations/history-builder.ts
18667
18885
  import { readFile as readFile11, stat as stat12 } from "fs/promises";
18668
- import path19 from "path";
18886
+ import path20 from "path";
18669
18887
  var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
18670
18888
  var HERMES_HISTORY_COLUMNS = [
18671
18889
  "role",
@@ -18737,13 +18955,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
18737
18955
  }
18738
18956
  const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
18739
18957
  const profileDir = resolveHermesProfileDir(normalizedProfileName);
18740
- const dbPath = path19.join(profileDir, "state.db");
18958
+ const dbPath = path20.join(profileDir, "state.db");
18741
18959
  const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
18742
18960
  sessionsDir: value.sessionsDir,
18743
18961
  configured: value.configured,
18744
18962
  configError: false
18745
18963
  })).catch(() => ({
18746
- sessionsDir: path19.join(profileDir, "sessions"),
18964
+ sessionsDir: path20.join(profileDir, "sessions"),
18747
18965
  configured: false,
18748
18966
  configError: true
18749
18967
  }));
@@ -18919,8 +19137,8 @@ async function readFirstExistingFile(paths) {
18919
19137
  }
18920
19138
  function candidateTranscriptPaths(sessionsDir, sessionId, extension) {
18921
19139
  return [
18922
- path19.join(sessionsDir, `session_${sessionId}.${extension}`),
18923
- path19.join(sessionsDir, `${sessionId}.${extension}`)
19140
+ path20.join(sessionsDir, `session_${sessionId}.${extension}`),
19141
+ path20.join(sessionsDir, `${sessionId}.${extension}`)
18924
19142
  ];
18925
19143
  }
18926
19144
  function readHistoryRows(dbPath, sessionId) {
@@ -19139,8 +19357,8 @@ function normalizeProfileForCompare(value) {
19139
19357
  // src/hermes/stt.ts
19140
19358
  import { execFile as execFile3 } from "child_process";
19141
19359
  import { access as access2, readFile as readFile12, stat as stat13 } from "fs/promises";
19142
- import os4 from "os";
19143
- import path20 from "path";
19360
+ import os5 from "os";
19361
+ import path21 from "path";
19144
19362
  import { promisify as promisify3 } from "util";
19145
19363
  var execFileAsync3 = promisify3(execFile3);
19146
19364
  var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
@@ -19235,7 +19453,7 @@ async function buildHermesSttEnv(profileName, hermesSourceRoot) {
19235
19453
  };
19236
19454
  const sourceRoot = hermesSourceRoot ?? await findDevHermesAgentSource();
19237
19455
  if (sourceRoot) {
19238
- env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path20.delimiter);
19456
+ env.PYTHONPATH = [sourceRoot, env.PYTHONPATH].filter(Boolean).join(path21.delimiter);
19239
19457
  }
19240
19458
  return env;
19241
19459
  }
@@ -19272,7 +19490,7 @@ async function resolveHermesPythonRuntime() {
19272
19490
  sourceRoot: await findExplicitHermesSourceRoot() ?? await findHermesSourceRoot(explicit)
19273
19491
  };
19274
19492
  }
19275
- const hermesBin = await resolveExecutablePath2(resolveHermesBin());
19493
+ const hermesBin = await resolveExecutablePath(resolveHermesBin());
19276
19494
  let shebangRuntime = null;
19277
19495
  if (hermesBin) {
19278
19496
  const launcherTarget = await readHermesLauncherTarget(hermesBin);
@@ -19329,23 +19547,23 @@ async function resolveHermesPythonRuntime() {
19329
19547
  sourceRoot: await findExplicitHermesSourceRoot() ?? await findDevHermesAgentSource()
19330
19548
  };
19331
19549
  }
19332
- async function resolveExecutablePath2(command) {
19333
- if (path20.isAbsolute(command)) {
19334
- return await isExecutableFile(command) ? command : null;
19550
+ async function resolveExecutablePath(command) {
19551
+ if (path21.isAbsolute(command)) {
19552
+ return await isExecutableFile2(command) ? command : null;
19335
19553
  }
19336
19554
  const pathEnv = process.env.PATH ?? "";
19337
19555
  const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
19338
- for (const dir of pathEnv.split(path20.delimiter)) {
19556
+ for (const dir of pathEnv.split(path21.delimiter)) {
19339
19557
  for (const extension of extensions) {
19340
- const candidate = path20.join(dir, `${command}${extension}`);
19341
- if (await isExecutableFile(candidate)) {
19558
+ const candidate = path21.join(dir, `${command}${extension}`);
19559
+ if (await isExecutableFile2(candidate)) {
19342
19560
  return candidate;
19343
19561
  }
19344
19562
  }
19345
19563
  }
19346
19564
  return null;
19347
19565
  }
19348
- async function isExecutableFile(filePath) {
19566
+ async function isExecutableFile2(filePath) {
19349
19567
  try {
19350
19568
  const info = await stat13(filePath);
19351
19569
  if (!info.isFile()) {
@@ -19359,14 +19577,14 @@ async function isExecutableFile(filePath) {
19359
19577
  }
19360
19578
  async function findHermesVenvPython(sourceRoot) {
19361
19579
  const candidates = process.platform === "win32" ? [
19362
- path20.join(sourceRoot, "venv", "Scripts", "python.exe"),
19363
- path20.join(sourceRoot, ".venv", "Scripts", "python.exe")
19580
+ path21.join(sourceRoot, "venv", "Scripts", "python.exe"),
19581
+ path21.join(sourceRoot, ".venv", "Scripts", "python.exe")
19364
19582
  ] : [
19365
- path20.join(sourceRoot, "venv", "bin", "python"),
19366
- path20.join(sourceRoot, ".venv", "bin", "python")
19583
+ path21.join(sourceRoot, "venv", "bin", "python"),
19584
+ path21.join(sourceRoot, ".venv", "bin", "python")
19367
19585
  ];
19368
19586
  for (const candidate of candidates) {
19369
- if (await isExecutableFile(candidate)) {
19587
+ if (await isExecutableFile2(candidate)) {
19370
19588
  return candidate;
19371
19589
  }
19372
19590
  }
@@ -19392,8 +19610,8 @@ function shebangToPythonCommand(shebang) {
19392
19610
  }
19393
19611
  async function findDevHermesAgentSource() {
19394
19612
  const candidates = [
19395
- path20.resolve(process.cwd(), "reference/hermes-agent"),
19396
- path20.resolve(process.cwd(), "../../reference/hermes-agent")
19613
+ path21.resolve(process.cwd(), "reference/hermes-agent"),
19614
+ path21.resolve(process.cwd(), "../../reference/hermes-agent")
19397
19615
  ];
19398
19616
  for (const candidate of candidates) {
19399
19617
  if (await isHermesAgentSourceRoot(candidate)) {
@@ -19409,10 +19627,10 @@ async function readHermesLauncherTarget(filePath) {
19409
19627
  line
19410
19628
  );
19411
19629
  const rawTarget = quoted?.groups?.target ?? /^\s*exec\s+(?<target>\S+)\s+(?:"\$@"|'\$@')/.exec(line)?.groups?.target;
19412
- if (!rawTarget || !path20.isAbsolute(rawTarget)) {
19630
+ if (!rawTarget || !path21.isAbsolute(rawTarget)) {
19413
19631
  continue;
19414
19632
  }
19415
- if (await isExecutableFile(rawTarget)) {
19633
+ if (await isExecutableFile2(rawTarget)) {
19416
19634
  return rawTarget;
19417
19635
  }
19418
19636
  }
@@ -19439,12 +19657,12 @@ async function resolveHermesEntrypointRuntime(entrypointPath) {
19439
19657
  return null;
19440
19658
  }
19441
19659
  async function findHermesSourceRoot(executablePath) {
19442
- let cursor = path20.dirname(path20.resolve(executablePath));
19660
+ let cursor = path21.dirname(path21.resolve(executablePath));
19443
19661
  for (let index = 0; index < 6; index += 1) {
19444
19662
  if (await isHermesAgentSourceRoot(cursor)) {
19445
19663
  return cursor;
19446
19664
  }
19447
- const parent = path20.dirname(cursor);
19665
+ const parent = path21.dirname(cursor);
19448
19666
  if (parent === cursor) {
19449
19667
  break;
19450
19668
  }
@@ -19455,14 +19673,14 @@ async function findHermesSourceRoot(executablePath) {
19455
19673
  function hermesSourceRootCandidates() {
19456
19674
  const candidates = [
19457
19675
  process.env.HERMES_PYTHON_SRC_ROOT?.trim(),
19458
- path20.join(os4.homedir(), ".hermes", "hermes-agent"),
19676
+ path21.join(os5.homedir(), ".hermes", "hermes-agent"),
19459
19677
  "/usr/local/lib/hermes-agent",
19460
19678
  "/opt/hermes"
19461
19679
  ].filter((candidate) => Boolean(candidate));
19462
19680
  if (process.platform === "win32") {
19463
19681
  const localAppData = process.env.LOCALAPPDATA?.trim();
19464
19682
  if (localAppData) {
19465
- candidates.unshift(path20.join(localAppData, "hermes", "hermes-agent"));
19683
+ candidates.unshift(path21.join(localAppData, "hermes", "hermes-agent"));
19466
19684
  }
19467
19685
  }
19468
19686
  return candidates;
@@ -19472,7 +19690,7 @@ async function findExplicitHermesSourceRoot() {
19472
19690
  return explicit && await isHermesAgentSourceRoot(explicit) ? explicit : null;
19473
19691
  }
19474
19692
  async function isHermesAgentSourceRoot(candidate) {
19475
- 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"));
19476
19694
  }
19477
19695
  async function isDirectory(candidate) {
19478
19696
  return stat13(candidate).then((info) => info.isDirectory()).catch(() => false);
@@ -19484,13 +19702,13 @@ function compactProcessOutput(value) {
19484
19702
 
19485
19703
  // src/hermes/usage-probe.ts
19486
19704
  import { open as open3, readFile as readFile14, rm as rm6, stat as stat15 } from "fs/promises";
19487
- import path22 from "path";
19705
+ import path23 from "path";
19488
19706
  import YAML3 from "yaml";
19489
19707
 
19490
19708
  // src/hermes/profiles.ts
19491
19709
  import { execFile as execFile4 } from "child_process";
19492
19710
  import { readdir as readdir8, readFile as readFile13, rename as rename3, stat as stat14 } from "fs/promises";
19493
- import path21 from "path";
19711
+ import path22 from "path";
19494
19712
  import { setTimeout as delay4 } from "timers/promises";
19495
19713
  import { promisify as promisify4 } from "util";
19496
19714
  import YAML2 from "yaml";
@@ -19633,7 +19851,7 @@ async function readHermesProfileCapabilities(name) {
19633
19851
  return {
19634
19852
  defaultModel: listedModels?.defaultModel ?? null,
19635
19853
  modelCount: listedModels?.models.length ?? 0,
19636
- skillCount: await countSkills(path21.join(profileDir, "skills")).catch(
19854
+ skillCount: await countSkills(path22.join(profileDir, "skills")).catch(
19637
19855
  () => 0
19638
19856
  ),
19639
19857
  toolCount: await countConfiguredTools(name).catch(() => 0)
@@ -19694,7 +19912,7 @@ async function hasDefaultProfileConfigSource() {
19694
19912
  if (!profileStat?.isDirectory()) {
19695
19913
  return false;
19696
19914
  }
19697
- 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"));
19698
19916
  }
19699
19917
  async function deleteHermesProfileWithCli(name) {
19700
19918
  try {
@@ -19864,7 +20082,7 @@ async function countSkills(root) {
19864
20082
  );
19865
20083
  let count = 0;
19866
20084
  for (const entry of entries) {
19867
- const entryPath = path21.join(root, entry.name);
20085
+ const entryPath = path22.join(root, entry.name);
19868
20086
  if (entry.name === ".git" || entry.name === ".hub") {
19869
20087
  continue;
19870
20088
  }
@@ -19957,7 +20175,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
19957
20175
  const normalizedProfile = normalizeProfileName4(profileName);
19958
20176
  const profilePath = resolveHermesProfileDir(normalizedProfile);
19959
20177
  const configPath = resolveHermesConfigPath(normalizedProfile);
19960
- const pluginPath = path22.join(
20178
+ const pluginPath = path23.join(
19961
20179
  profilePath,
19962
20180
  "plugins",
19963
20181
  HERMES_USAGE_PROBE_PLUGIN_KEY
@@ -19983,7 +20201,7 @@ async function ensureHermesUsageProbeForProfile(profileName, options = {}) {
19983
20201
  return { ...base(), skipped: true };
19984
20202
  }
19985
20203
  try {
19986
- await ensureDirectoryWithInheritedMetadata(path22.dirname(eventsPath), 448);
20204
+ await ensureDirectoryWithInheritedMetadata(path23.dirname(eventsPath), 448);
19987
20205
  const state = await readUsageProbeState(paths);
19988
20206
  const pluginState = state.profiles[normalizedProfile];
19989
20207
  const currentConfig = await readUsageProbeConfigStatus({
@@ -20116,7 +20334,7 @@ async function findHermesUsageProbeEventForRun(input) {
20116
20334
  return aggregateUsageProbeEvents(events.sort(compareUsageProbeEventsByTime));
20117
20335
  }
20118
20336
  function resolveUsageProbeEventsPath(paths = resolveRuntimePaths(), profileName = "default") {
20119
- return path22.join(
20337
+ return path23.join(
20120
20338
  paths.homeDir,
20121
20339
  USAGE_PROBE_DIR,
20122
20340
  safeProfileSegment(profileName),
@@ -20140,8 +20358,8 @@ function summarizeUsageProbeEnsure(result) {
20140
20358
  };
20141
20359
  }
20142
20360
  async function writeUsageProbePlugin(input) {
20143
- const manifestPath = path22.join(input.pluginPath, "plugin.yaml");
20144
- 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");
20145
20363
  const manifest = usageProbeManifest();
20146
20364
  const source = usageProbePythonSource(
20147
20365
  input.profileName,
@@ -20488,9 +20706,9 @@ async function disableUsageProbeAfterActivationFailure(input) {
20488
20706
  });
20489
20707
  }
20490
20708
  async function cleanupLegacyUsageProbePlugin(profilePath) {
20491
- const legacyPath = path22.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
20709
+ const legacyPath = path23.join(profilePath, "plugins", LEGACY_PLUGIN_KEY);
20492
20710
  const [manifest, init] = await Promise.all([
20493
- readFile14(path22.join(legacyPath, "plugin.yaml"), "utf8").catch(
20711
+ readFile14(path23.join(legacyPath, "plugin.yaml"), "utf8").catch(
20494
20712
  (error) => {
20495
20713
  if (isNodeError15(error, "ENOENT")) {
20496
20714
  return null;
@@ -20498,7 +20716,7 @@ async function cleanupLegacyUsageProbePlugin(profilePath) {
20498
20716
  throw error;
20499
20717
  }
20500
20718
  ),
20501
- readFile14(path22.join(legacyPath, "__init__.py"), "utf8").catch(
20719
+ readFile14(path23.join(legacyPath, "__init__.py"), "utf8").catch(
20502
20720
  (error) => {
20503
20721
  if (isNodeError15(error, "ENOENT")) {
20504
20722
  return null;
@@ -20547,7 +20765,7 @@ async function rememberUsageProbeState(paths, profileName, patch) {
20547
20765
  }));
20548
20766
  }
20549
20767
  function usageProbeStatePath(paths) {
20550
- 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);
20551
20769
  }
20552
20770
  async function readHermesConfigDocument2(configPath, language) {
20553
20771
  const existingRaw = await readFile14(configPath, "utf8").catch(
@@ -21150,7 +21368,7 @@ function isRunToolResultCompensationEnabled(env = process.env) {
21150
21368
 
21151
21369
  // src/conversations/run-transcript-enrichment.ts
21152
21370
  import { readFile as readFile15, stat as stat16 } from "fs/promises";
21153
- import path23 from "path";
21371
+ import path24 from "path";
21154
21372
  var MESSAGE_COLUMNS2 = [
21155
21373
  "id",
21156
21374
  "session_id",
@@ -21226,8 +21444,8 @@ async function readRunFinalAssistantText(input) {
21226
21444
  }
21227
21445
  async function readHermesTranscriptRows(profileName, sessionId) {
21228
21446
  const profileDir = resolveHermesProfileDir(profileName);
21229
- const dbPath = path23.join(profileDir, "state.db");
21230
- 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"));
21231
21449
  const [dbRows, jsonlRows] = await Promise.all([
21232
21450
  readStateDbMessages2(dbPath, sessionId),
21233
21451
  readJsonlMessages2(sessionsDir, sessionId)
@@ -21290,7 +21508,7 @@ async function readJsonlMessages2(sessionsDir, sessionId) {
21290
21508
  if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
21291
21509
  return [];
21292
21510
  }
21293
- const transcriptPath = path23.join(sessionsDir, `${sessionId}.jsonl`);
21511
+ const transcriptPath = path24.join(sessionsDir, `${sessionId}.jsonl`);
21294
21512
  const raw = await readFile15(transcriptPath, "utf8").catch((error) => {
21295
21513
  if (isNodeError16(error, "ENOENT")) {
21296
21514
  return "";
@@ -22283,6 +22501,7 @@ var ConversationRunLifecycle = class {
22283
22501
  profileName: run.profile,
22284
22502
  signal: controller.signal,
22285
22503
  logger: this.deps.logger,
22504
+ language: run.language === "en" ? "en" : "zh-CN",
22286
22505
  paths: this.deps.paths
22287
22506
  });
22288
22507
  await this.updateRun(conversationId, runId, {
@@ -25754,7 +25973,7 @@ var ConversationService = class {
25754
25973
  }
25755
25974
  }
25756
25975
  hermesArchiveStateSyncMarkerPath() {
25757
- return path24.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
25976
+ return path25.join(this.paths.indexesDir, "hermes-archive-state-sync.json");
25758
25977
  }
25759
25978
  prepareClearAllConversationPlan(targetStatus) {
25760
25979
  return this.maintenance.prepareClearAllConversationPlan(targetStatus);
@@ -27640,11 +27859,11 @@ function isSseRequestContext(ctx) {
27640
27859
  }
27641
27860
  return isSseRequestPath(ctx.path) || isActiveSseSocket(ctx.req.socket);
27642
27861
  }
27643
- function isSseRequestPath(path34) {
27644
- if (!path34) {
27862
+ function isSseRequestPath(path35) {
27863
+ if (!path35) {
27645
27864
  return false;
27646
27865
  }
27647
- 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);
27648
27867
  }
27649
27868
  function isExpectedClientDisconnectError2(error, options = {}) {
27650
27869
  if (!(error instanceof Error)) {
@@ -28476,7 +28695,7 @@ function errorMessage3(error) {
28476
28695
 
28477
28696
  // src/hermes/profile-identity.ts
28478
28697
  import { readFile as readFile16, stat as stat17 } from "fs/promises";
28479
- import path25 from "path";
28698
+ import path26 from "path";
28480
28699
  var MAX_SOUL_MD_LENGTH = 2e4;
28481
28700
  async function readHermesProfileIdentity(profileName, paths) {
28482
28701
  await assertProfileExists3(profileName, paths);
@@ -28533,7 +28752,7 @@ async function assertProfileExists3(profileName, paths) {
28533
28752
  }
28534
28753
  }
28535
28754
  function resolveSoulPath(profileName) {
28536
- return path25.join(resolveHermesProfileDir(profileName), "SOUL.md");
28755
+ return path26.join(resolveHermesProfileDir(profileName), "SOUL.md");
28537
28756
  }
28538
28757
  function isNodeError18(error, code) {
28539
28758
  return error instanceof Error && "code" in error && error.code === code;
@@ -28549,13 +28768,13 @@ import {
28549
28768
  rm as rm7,
28550
28769
  stat as stat19
28551
28770
  } from "fs/promises";
28552
- import path27 from "path";
28771
+ import path28 from "path";
28553
28772
  import YAML5 from "yaml";
28554
28773
 
28555
28774
  // src/hermes/link-skill.ts
28556
28775
  import { readFile as readFile17, stat as stat18 } from "fs/promises";
28557
- import os5 from "os";
28558
- import path26 from "path";
28776
+ import os6 from "os";
28777
+ import path27 from "path";
28559
28778
  import YAML4 from "yaml";
28560
28779
  var HERMES_LINK_SKILL_ROOT_DIR = "hermes-skills";
28561
28780
  var HERMES_LINK_SKILL_DIR = "hermes-link";
@@ -28640,7 +28859,7 @@ Do not modify Hermes profiles, delete user data, edit config files, or kill proc
28640
28859
  async function ensureHermesLinkSkillInstalledForProfiles(options = {}) {
28641
28860
  const paths = options.paths ?? resolveRuntimePaths();
28642
28861
  const externalDir = resolveHermesLinkSkillExternalDir(paths);
28643
- const skillPath = path26.join(
28862
+ const skillPath = path27.join(
28644
28863
  externalDir,
28645
28864
  HERMES_LINK_SKILL_DIR,
28646
28865
  HERMES_LINK_SKILL_FILE
@@ -28722,7 +28941,7 @@ function withDefaultProfilePlaceholder2(profiles) {
28722
28941
  ];
28723
28942
  }
28724
28943
  function resolveHermesLinkSkillExternalDir(paths = resolveRuntimePaths()) {
28725
- return path26.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
28944
+ return path27.join(paths.homeDir, HERMES_LINK_SKILL_ROOT_DIR);
28726
28945
  }
28727
28946
  async function writeHermesLinkSkill(skillPath) {
28728
28947
  const existing = await readFile17(skillPath, "utf8").catch((error) => {
@@ -28817,11 +29036,11 @@ function appendExternalDir(current, externalDir, hermesHome) {
28817
29036
  const seen = new Set(
28818
29037
  entries.map((entry) => resolveExternalDirEntry(entry, hermesHome))
28819
29038
  );
28820
- const normalizedExternalDir = path26.resolve(externalDir);
29039
+ const normalizedExternalDir = path27.resolve(externalDir);
28821
29040
  return seen.has(normalizedExternalDir) ? entries : [...entries, normalizedExternalDir];
28822
29041
  }
28823
29042
  function externalDirsInclude(current, externalDir, hermesHome) {
28824
- const normalizedExternalDir = path26.resolve(externalDir);
29043
+ const normalizedExternalDir = path27.resolve(externalDir);
28825
29044
  return readExternalDirEntries(current).some(
28826
29045
  (entry) => resolveExternalDirEntry(entry, hermesHome) === normalizedExternalDir
28827
29046
  );
@@ -28832,14 +29051,14 @@ function readExternalDirEntries(value) {
28832
29051
  }
28833
29052
  function resolveExternalDirEntry(entry, hermesHome) {
28834
29053
  const expanded = expandHome(expandEnvVars(entry));
28835
- return path26.resolve(path26.isAbsolute(expanded) ? expanded : path26.join(hermesHome, expanded));
29054
+ return path27.resolve(path27.isAbsolute(expanded) ? expanded : path27.join(hermesHome, expanded));
28836
29055
  }
28837
29056
  function expandHome(value) {
28838
29057
  if (value === "~") {
28839
- return os5.homedir();
29058
+ return os6.homedir();
28840
29059
  }
28841
- if (value.startsWith(`~${path26.sep}`) || value.startsWith("~/")) {
28842
- 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));
28843
29062
  }
28844
29063
  return value;
28845
29064
  }
@@ -29391,7 +29610,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
29391
29610
  return keys;
29392
29611
  }
29393
29612
  async function writeEnvValues(profileName, values) {
29394
- const envPath = path27.join(resolveHermesProfileDir(profileName), ".env");
29613
+ const envPath = path28.join(resolveHermesProfileDir(profileName), ".env");
29395
29614
  const existingRaw = await readFile18(envPath, "utf8").catch((error) => {
29396
29615
  if (isNodeError20(error, "ENOENT")) {
29397
29616
  return "";
@@ -29428,8 +29647,8 @@ async function writeEnvValues(profileName, values) {
29428
29647
  await atomicWriteFilePreservingMetadata(envPath, nextRaw);
29429
29648
  }
29430
29649
  async function copySkills(sourceProfile, targetProfile) {
29431
- const sourceSkills = path27.join(resolveHermesProfileDir(sourceProfile), "skills");
29432
- const targetSkills = path27.join(resolveHermesProfileDir(targetProfile), "skills");
29650
+ const sourceSkills = path28.join(resolveHermesProfileDir(sourceProfile), "skills");
29651
+ const targetSkills = path28.join(resolveHermesProfileDir(targetProfile), "skills");
29433
29652
  if (!await pathExists2(sourceSkills)) {
29434
29653
  return;
29435
29654
  }
@@ -29524,10 +29743,10 @@ async function readProfileCreationLogLines(paths) {
29524
29743
  );
29525
29744
  }
29526
29745
  function profileCreationStatePath(paths) {
29527
- return path27.join(paths.runDir, "profile-create-state.json");
29746
+ return path28.join(paths.runDir, "profile-create-state.json");
29528
29747
  }
29529
29748
  function profileCreationLogPath(paths) {
29530
- return path27.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
29749
+ return path28.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
29531
29750
  }
29532
29751
  async function clearProfileCreationLogFiles(paths) {
29533
29752
  const primary = profileCreationLogPath(paths);
@@ -29816,7 +30035,7 @@ import {
29816
30035
  readFile as readFile19,
29817
30036
  stat as stat20
29818
30037
  } from "fs/promises";
29819
- import path28 from "path";
30038
+ import path29 from "path";
29820
30039
  import YAML6 from "yaml";
29821
30040
  var ENTRY_DELIMITER = "\n\xA7\n";
29822
30041
  var DEFAULT_MEMORY_LIMIT = 2200;
@@ -30147,7 +30366,7 @@ async function saveProviderSettings(profileName, provider, patch) {
30147
30366
  });
30148
30367
  await patchJsonProviderConfig(
30149
30368
  profileName,
30150
- path28.join("hindsight", "config.json"),
30369
+ path29.join("hindsight", "config.json"),
30151
30370
  {
30152
30371
  mode: patch.mode,
30153
30372
  api_url: patch.apiUrl,
@@ -30350,7 +30569,7 @@ async function patchHermesMemoryLimits(profileName, patch) {
30350
30569
  await atomicWriteFilePreservingMetadata(configPath, document.toString());
30351
30570
  }
30352
30571
  function resolveMemoryDir(profileName) {
30353
- return path28.join(resolveHermesProfileDir(profileName), "memories");
30572
+ return path29.join(resolveHermesProfileDir(profileName), "memories");
30354
30573
  }
30355
30574
  async function readMemoryStore(profileName, target, limits) {
30356
30575
  const filePath = memoryFilePath(profileName, target);
@@ -30411,7 +30630,7 @@ async function writeMemoryEntries(profileName, target, entries) {
30411
30630
  );
30412
30631
  }
30413
30632
  function memoryFilePath(profileName, target) {
30414
- return path28.join(
30633
+ return path29.join(
30415
30634
  resolveMemoryDir(profileName),
30416
30635
  target === "user" ? "USER.md" : "MEMORY.md"
30417
30636
  );
@@ -30471,7 +30690,7 @@ async function readCustomProviderSetupSummary(profileName) {
30471
30690
  configurable: true,
30472
30691
  configured: true,
30473
30692
  configurationIssue: null,
30474
- providerConfigPath: path28.join(
30693
+ providerConfigPath: path29.join(
30475
30694
  resolveHermesProfileDir(profileName),
30476
30695
  "<provider>.json"
30477
30696
  ),
@@ -30865,7 +31084,7 @@ async function readProviderSettings(profileName, provider) {
30865
31084
  stringSetting(
30866
31085
  "dbPath",
30867
31086
  "SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
30868
- config.db_path ?? path28.join(resolveHermesProfileDir(profileName), "memory_store.db")
31087
+ config.db_path ?? path29.join(resolveHermesProfileDir(profileName), "memory_store.db")
30869
31088
  ),
30870
31089
  booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
30871
31090
  numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
@@ -30901,7 +31120,7 @@ async function readProviderSettings(profileName, provider) {
30901
31120
  stringSetting(
30902
31121
  "workingDirectory",
30903
31122
  "\u5DE5\u4F5C\u76EE\u5F55",
30904
- path28.join(resolveHermesProfileDir(profileName), "byterover"),
31123
+ path29.join(resolveHermesProfileDir(profileName), "byterover"),
30905
31124
  false
30906
31125
  )
30907
31126
  ];
@@ -30910,16 +31129,16 @@ async function readProviderSettings(profileName, provider) {
30910
31129
  }
30911
31130
  function memoryProviderConfigPath(profileName, provider) {
30912
31131
  if (provider === "honcho") {
30913
- return path28.join(resolveHermesProfileDir(profileName), "honcho.json");
31132
+ return path29.join(resolveHermesProfileDir(profileName), "honcho.json");
30914
31133
  }
30915
31134
  if (provider === "mem0") {
30916
- return path28.join(resolveHermesProfileDir(profileName), "mem0.json");
31135
+ return path29.join(resolveHermesProfileDir(profileName), "mem0.json");
30917
31136
  }
30918
31137
  if (provider === "supermemory") {
30919
- return path28.join(resolveHermesProfileDir(profileName), "supermemory.json");
31138
+ return path29.join(resolveHermesProfileDir(profileName), "supermemory.json");
30920
31139
  }
30921
31140
  if (provider === "hindsight") {
30922
- return path28.join(
31141
+ return path29.join(
30923
31142
  resolveHermesProfileDir(profileName),
30924
31143
  "hindsight",
30925
31144
  "config.json"
@@ -30928,13 +31147,13 @@ function memoryProviderConfigPath(profileName, provider) {
30928
31147
  return null;
30929
31148
  }
30930
31149
  function customProviderConfigPath(profileName, provider) {
30931
- return path28.join(
31150
+ return path29.join(
30932
31151
  resolveHermesProfileDir(profileName),
30933
31152
  `${normalizeCustomProviderId(provider)}.json`
30934
31153
  );
30935
31154
  }
30936
31155
  function customProviderRegistryPath(profileName) {
30937
- return path28.join(
31156
+ return path29.join(
30938
31157
  resolveHermesProfileDir(profileName),
30939
31158
  CUSTOM_PROVIDER_REGISTRY_FILE
30940
31159
  );
@@ -30986,7 +31205,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
30986
31205
  );
30987
31206
  }
30988
31207
  async function discoverUserMemoryProviderDescriptors(profileName) {
30989
- const pluginsDir = path28.join(resolveHermesProfileDir(profileName), "plugins");
31208
+ const pluginsDir = path29.join(resolveHermesProfileDir(profileName), "plugins");
30990
31209
  const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
30991
31210
  (error) => {
30992
31211
  if (isNodeError21(error, "ENOENT")) {
@@ -31006,7 +31225,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
31006
31225
  } catch {
31007
31226
  continue;
31008
31227
  }
31009
- const providerDir = path28.join(pluginsDir, entry.name);
31228
+ const providerDir = path29.join(pluginsDir, entry.name);
31010
31229
  if (!await isMemoryProviderPluginDir(providerDir)) {
31011
31230
  continue;
31012
31231
  }
@@ -31020,7 +31239,7 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
31020
31239
  return descriptors;
31021
31240
  }
31022
31241
  async function isUserMemoryProviderInstalled(profileName, provider) {
31023
- const providerDir = path28.join(
31242
+ const providerDir = path29.join(
31024
31243
  resolveHermesProfileDir(profileName),
31025
31244
  "plugins",
31026
31245
  normalizeCustomProviderId(provider)
@@ -31028,7 +31247,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
31028
31247
  return isMemoryProviderPluginDir(providerDir);
31029
31248
  }
31030
31249
  async function isMemoryProviderPluginDir(providerDir) {
31031
- const source = await readFile19(path28.join(providerDir, "__init__.py"), "utf8").catch(
31250
+ const source = await readFile19(path29.join(providerDir, "__init__.py"), "utf8").catch(
31032
31251
  (error) => {
31033
31252
  if (isNodeError21(error, "ENOENT")) {
31034
31253
  return "";
@@ -31040,7 +31259,7 @@ async function isMemoryProviderPluginDir(providerDir) {
31040
31259
  return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
31041
31260
  }
31042
31261
  async function readPluginMetadata(providerDir) {
31043
- const raw = await readFile19(path28.join(providerDir, "plugin.yaml"), "utf8").catch(
31262
+ const raw = await readFile19(path29.join(providerDir, "plugin.yaml"), "utf8").catch(
31044
31263
  (error) => {
31045
31264
  if (isNodeError21(error, "ENOENT")) {
31046
31265
  return "";
@@ -31052,10 +31271,10 @@ async function readPluginMetadata(providerDir) {
31052
31271
  }
31053
31272
  async function resolveByteRoverCli() {
31054
31273
  const candidates = [
31055
- ...(process.env.PATH ?? "").split(path28.delimiter).filter(Boolean).map((dir) => path28.join(dir, "brv")),
31056
- 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"),
31057
31276
  "/usr/local/bin/brv",
31058
- path28.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
31277
+ path29.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
31059
31278
  ].filter(Boolean);
31060
31279
  for (const candidate of candidates) {
31061
31280
  const found = await access3(candidate).then(() => true).catch(() => false);
@@ -31116,7 +31335,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
31116
31335
  if (entries.length === 0) {
31117
31336
  return;
31118
31337
  }
31119
- const envPath = path28.join(resolveHermesProfileDir(profileName), ".env");
31338
+ const envPath = path29.join(resolveHermesProfileDir(profileName), ".env");
31120
31339
  const existingRaw = await readFile19(envPath, "utf8").catch((error) => {
31121
31340
  if (isNodeError21(error, "ENOENT")) {
31122
31341
  return "";
@@ -31287,7 +31506,7 @@ async function readActiveMemoryProvider(profileName) {
31287
31506
  return provider;
31288
31507
  }
31289
31508
  async function patchJsonProviderConfig(profileName, relativePath, patch) {
31290
- const configPath = path28.join(
31509
+ const configPath = path29.join(
31291
31510
  resolveHermesProfileDir(profileName),
31292
31511
  relativePath
31293
31512
  );
@@ -31316,7 +31535,7 @@ async function readJsonObject(filePath) {
31316
31535
  } catch {
31317
31536
  throw new HermesMemoryError(
31318
31537
  "memory_provider_config_invalid",
31319
- `${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`
31320
31539
  );
31321
31540
  }
31322
31541
  }
@@ -31940,7 +32159,7 @@ function toMemoryHttpError(error) {
31940
32159
 
31941
32160
  // src/hermes/skills.ts
31942
32161
  import { readFile as readFile20, readdir as readdir11 } from "fs/promises";
31943
- import path29 from "path";
32162
+ import path30 from "path";
31944
32163
  import YAML7 from "yaml";
31945
32164
  var HermesSkillNotFoundError = class extends Error {
31946
32165
  constructor(skillName) {
@@ -31954,7 +32173,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
31954
32173
  async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
31955
32174
  const profile = await readExistingProfile(profileName, paths);
31956
32175
  const profileDir = resolveHermesProfileDir(profile.name);
31957
- const skillsRoot = path29.join(profileDir, "skills");
32176
+ const skillsRoot = path30.join(profileDir, "skills");
31958
32177
  const [skillFiles, disabled, provenance] = await Promise.all([
31959
32178
  findSkillFiles(skillsRoot),
31960
32179
  readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
@@ -32045,7 +32264,7 @@ async function collectSkillFiles(directory, results) {
32045
32264
  if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
32046
32265
  continue;
32047
32266
  }
32048
- const entryPath = path29.join(directory, entry.name);
32267
+ const entryPath = path30.join(directory, entry.name);
32049
32268
  if (entry.isDirectory()) {
32050
32269
  await collectSkillFiles(entryPath, results);
32051
32270
  continue;
@@ -32067,10 +32286,10 @@ async function readSkillMetadata(input) {
32067
32286
  if (raw === null) {
32068
32287
  return null;
32069
32288
  }
32070
- const skillDir = path29.dirname(input.skillFile);
32289
+ const skillDir = path30.dirname(input.skillFile);
32071
32290
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
32072
32291
  const name = normalizeSkillName(
32073
- readString21(frontmatter.name) ?? path29.basename(skillDir)
32292
+ readString21(frontmatter.name) ?? path30.basename(skillDir)
32074
32293
  );
32075
32294
  if (!name) {
32076
32295
  return null;
@@ -32089,7 +32308,7 @@ async function readSkillMetadata(input) {
32089
32308
  enabled: !input.disabled.has(name),
32090
32309
  source: provenance.source,
32091
32310
  trust: provenance.trust,
32092
- relativePath: path29.relative(input.skillsRoot, skillDir)
32311
+ relativePath: path30.relative(input.skillsRoot, skillDir)
32093
32312
  };
32094
32313
  }
32095
32314
  function parseSkillDocument(raw) {
@@ -32110,8 +32329,8 @@ function parseSkillDocument(raw) {
32110
32329
  }
32111
32330
  }
32112
32331
  function categoryFromPath(skillsRoot, skillFile) {
32113
- const relative = path29.relative(skillsRoot, skillFile);
32114
- const parts = relative.split(path29.sep).filter(Boolean);
32332
+ const relative = path30.relative(skillsRoot, skillFile);
32333
+ const parts = relative.split(path30.sep).filter(Boolean);
32115
32334
  return parts.length >= 3 ? parts[0] : null;
32116
32335
  }
32117
32336
  function firstBodyDescription(body) {
@@ -32158,7 +32377,7 @@ async function readSkillProvenance(root) {
32158
32377
  return provenance;
32159
32378
  }
32160
32379
  async function readBundledSkillNames(root) {
32161
- const raw = await readFile20(path29.join(root, ".bundled_manifest"), "utf8").catch(
32380
+ const raw = await readFile20(path30.join(root, ".bundled_manifest"), "utf8").catch(
32162
32381
  (error) => {
32163
32382
  if (isNodeError22(error, "ENOENT")) {
32164
32383
  return "";
@@ -32181,7 +32400,7 @@ async function readBundledSkillNames(root) {
32181
32400
  return names;
32182
32401
  }
32183
32402
  async function readHubInstalledSkills(root) {
32184
- 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(
32185
32404
  (error) => {
32186
32405
  if (isNodeError22(error, "ENOENT")) {
32187
32406
  return "";
@@ -32658,6 +32877,7 @@ function registerRunRoutes(router, options) {
32658
32877
  const { paths, logger, conversations } = options;
32659
32878
  router.post("/api/v1/runs", async (ctx) => {
32660
32879
  await authenticateRequest(ctx, paths);
32880
+ const language = readPreferredLanguage(ctx);
32661
32881
  const body = await readJsonBody(ctx.req);
32662
32882
  const input = readString19(body, "input");
32663
32883
  if (!input) {
@@ -32674,14 +32894,16 @@ function registerRunRoutes(router, options) {
32674
32894
  session_id: readString19(body, "session_id") ?? readString19(body, "sessionId") ?? void 0,
32675
32895
  session_key: readString19(body, "session_key") ?? readString19(body, "sessionKey") ?? void 0
32676
32896
  },
32677
- { logger, profileName: readOptionalProfileName(body) }
32897
+ { logger, profileName: readOptionalProfileName(body), language }
32678
32898
  );
32679
32899
  });
32680
32900
  router.get("/api/v1/runs/:runId/events", async (ctx) => {
32681
32901
  await authenticateRequest(ctx, paths);
32902
+ const language = readPreferredLanguage(ctx);
32682
32903
  const response = await streamHermesRunEvents(ctx.params.runId, {
32683
32904
  logger,
32684
- profileName: readQueryString(ctx.query.profile)
32905
+ profileName: readQueryString(ctx.query.profile),
32906
+ language
32685
32907
  });
32686
32908
  ctx.status = response.status;
32687
32909
  for (const [key, value] of response.headers.entries()) {
@@ -32703,6 +32925,7 @@ function registerRunRoutes(router, options) {
32703
32925
  });
32704
32926
  router.post("/api/v1/runs/:runId/cancel", async (ctx) => {
32705
32927
  await authenticateRequest(ctx, paths);
32928
+ const language = readPreferredLanguage(ctx);
32706
32929
  try {
32707
32930
  ctx.body = {
32708
32931
  ok: true,
@@ -32714,7 +32937,8 @@ function registerRunRoutes(router, options) {
32714
32937
  }
32715
32938
  await cancelHermesRun(ctx.params.runId, {
32716
32939
  logger,
32717
- profileName: readQueryString(ctx.query.profile)
32940
+ profileName: readQueryString(ctx.query.profile),
32941
+ language
32718
32942
  });
32719
32943
  ctx.body = { ok: true };
32720
32944
  }
@@ -32844,7 +33068,7 @@ function readModelList(payload) {
32844
33068
  import { EventEmitter as EventEmitter3 } from "events";
32845
33069
  import { spawn as spawn4 } from "child_process";
32846
33070
  import { mkdir as mkdir13, readFile as readFile21, rm as rm8 } from "fs/promises";
32847
- import path30 from "path";
33071
+ import path31 from "path";
32848
33072
  var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
32849
33073
  var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
32850
33074
  var RELEASE_FETCH_TIMEOUT_MS = 5e3;
@@ -33122,13 +33346,13 @@ async function readUpdateLogLines(paths) {
33122
33346
  );
33123
33347
  }
33124
33348
  function releaseCachePath(paths) {
33125
- return path30.join(paths.indexesDir, "hermes-release-check.json");
33349
+ return path31.join(paths.indexesDir, "hermes-release-check.json");
33126
33350
  }
33127
33351
  function updateStatePath(paths) {
33128
- return path30.join(paths.runDir, "hermes-update-state.json");
33352
+ return path31.join(paths.runDir, "hermes-update-state.json");
33129
33353
  }
33130
33354
  function updateLogPath(paths) {
33131
- return path30.join(paths.logsDir, UPDATE_LOG_FILE);
33355
+ return path31.join(paths.logsDir, UPDATE_LOG_FILE);
33132
33356
  }
33133
33357
  async function clearUpdateLogFiles(paths) {
33134
33358
  const primary = updateLogPath(paths);
@@ -33229,12 +33453,12 @@ function readString22(payload, key) {
33229
33453
  import { spawn as spawn6 } from "child_process";
33230
33454
  import { EventEmitter as EventEmitter4 } from "events";
33231
33455
  import { mkdir as mkdir16, readFile as readFile23, rm as rm11 } from "fs/promises";
33232
- import path32 from "path";
33456
+ import path33 from "path";
33233
33457
 
33234
33458
  // src/daemon/process.ts
33235
33459
  import { spawn as spawn5 } from "child_process";
33236
33460
  import { mkdir as mkdir15, readFile as readFile22, rm as rm10, writeFile as writeFile4 } from "fs/promises";
33237
- import path31 from "path";
33461
+ import path32 from "path";
33238
33462
 
33239
33463
  // src/daemon/service.ts
33240
33464
  import { createServer } from "http";
@@ -33910,8 +34134,8 @@ function normalizeMessage(value) {
33910
34134
 
33911
34135
  // src/runtime/system-info.ts
33912
34136
  import { execFileSync } from "child_process";
33913
- import { readFileSync } from "fs";
33914
- import os6 from "os";
34137
+ import { readFileSync as readFileSync2 } from "fs";
34138
+ import os7 from "os";
33915
34139
  function readLinkSystemInfo() {
33916
34140
  const platform = process.platform;
33917
34141
  const hostname = readHostname(platform);
@@ -33950,7 +34174,7 @@ function readHostname(platform) {
33950
34174
  return computerName;
33951
34175
  }
33952
34176
  }
33953
- return normalizeText(os6.hostname());
34177
+ return normalizeText(os7.hostname());
33954
34178
  }
33955
34179
  function readOsLabel(platform) {
33956
34180
  if (platform === "darwin") {
@@ -33958,17 +34182,17 @@ function readOsLabel(platform) {
33958
34182
  return version ? `macOS ${version}` : "macOS";
33959
34183
  }
33960
34184
  if (platform === "linux") {
33961
- return readLinuxOsRelease() ?? `Linux ${os6.release()}`;
34185
+ return readLinuxOsRelease() ?? `Linux ${os7.release()}`;
33962
34186
  }
33963
34187
  if (platform === "win32") {
33964
- return `Windows ${os6.release()}`;
34188
+ return `Windows ${os7.release()}`;
33965
34189
  }
33966
- return `${os6.type()} ${os6.release()}`.trim();
34190
+ return `${os7.type()} ${os7.release()}`.trim();
33967
34191
  }
33968
34192
  function readLinuxOsRelease() {
33969
34193
  for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
33970
34194
  try {
33971
- return parseLinuxOsRelease(readFileSync(file, "utf8"));
34195
+ return parseLinuxOsRelease(readFileSync2(file, "utf8"));
33972
34196
  } catch {
33973
34197
  }
33974
34198
  }
@@ -34010,11 +34234,11 @@ function truncateText(value, maxLength) {
34010
34234
  }
34011
34235
 
34012
34236
  // src/topology/network.ts
34013
- import os8 from "os";
34237
+ import os9 from "os";
34014
34238
 
34015
34239
  // src/topology/environment.ts
34016
- import { existsSync, readFileSync as readFileSync2 } from "fs";
34017
- import os7 from "os";
34240
+ import { existsSync, readFileSync as readFileSync3 } from "fs";
34241
+ import os8 from "os";
34018
34242
  function detectRuntimeEnvironment(env = process.env) {
34019
34243
  if (isWsl(env)) {
34020
34244
  return {
@@ -34043,7 +34267,7 @@ function isWsl(env) {
34043
34267
  if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
34044
34268
  return true;
34045
34269
  }
34046
- const release = os7.release().toLowerCase();
34270
+ const release = os8.release().toLowerCase();
34047
34271
  return release.includes("microsoft") || release.includes("wsl");
34048
34272
  }
34049
34273
  function isContainer(env) {
@@ -34054,7 +34278,7 @@ function isContainer(env) {
34054
34278
  return true;
34055
34279
  }
34056
34280
  try {
34057
- const cgroup = readFileSync2("/proc/1/cgroup", "utf8").toLowerCase();
34281
+ const cgroup = readFileSync3("/proc/1/cgroup", "utf8").toLowerCase();
34058
34282
  return /docker|containerd|kubepods|libpod|podman/u.test(cgroup);
34059
34283
  } catch {
34060
34284
  return false;
@@ -34088,7 +34312,7 @@ async function discoverRouteCandidates(options) {
34088
34312
  };
34089
34313
  }
34090
34314
  function discoverLanIps() {
34091
- return discoverLanIpsFromInterfaces(os8.networkInterfaces());
34315
+ return discoverLanIpsFromInterfaces(os9.networkInterfaces());
34092
34316
  }
34093
34317
  function discoverLanIpsFromInterfaces(interfaces) {
34094
34318
  const result = /* @__PURE__ */ new Set();
@@ -35181,7 +35405,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
35181
35405
  await mkdir15(paths.logsDir, { recursive: true, mode: 448 });
35182
35406
  const log = createRotatingTextLogWriter({
35183
35407
  paths,
35184
- fileName: path31.basename(daemonLogFile(paths))
35408
+ fileName: path32.basename(daemonLogFile(paths))
35185
35409
  });
35186
35410
  const scriptPath = currentCliScriptPath();
35187
35411
  const write = (chunk) => {
@@ -35479,7 +35703,7 @@ function terminateChild(child, previousForceKillTimer) {
35479
35703
  }
35480
35704
  }
35481
35705
  function supervisorStopIntentPath(paths) {
35482
- return path31.join(paths.runDir, "supervisor-stop-intent.json");
35706
+ return path32.join(paths.runDir, "supervisor-stop-intent.json");
35483
35707
  }
35484
35708
  async function writeSupervisorStopIntent(paths, pid) {
35485
35709
  await mkdir15(paths.runDir, { recursive: true, mode: 448 });
@@ -36042,7 +36266,7 @@ async function buildOfficialInstallCommand(options, targetVersion) {
36042
36266
  };
36043
36267
  }
36044
36268
  function buildUnixInstallCommand(installerUrl) {
36045
- const nodeBinDir = path32.dirname(process.execPath);
36269
+ const nodeBinDir = path33.dirname(process.execPath);
36046
36270
  const fetchScript = [
36047
36271
  quoteShellToken(process.execPath),
36048
36272
  "--input-type=module",
@@ -36312,10 +36536,10 @@ async function readUpdateLogLines2(paths) {
36312
36536
  );
36313
36537
  }
36314
36538
  function updateStatePath2(paths) {
36315
- return path32.join(paths.runDir, "link-update-state.json");
36539
+ return path33.join(paths.runDir, "link-update-state.json");
36316
36540
  }
36317
36541
  function updateLogPath2(paths) {
36318
- return path32.join(paths.logsDir, UPDATE_LOG_FILE2);
36542
+ return path33.join(paths.logsDir, UPDATE_LOG_FILE2);
36319
36543
  }
36320
36544
  async function clearUpdateLogFiles2(paths) {
36321
36545
  const primary = updateLogPath2(paths);
@@ -36399,7 +36623,7 @@ function readString23(payload, key) {
36399
36623
  }
36400
36624
 
36401
36625
  // src/pairing/pairing.ts
36402
- import path33 from "path";
36626
+ import path34 from "path";
36403
36627
  import { rm as rm12 } from "fs/promises";
36404
36628
 
36405
36629
  // src/relay/bootstrap.ts
@@ -36739,10 +36963,10 @@ async function loadRequiredIdentity2(paths) {
36739
36963
  }
36740
36964
  return identity;
36741
36965
  }
36742
- async function postServerJson(serverBaseUrl, path34, body, options) {
36966
+ async function postServerJson(serverBaseUrl, path35, body, options) {
36743
36967
  let response;
36744
36968
  try {
36745
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path34}`, {
36969
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
36746
36970
  method: "POST",
36747
36971
  headers: {
36748
36972
  accept: "application/json",
@@ -36790,10 +37014,10 @@ function pairingErrorSnapshot(stage, error) {
36790
37014
  occurred_at: (/* @__PURE__ */ new Date()).toISOString()
36791
37015
  };
36792
37016
  }
36793
- async function patchServerJson(serverBaseUrl, path34, token, body, options) {
37017
+ async function patchServerJson(serverBaseUrl, path35, token, body, options) {
36794
37018
  let response;
36795
37019
  try {
36796
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path34}`, {
37020
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path35}`, {
36797
37021
  method: "PATCH",
36798
37022
  headers: {
36799
37023
  accept: "application/json",
@@ -36841,10 +37065,10 @@ function createPairingNetworkError(input) {
36841
37065
  );
36842
37066
  }
36843
37067
  function pairingClaimPath(sessionId, paths) {
36844
- 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`);
36845
37069
  }
36846
37070
  function pairingSessionPath(sessionId, paths) {
36847
- return path33.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
37071
+ return path34.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
36848
37072
  }
36849
37073
  function qrPreferredUrls(routes) {
36850
37074
  return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
@@ -38171,6 +38395,7 @@ export {
38171
38395
  getGatewayLogFiles,
38172
38396
  readRecentGatewayLogEntries,
38173
38397
  flushLogFiles,
38398
+ resolveHermesBin,
38174
38399
  HermesApiServerUnavailableError,
38175
38400
  ensureHermesApiServerAvailable,
38176
38401
  readHermesVersion,