@hermespilot/link 0.5.7 → 0.5.9

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.
@@ -4425,7 +4425,7 @@ async function listCronOutputFiles(profileName, jobId) {
4425
4425
  mtimeMs: fileStat.mtimeMs
4426
4426
  });
4427
4427
  }
4428
- return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path: path27, mtime }) => ({ path: path27, mtime }));
4428
+ return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path: path28, mtime }) => ({ path: path28, mtime }));
4429
4429
  }
4430
4430
  async function readCronOutput(outputPath) {
4431
4431
  const content = await readFile3(outputPath, "utf8");
@@ -4502,7 +4502,7 @@ import os2 from "os";
4502
4502
  import path5 from "path";
4503
4503
 
4504
4504
  // src/constants.ts
4505
- var LINK_VERSION = "0.5.7";
4505
+ var LINK_VERSION = "0.5.9";
4506
4506
  var LINK_COMMAND = "hermeslink";
4507
4507
  var LINK_DEFAULT_PORT = 52379;
4508
4508
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -6375,6 +6375,27 @@ function estimateContextUsage(input) {
6375
6375
  context_source: "estimated"
6376
6376
  };
6377
6377
  }
6378
+ function resolveRuntimeContextUsage(input) {
6379
+ const usage = input.usage;
6380
+ const contextWindow = input.contextWindow ?? usage?.context_window;
6381
+ if (!usage) {
6382
+ return { source: "unknown", contextWindow };
6383
+ }
6384
+ const explicitContextTokens = usage.context_tokens !== void 0 && usage.context_source !== "estimated" ? usage.context_tokens : void 0;
6385
+ const estimatedContextTokens = usage.context_tokens !== void 0 && usage.context_source === "estimated" ? usage.context_tokens : void 0;
6386
+ const contextTokens = explicitContextTokens ?? (usage.total_tokens > 0 ? usage.total_tokens : void 0) ?? estimatedContextTokens;
6387
+ if (contextTokens === void 0) {
6388
+ return { source: "unknown", contextWindow };
6389
+ }
6390
+ const source = explicitContextTokens !== void 0 || usage.total_tokens > 0 ? "explicit" : "estimated";
6391
+ const computedPercent = contextWindow && contextWindow > 0 ? Math.min(100, Math.round(contextTokens / contextWindow * 100)) : void 0;
6392
+ return {
6393
+ contextTokens,
6394
+ contextWindow,
6395
+ source,
6396
+ usagePercent: explicitContextTokens !== void 0 || source === "estimated" ? usage.usage_percent ?? computedPercent : computedPercent
6397
+ };
6398
+ }
6378
6399
  function isAgentRun(run) {
6379
6400
  return run.kind !== "command";
6380
6401
  }
@@ -7116,9 +7137,12 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
7116
7137
  output_tokens: 0,
7117
7138
  total_tokens: 0
7118
7139
  };
7119
- const contextWindow = current.contextWindow ?? usage.context_window ?? usageRun?.context_window;
7120
- const contextTokens = usage.context_tokens;
7121
- const contextSource = contextTokens === void 0 ? "unknown" : usage.context_source ?? "explicit";
7140
+ const runtimeContext = resolveRuntimeContextUsage({
7141
+ usage: usageRun?.usage,
7142
+ contextWindow: current.contextWindow ?? usage.context_window ?? usageRun?.context_window
7143
+ });
7144
+ const contextTokens = runtimeContext.contextTokens;
7145
+ const contextWindow = runtimeContext.contextWindow;
7122
7146
  const provider = current.provider ?? usageRun?.provider;
7123
7147
  const reasoningEffort = current.reasoningEffort;
7124
7148
  return {
@@ -7141,13 +7165,8 @@ async function buildConversationRuntimeMetadata(paths, manifest, snapshot) {
7141
7165
  ...contextWindow ? { context_window: contextWindow } : {},
7142
7166
  ...contextTokens !== void 0 ? { used_tokens: contextTokens } : {},
7143
7167
  ...contextWindow ? { window_tokens: contextWindow } : {},
7144
- source: contextSource,
7145
- ...contextWindow && contextTokens !== void 0 ? {
7146
- usage_percent: Math.min(
7147
- 100,
7148
- usage.usage_percent ?? Math.round(contextTokens / contextWindow * 100)
7149
- )
7150
- } : {},
7168
+ source: runtimeContext.source,
7169
+ ...runtimeContext.usagePercent !== void 0 ? { usage_percent: runtimeContext.usagePercent } : {},
7151
7170
  ...usageRun?.completed_at ? { updated_at: usageRun.completed_at } : {}
7152
7171
  }
7153
7172
  };
@@ -9998,10 +10017,12 @@ function upsertAgentEventProjection(events, next) {
9998
10017
  next.subtitle,
9999
10018
  next.title
10000
10019
  );
10020
+ const status = next.status === "running" && previous.status !== "running" ? previous.status : next.status;
10001
10021
  const merged = {
10002
10022
  ...previous,
10003
10023
  ...next,
10004
10024
  id: previous.id,
10025
+ status,
10005
10026
  title: isGenericToolTitle(next.title) ? previous.title : next.title,
10006
10027
  created_at: earliestTimestamp(previous.created_at, next.created_at),
10007
10028
  subtitle: nextSubtitleIsFallback ? previous.subtitle ?? next.subtitle : next.subtitle ?? previous.subtitle,
@@ -12629,7 +12650,7 @@ function isNodeError11(error, code) {
12629
12650
  }
12630
12651
 
12631
12652
  // src/conversations/run-lifecycle.ts
12632
- import { createHash as createHash4 } from "crypto";
12653
+ import { createHash as createHash5 } from "crypto";
12633
12654
  import { readdir as readdir8 } from "fs/promises";
12634
12655
 
12635
12656
  // src/hermes/api-server.ts
@@ -12755,16 +12776,38 @@ async function runHermesCronJobAction(jobId, action, options = {}) {
12755
12776
  }
12756
12777
  async function createHermesRun(input, options = {}) {
12757
12778
  const headers = new Headers({ "content-type": "application/json" });
12758
- await applySessionKeyHeader(headers, input.session_key, options);
12759
- const response = await callHermesApi(
12779
+ const sessionKeyHeader = await applySessionKeyHeader(
12780
+ headers,
12781
+ input.session_key,
12782
+ options
12783
+ );
12784
+ const requestBody = buildHermesRunRequestBody(input);
12785
+ let response = await callHermesApi(
12760
12786
  "/v1/runs",
12761
12787
  {
12762
12788
  method: "POST",
12763
- body: JSON.stringify(input),
12764
- headers
12789
+ body: JSON.stringify(requestBody),
12790
+ headers,
12791
+ signal: options.signal
12765
12792
  },
12766
12793
  options
12767
12794
  );
12795
+ if (response.status === 403 && sessionKeyHeader && await isSessionKeyRejected(response.clone())) {
12796
+ void options.logger?.warn("hermes_session_key_rejected_downgrading", {
12797
+ profile: options.profileName ?? "default"
12798
+ });
12799
+ headers.delete(sessionKeyHeader);
12800
+ response = await callHermesApi(
12801
+ "/v1/runs",
12802
+ {
12803
+ method: "POST",
12804
+ body: JSON.stringify(requestBody),
12805
+ headers,
12806
+ signal: options.signal
12807
+ },
12808
+ options
12809
+ );
12810
+ }
12768
12811
  if (response.status === 404 || response.status === 503) {
12769
12812
  assertHermesRunsApiSupported(
12770
12813
  await readHermesVersion({ logger: options.logger }).catch(() => null),
@@ -12787,6 +12830,53 @@ async function createHermesRun(input, options = {}) {
12787
12830
  }
12788
12831
  return { run_id: runId, fallback: false };
12789
12832
  }
12833
+ function buildHermesRunRequestBody(input) {
12834
+ return {
12835
+ input: input.input,
12836
+ ...input.instructions ? { instructions: input.instructions } : {},
12837
+ ...input.conversation_history && input.conversation_history.length > 0 ? { conversation_history: input.conversation_history } : {},
12838
+ ...input.session_id ? { session_id: input.session_id } : {},
12839
+ ...input.model ? { model: input.model } : {}
12840
+ };
12841
+ }
12842
+ async function readHermesRunStatus(runId, options = {}) {
12843
+ const response = await callHermesApi(
12844
+ `/v1/runs/${encodeURIComponent(runId)}`,
12845
+ { method: "GET", signal: options.signal },
12846
+ options
12847
+ );
12848
+ if (response.status === 404) {
12849
+ return null;
12850
+ }
12851
+ assertHermesRunsApiSupported(
12852
+ await readHermesVersion({ logger: options.logger }).catch(() => null),
12853
+ response.status
12854
+ );
12855
+ const payload = await readJsonResponse(response);
12856
+ const status = readString10(payload, "status");
12857
+ const resolvedRunId = readString10(payload, "run_id") ?? readString10(payload, "runId") ?? runId;
12858
+ if (!status) {
12859
+ throw new LinkHttpError(
12860
+ 502,
12861
+ "hermes_run_status_invalid",
12862
+ "Hermes API Server did not return a run status"
12863
+ );
12864
+ }
12865
+ return {
12866
+ object: readString10(payload, "object") ?? void 0,
12867
+ run_id: resolvedRunId,
12868
+ status,
12869
+ output: payload.output,
12870
+ usage: payload.usage,
12871
+ error: payload.error,
12872
+ last_event: readString10(payload, "last_event") ?? void 0,
12873
+ session_id: readString10(payload, "session_id") ?? void 0,
12874
+ model: readString10(payload, "model") ?? void 0,
12875
+ created_at: payload.created_at,
12876
+ updated_at: payload.updated_at,
12877
+ raw: payload
12878
+ };
12879
+ }
12790
12880
  async function streamHermesRunEvents(runId, options = {}) {
12791
12881
  const response = await callHermesApi(
12792
12882
  `/v1/runs/${encodeURIComponent(runId)}/events`,
@@ -12995,10 +13085,10 @@ function parseHermesApiCapabilities(payload) {
12995
13085
  sessionKeyHeader: readString10(features, "session_key_header")
12996
13086
  };
12997
13087
  }
12998
- async function callHermesApi(path27, init, options) {
13088
+ async function callHermesApi(path28, init, options) {
12999
13089
  const method = init.method ?? "GET";
13000
13090
  const startedAt = Date.now();
13001
- void options.logger?.debug("hermes_api_request_started", { method, path: path27 });
13091
+ void options.logger?.debug("hermes_api_request_started", { method, path: path28 });
13002
13092
  const availability = await ensureHermesApiServerAvailable({
13003
13093
  fetchImpl: options.fetchImpl,
13004
13094
  logger: options.logger,
@@ -13006,7 +13096,7 @@ async function callHermesApi(path27, init, options) {
13006
13096
  });
13007
13097
  let config = availability.configResult.apiServer;
13008
13098
  const fetcher = options.fetchImpl ?? fetch;
13009
- const request = () => fetchHermesApi(fetcher, config, path27, init, options);
13099
+ const request = () => fetchHermesApi(fetcher, config, path28, init, options);
13010
13100
  let response;
13011
13101
  try {
13012
13102
  response = await request();
@@ -13014,7 +13104,7 @@ async function callHermesApi(path27, init, options) {
13014
13104
  logHermesApiError(
13015
13105
  options.logger,
13016
13106
  method,
13017
- path27,
13107
+ path28,
13018
13108
  options.profileName,
13019
13109
  startedAt,
13020
13110
  error
@@ -13025,7 +13115,7 @@ async function callHermesApi(path27, init, options) {
13025
13115
  logHermesApiResponse(
13026
13116
  options.logger,
13027
13117
  method,
13028
- path27,
13118
+ path28,
13029
13119
  options.profileName,
13030
13120
  startedAt,
13031
13121
  response
@@ -13034,7 +13124,7 @@ async function callHermesApi(path27, init, options) {
13034
13124
  }
13035
13125
  void options.logger?.warn("hermes_api_request_retrying_after_401", {
13036
13126
  method,
13037
- path: path27,
13127
+ path: path28,
13038
13128
  profile: options.profileName ?? "default",
13039
13129
  port: config.port ?? null,
13040
13130
  duration_ms: Date.now() - startedAt
@@ -13052,7 +13142,7 @@ async function callHermesApi(path27, init, options) {
13052
13142
  logHermesApiError(
13053
13143
  options.logger,
13054
13144
  method,
13055
- path27,
13145
+ path28,
13056
13146
  options.profileName,
13057
13147
  startedAt,
13058
13148
  error
@@ -13062,7 +13152,7 @@ async function callHermesApi(path27, init, options) {
13062
13152
  logHermesApiResponse(
13063
13153
  options.logger,
13064
13154
  method,
13065
- path27,
13155
+ path28,
13066
13156
  options.profileName,
13067
13157
  startedAt,
13068
13158
  response
@@ -13072,7 +13162,7 @@ async function callHermesApi(path27, init, options) {
13072
13162
  }
13073
13163
  void options.logger?.warn("hermes_api_request_repairing_after_401", {
13074
13164
  method,
13075
- path: path27,
13165
+ path: path28,
13076
13166
  profile: options.profileName ?? "default",
13077
13167
  port: config.port ?? null,
13078
13168
  duration_ms: Date.now() - startedAt
@@ -13092,7 +13182,7 @@ async function callHermesApi(path27, init, options) {
13092
13182
  logHermesApiError(
13093
13183
  options.logger,
13094
13184
  method,
13095
- path27,
13185
+ path28,
13096
13186
  options.profileName,
13097
13187
  startedAt,
13098
13188
  error
@@ -13102,21 +13192,21 @@ async function callHermesApi(path27, init, options) {
13102
13192
  logHermesApiResponse(
13103
13193
  options.logger,
13104
13194
  method,
13105
- path27,
13195
+ path28,
13106
13196
  options.profileName,
13107
13197
  startedAt,
13108
13198
  response
13109
13199
  );
13110
13200
  return response;
13111
13201
  }
13112
- async function fetchHermesApi(fetcher, config, path27, init, options) {
13202
+ async function fetchHermesApi(fetcher, config, path28, init, options) {
13113
13203
  const headers = new Headers(init.headers);
13114
13204
  headers.set("accept", headers.get("accept") ?? "application/json");
13115
13205
  if (config.key) {
13116
13206
  headers.set("x-api-key", config.key);
13117
13207
  headers.set("authorization", `Bearer ${config.key}`);
13118
13208
  }
13119
- return await fetcher(`http://127.0.0.1:${config.port}${path27}`, {
13209
+ return await fetcher(`http://127.0.0.1:${config.port}${path28}`, {
13120
13210
  ...init,
13121
13211
  headers
13122
13212
  }).catch((error) => {
@@ -13125,10 +13215,10 @@ async function fetchHermesApi(fetcher, config, path27, init, options) {
13125
13215
  }
13126
13216
  void options.logger?.warn("hermes_api_server_connect_failed", {
13127
13217
  method: String(init.method ?? "GET").toUpperCase(),
13128
- path: path27,
13218
+ path: path28,
13129
13219
  profile: options.profileName ?? "default",
13130
13220
  port: config.port ?? null,
13131
- url: `http://127.0.0.1:${config.port}${path27}`,
13221
+ url: `http://127.0.0.1:${config.port}${path28}`,
13132
13222
  error: error instanceof Error ? error.message : String(error)
13133
13223
  });
13134
13224
  throw new LinkHttpError(
@@ -13138,10 +13228,10 @@ async function fetchHermesApi(fetcher, config, path27, init, options) {
13138
13228
  );
13139
13229
  });
13140
13230
  }
13141
- function logHermesApiResponse(logger, method, path27, profileName, startedAt, response) {
13231
+ function logHermesApiResponse(logger, method, path28, profileName, startedAt, response) {
13142
13232
  const fields = {
13143
13233
  method,
13144
- path: path27,
13234
+ path: path28,
13145
13235
  profile: profileName ?? "default",
13146
13236
  status: response.status,
13147
13237
  duration_ms: Date.now() - startedAt
@@ -13162,10 +13252,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
13162
13252
  ...upstreamError ? { upstream_error: upstreamError } : {}
13163
13253
  });
13164
13254
  }
13165
- function logHermesApiError(logger, method, path27, profileName, startedAt, error) {
13255
+ function logHermesApiError(logger, method, path28, profileName, startedAt, error) {
13166
13256
  void logger?.warn("hermes_api_request_failed", {
13167
13257
  method,
13168
- path: path27,
13258
+ path: path28,
13169
13259
  profile: profileName ?? "default",
13170
13260
  duration_ms: Date.now() - startedAt,
13171
13261
  ...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
@@ -13943,178 +14033,759 @@ function toRecord9(value) {
13943
14033
  return typeof value === "object" && value !== null ? value : {};
13944
14034
  }
13945
14035
 
13946
- // src/conversations/stream-events.ts
13947
- function normalizeHermesStreamEvent(event) {
13948
- const responseEvent = normalizeHermesResponseEvent(event);
13949
- if (responseEvent || isHermesResponseEvent(event)) {
13950
- return responseEvent;
14036
+ // src/conversations/run-backend.ts
14037
+ var RUNS_BACKEND_VALUES = /* @__PURE__ */ new Set(["run", "runs", "v1/runs", "/v1/runs"]);
14038
+ var RESPONSES_BACKEND_VALUES = /* @__PURE__ */ new Set([
14039
+ "response",
14040
+ "responses",
14041
+ "v1/responses",
14042
+ "/v1/responses"
14043
+ ]);
14044
+ function resolveConversationRunBackend(env = process.env) {
14045
+ const raw = (env.HERMESLINK_CONVERSATION_BACKEND ?? env.HERMESLINK_CHAT_BACKEND ?? "").trim().toLowerCase();
14046
+ if (RESPONSES_BACKEND_VALUES.has(raw)) {
14047
+ return "responses";
13951
14048
  }
13952
- if (isTopLevelErrorEvent(event)) {
13953
- return {
13954
- ...event,
13955
- payloadType: "run.failed",
13956
- payload: {
13957
- ...event.payload,
13958
- type: "run.failed",
13959
- error: {
13960
- message: readErrorMessage2(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
14049
+ if (RUNS_BACKEND_VALUES.has(raw)) {
14050
+ return "runs";
14051
+ }
14052
+ return "runs";
14053
+ }
14054
+ function isRunToolResultCompensationEnabled(env = process.env) {
14055
+ const raw = env.HERMESLINK_RUN_TOOL_RESULT_COMPENSATION?.trim().toLowerCase();
14056
+ return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
14057
+ }
14058
+
14059
+ // src/conversations/run-transcript-enrichment.ts
14060
+ import { readFile as readFile12, stat as stat12 } from "fs/promises";
14061
+ import path19 from "path";
14062
+ var MESSAGE_COLUMNS2 = [
14063
+ "id",
14064
+ "session_id",
14065
+ "role",
14066
+ "content",
14067
+ "tool_call_id",
14068
+ "tool_calls",
14069
+ "tool_name",
14070
+ "timestamp"
14071
+ ];
14072
+ async function buildRunTranscriptEvents(input) {
14073
+ const sessionId = input.hermesSessionId.trim();
14074
+ if (!sessionId) {
14075
+ return [];
14076
+ }
14077
+ const profileName = isValidProfileName3(input.profileName) ? input.profileName : "default";
14078
+ const rows = await readHermesTranscriptRows(profileName, sessionId);
14079
+ if (rows.length === 0) {
14080
+ return [];
14081
+ }
14082
+ const lowerBoundSeconds = runStartedLowerBoundSeconds(input.runStartedAt);
14083
+ const reusableEventIds = groupReusableToolEventIds(
14084
+ input.assistant?.agent_events ?? []
14085
+ );
14086
+ const events = [];
14087
+ const pendingToolCalls = [];
14088
+ const toolCallsById = /* @__PURE__ */ new Map();
14089
+ for (const row of rows) {
14090
+ if (!isRunRow(row, lowerBoundSeconds)) {
14091
+ continue;
14092
+ }
14093
+ const role = normalizeRole(row.role);
14094
+ if (role === "assistant") {
14095
+ for (const toolCall of readHermesToolCalls2(row)) {
14096
+ const pending = { toolCall };
14097
+ pendingToolCalls.push(pending);
14098
+ if (toolCall.id) {
14099
+ toolCallsById.set(toolCall.id, pending);
13961
14100
  }
13962
14101
  }
13963
- };
13964
- }
13965
- if (event.eventName === "hermes.tool.progress") {
13966
- const toolName = readString12(event.payload, "tool") ?? readString12(event.payload, "name") ?? "tool";
13967
- const preview = readString12(event.payload, "label") ?? readString12(event.payload, "preview") ?? toolName;
13968
- return {
13969
- ...event,
13970
- payloadType: "tool.started",
13971
- payload: {
13972
- ...event.payload,
13973
- type: "tool.started",
13974
- tool: toolName,
13975
- tool_name: toolName,
13976
- preview
14102
+ continue;
14103
+ }
14104
+ if (role === "tool") {
14105
+ const pending = consumePendingToolCall2({
14106
+ toolMessage: row,
14107
+ pendingToolCalls,
14108
+ toolCallsById
14109
+ });
14110
+ if (!pending) {
14111
+ continue;
13977
14112
  }
13978
- };
14113
+ events.push(
14114
+ toolCompletedEvent(
14115
+ row,
14116
+ pending,
14117
+ consumeReusableToolEventId(reusableEventIds, pending, row)
14118
+ )
14119
+ );
14120
+ }
13979
14121
  }
13980
- if (event.payloadType !== "chat.completion.chunk") {
13981
- return event;
14122
+ return events;
14123
+ }
14124
+ async function readHermesTranscriptRows(profileName, sessionId) {
14125
+ const profileDir = resolveHermesProfileDir(profileName);
14126
+ const dbPath = path19.join(profileDir, "state.db");
14127
+ const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path19.join(profileDir, "sessions"));
14128
+ const [dbRows, jsonlRows] = await Promise.all([
14129
+ readStateDbMessages2(dbPath, sessionId),
14130
+ readJsonlMessages2(sessionsDir, sessionId)
14131
+ ]);
14132
+ return selectTranscriptRows(dbRows, jsonlRows);
14133
+ }
14134
+ function selectTranscriptRows(dbRows, jsonlRows) {
14135
+ if (jsonlRows.length === 0) {
14136
+ return dbRows;
13982
14137
  }
13983
- const delta = readChatCompletionDelta(event.payload);
13984
- if (delta) {
13985
- return {
13986
- ...event,
13987
- payloadType: "message.delta",
13988
- payload: { type: "message.delta", delta }
13989
- };
14138
+ if (dbRows.length === 0) {
14139
+ return jsonlRows;
13990
14140
  }
13991
- const finishReason = readChatCompletionFinishReason(event.payload);
13992
- const usage = readChatCompletionUsage(event.payload);
13993
- if (finishReason || usage) {
13994
- return {
13995
- ...event,
13996
- payloadType: "run.completed",
13997
- payload: {
13998
- type: "run.completed",
13999
- ...finishReason ? { finish_reason: finishReason } : {},
14000
- ...usage ? { usage } : {}
14001
- }
14002
- };
14141
+ if (jsonlRows.length <= dbRows.length) {
14142
+ return dbRows;
14003
14143
  }
14004
- return null;
14144
+ const jsonlTimestampedRows = jsonlRows.filter(
14145
+ (row) => hasUsableTimestamp(row.timestamp)
14146
+ ).length;
14147
+ const dbTimestampedRows = dbRows.filter(
14148
+ (row) => hasUsableTimestamp(row.timestamp)
14149
+ ).length;
14150
+ if (jsonlTimestampedRows === 0 && dbTimestampedRows > 0) {
14151
+ return dbRows;
14152
+ }
14153
+ return jsonlRows;
14005
14154
  }
14006
- function isHermesResponseEvent(event) {
14007
- return event.payloadType.startsWith("response.") || event.eventName.startsWith("response.");
14155
+ async function readStateDbMessages2(dbPath, sessionId) {
14156
+ if (!await isFile2(dbPath)) {
14157
+ return [];
14158
+ }
14159
+ let db = null;
14160
+ try {
14161
+ db = openSqliteDatabase(dbPath, {
14162
+ readonly: true,
14163
+ timeout: 1e3
14164
+ });
14165
+ const columns = readTableColumns3(db, "messages");
14166
+ if (!columns.has("session_id") || !columns.has("role")) {
14167
+ return [];
14168
+ }
14169
+ const selectColumns = MESSAGE_COLUMNS2.map(
14170
+ (column) => columns.has(column) ? quoteIdentifier2(column) : `NULL AS ${column}`
14171
+ ).join(", ");
14172
+ return db.prepare(
14173
+ `
14174
+ SELECT ${selectColumns}
14175
+ FROM messages
14176
+ WHERE session_id = ?
14177
+ ORDER BY timestamp, id
14178
+ `
14179
+ ).all(sessionId);
14180
+ } catch {
14181
+ return [];
14182
+ } finally {
14183
+ db?.close();
14184
+ }
14008
14185
  }
14009
- function normalizeHermesResponseEvent(event) {
14010
- switch (event.payloadType) {
14011
- case "response.output_text.delta": {
14012
- const delta = readDelta(event.payload);
14013
- return delta ? {
14014
- ...event,
14015
- payloadType: "message.delta",
14016
- payload: { type: "message.delta", delta }
14017
- } : null;
14186
+ async function readJsonlMessages2(sessionsDir, sessionId) {
14187
+ if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
14188
+ return [];
14189
+ }
14190
+ const transcriptPath = path19.join(sessionsDir, `${sessionId}.jsonl`);
14191
+ const raw = await readFile12(transcriptPath, "utf8").catch((error) => {
14192
+ if (isNodeError13(error, "ENOENT")) {
14193
+ return "";
14018
14194
  }
14019
- case "response.output_item.added":
14020
- return normalizeResponseOutputItemAdded(event);
14021
- case "response.output_item.done":
14022
- return normalizeResponseOutputItemDone(event);
14023
- case "response.completed":
14024
- return normalizeResponseCompleted(event);
14025
- case "response.failed":
14026
- return normalizeResponseFailed(event);
14027
- case "response.output_text.done": {
14028
- const delta = readDelta(event.payload);
14029
- return delta ? {
14030
- ...event,
14031
- payloadType: "message.delta",
14032
- payload: { type: "message.delta", delta }
14033
- } : null;
14195
+ throw error;
14196
+ });
14197
+ if (!raw.trim()) {
14198
+ return [];
14199
+ }
14200
+ const rows = [];
14201
+ for (const line of raw.split(/\r?\n/u)) {
14202
+ if (!line.trim()) {
14203
+ continue;
14204
+ }
14205
+ try {
14206
+ const parsed = JSON.parse(line);
14207
+ const role = readString12(parsed, "role");
14208
+ if (!role) {
14209
+ continue;
14210
+ }
14211
+ rows.push({
14212
+ ...parsed,
14213
+ role,
14214
+ content: normalizeContent3(parsed.content),
14215
+ timestamp: readNumber3(parsed.timestamp) ?? readNumber3(parsed.created_at) ?? readNumber3(parsed.createdAt) ?? void 0
14216
+ });
14217
+ } catch {
14218
+ continue;
14034
14219
  }
14035
- case "response.created":
14036
- return null;
14037
- default:
14038
- return null;
14039
14220
  }
14221
+ return rows;
14040
14222
  }
14041
- function normalizeResponseOutputItemAdded(event) {
14042
- const item = toRecord10(event.payload.item);
14043
- if (readString12(item, "type") !== "function_call") {
14223
+ function readHermesToolCalls2(row) {
14224
+ const decoded = parseJsonValue2(row.tool_calls) ?? row.tool_calls;
14225
+ const values = Array.isArray(decoded) ? decoded : decoded ? [decoded] : [];
14226
+ return values.map((value) => normalizeHermesToolCall2(value)).filter(
14227
+ (toolCall) => Boolean(toolCall)
14228
+ );
14229
+ }
14230
+ function normalizeHermesToolCall2(value) {
14231
+ const record = toRecord10(value);
14232
+ if (Object.keys(record).length === 0) {
14044
14233
  return null;
14045
14234
  }
14046
- const toolName = readString12(item, "name") ?? "tool";
14047
- const argumentsValue = parseJsonValue2(item.arguments) ?? item.arguments;
14235
+ const fn = toRecord10(record.function);
14236
+ const id = readString12(record, "id") ?? readString12(record, "call_id") ?? readString12(record, "tool_call_id") ?? readString12(fn, "id") ?? void 0;
14237
+ const name = readString12(fn, "name") ?? readString12(record, "name") ?? readString12(record, "tool_name") ?? readString12(record, "tool") ?? "tool";
14238
+ const rawArguments = fn.arguments ?? record.arguments ?? record.args ?? record.input;
14048
14239
  return {
14049
- ...event,
14050
- payloadType: "tool.started",
14051
- payload: {
14052
- type: "tool.started",
14053
- tool: toolName,
14054
- tool_name: toolName,
14055
- name: toolName,
14056
- tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
14057
- arguments: argumentsValue,
14058
- preview: toolName,
14059
- response_item_id: readString12(item, "id") ?? void 0
14060
- }
14240
+ ...id ? { id } : {},
14241
+ name,
14242
+ ...rawArguments === void 0 ? {} : { arguments: parseJsonValue2(rawArguments) ?? rawArguments },
14243
+ raw: value
14061
14244
  };
14062
14245
  }
14063
- function normalizeResponseOutputItemDone(event) {
14064
- const item = toRecord10(event.payload.item);
14065
- if (readString12(item, "type") === "message") {
14066
- const delta = extractResponseAssistantText({ output: [item] });
14067
- return delta ? {
14068
- ...event,
14069
- payloadType: "message.delta",
14246
+ function consumePendingToolCall2(input) {
14247
+ const toolCallId = readString12(input.toolMessage, "tool_call_id");
14248
+ const toolName = readString12(input.toolMessage, "tool_name");
14249
+ let pending = toolCallId ? input.toolCallsById.get(toolCallId) : void 0;
14250
+ if (!pending && toolName) {
14251
+ pending = input.pendingToolCalls.find(
14252
+ (item) => item.toolCall.name === toolName
14253
+ );
14254
+ }
14255
+ pending ??= input.pendingToolCalls[0];
14256
+ if (!pending) {
14257
+ return void 0;
14258
+ }
14259
+ const index = input.pendingToolCalls.indexOf(pending);
14260
+ if (index >= 0) {
14261
+ input.pendingToolCalls.splice(index, 1);
14262
+ }
14263
+ if (pending.toolCall.id) {
14264
+ input.toolCallsById.delete(pending.toolCall.id);
14265
+ }
14266
+ return pending;
14267
+ }
14268
+ function toolCompletedEvent(row, pending, existingEventId) {
14269
+ const output = normalizeContent3(row.content);
14270
+ const parsedOutput = parseJsonValue2(output);
14271
+ const toolCallId = readString12(row, "tool_call_id") ?? pending?.toolCall.id;
14272
+ const toolName = readString12(row, "tool_name") ?? pending?.toolCall.name ?? "tool";
14273
+ const payload = {
14274
+ type: "tool.completed",
14275
+ event: "tool.completed",
14276
+ tool: toolName,
14277
+ tool_name: toolName,
14278
+ name: toolName,
14279
+ ...existingEventId ? {
14280
+ id: existingEventId,
14281
+ ...toolCallId ? { hermes_tool_call_id: toolCallId } : {}
14282
+ } : toolCallId ? { tool_call_id: toolCallId, id: toolCallId } : {},
14283
+ ...pending?.toolCall.arguments === void 0 ? {} : { arguments: pending.toolCall.arguments },
14284
+ output,
14285
+ content: output,
14286
+ result: parsedOutput ?? output,
14287
+ ...pending ? { tool_call: pending.toolCall.raw } : {},
14288
+ timestamp: row.timestamp
14289
+ };
14290
+ return {
14291
+ eventName: "tool.completed",
14292
+ payloadType: "tool.completed",
14293
+ payload,
14294
+ rawPayload: {
14295
+ format: "hermes-message",
14296
+ message: row,
14297
+ ...pending ? { tool_call: pending.toolCall.raw } : {}
14298
+ }
14299
+ };
14300
+ }
14301
+ function groupReusableToolEventIds(events) {
14302
+ const grouped = /* @__PURE__ */ new Map();
14303
+ for (const event of events) {
14304
+ if (event.kind && event.kind !== "tool") {
14305
+ continue;
14306
+ }
14307
+ const key = normalizeToolTitle(event.title);
14308
+ if (!key) {
14309
+ continue;
14310
+ }
14311
+ grouped.set(key, [...grouped.get(key) ?? [], event.id]);
14312
+ }
14313
+ return grouped;
14314
+ }
14315
+ function consumeReusableToolEventId(grouped, pending, row) {
14316
+ const toolName = pending?.toolCall.name ?? readString12(row, "tool_name") ?? "tool";
14317
+ const key = normalizeToolTitle(humanizeToolName2(toolName));
14318
+ const ids = grouped.get(key);
14319
+ const id = ids?.shift();
14320
+ if (ids && ids.length === 0) {
14321
+ grouped.delete(key);
14322
+ }
14323
+ return id;
14324
+ }
14325
+ function humanizeToolName2(value) {
14326
+ const normalized = value.trim().replace(/([a-z0-9])([A-Z])/gu, "$1 $2").replace(/[_-]+/gu, " ").replace(/\s+/gu, " ");
14327
+ if (!normalized) {
14328
+ return "\u5DE5\u5177\u8C03\u7528";
14329
+ }
14330
+ return normalized.split(" ").map(
14331
+ (part) => part ? `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}` : part
14332
+ ).join(" ");
14333
+ }
14334
+ function normalizeToolTitle(value) {
14335
+ return value.trim().toLowerCase().replace(/[\s_-]+/gu, "");
14336
+ }
14337
+ function isRunRow(row, lowerBoundSeconds) {
14338
+ if (lowerBoundSeconds === null) {
14339
+ return true;
14340
+ }
14341
+ const timestamp = readNumber3(row.timestamp);
14342
+ if (!timestamp) {
14343
+ return false;
14344
+ }
14345
+ const seconds = timestamp > 1e10 ? timestamp / 1e3 : timestamp;
14346
+ return seconds >= lowerBoundSeconds;
14347
+ }
14348
+ function hasUsableTimestamp(value) {
14349
+ return readNumber3(value) !== null;
14350
+ }
14351
+ function runStartedLowerBoundSeconds(value) {
14352
+ const millis = Date.parse(value);
14353
+ if (Number.isNaN(millis)) {
14354
+ return null;
14355
+ }
14356
+ return millis / 1e3 - 1;
14357
+ }
14358
+ function normalizeRole(value) {
14359
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
14360
+ }
14361
+ function normalizeContent3(value) {
14362
+ if (typeof value === "string") {
14363
+ return value;
14364
+ }
14365
+ if (Array.isArray(value)) {
14366
+ return value.map((item) => {
14367
+ if (typeof item === "string") {
14368
+ return item;
14369
+ }
14370
+ if (typeof item === "object" && item !== null) {
14371
+ return readString12(item, "text") ?? "";
14372
+ }
14373
+ return "";
14374
+ }).filter(Boolean).join("");
14375
+ }
14376
+ return "";
14377
+ }
14378
+ function parseJsonValue2(value) {
14379
+ if (typeof value !== "string") {
14380
+ return void 0;
14381
+ }
14382
+ const trimmed = value.trim();
14383
+ if (!trimmed) {
14384
+ return void 0;
14385
+ }
14386
+ try {
14387
+ return JSON.parse(trimmed);
14388
+ } catch {
14389
+ return void 0;
14390
+ }
14391
+ }
14392
+ function readTableColumns3(db, tableName) {
14393
+ try {
14394
+ const rows = db.prepare(`PRAGMA table_info(${quoteIdentifier2(tableName)})`).all();
14395
+ return new Set(
14396
+ rows.map((row) => typeof row.name === "string" ? row.name : "").filter(Boolean)
14397
+ );
14398
+ } catch {
14399
+ return /* @__PURE__ */ new Set();
14400
+ }
14401
+ }
14402
+ function quoteIdentifier2(value) {
14403
+ return `"${value.replaceAll('"', '""')}"`;
14404
+ }
14405
+ async function isFile2(filePath) {
14406
+ return stat12(filePath).then((value) => value.isFile()).catch((error) => {
14407
+ if (isNodeError13(error, "ENOENT")) {
14408
+ return false;
14409
+ }
14410
+ throw error;
14411
+ });
14412
+ }
14413
+ function isValidProfileName3(value) {
14414
+ return typeof value === "string" && /^[a-zA-Z0-9._-]{1,64}$/u.test(value);
14415
+ }
14416
+ function readString12(payload, key) {
14417
+ const value = payload[key];
14418
+ return typeof value === "string" && value.trim() ? value.trim() : null;
14419
+ }
14420
+ function readNumber3(value) {
14421
+ if (typeof value === "number" && Number.isFinite(value)) {
14422
+ return value;
14423
+ }
14424
+ if (typeof value !== "string" || !value.trim()) {
14425
+ return null;
14426
+ }
14427
+ const numeric = Number(value);
14428
+ if (Number.isFinite(numeric)) {
14429
+ return numeric;
14430
+ }
14431
+ const millis = Date.parse(value);
14432
+ return Number.isNaN(millis) ? null : millis;
14433
+ }
14434
+ function toRecord10(value) {
14435
+ return typeof value === "object" && value !== null ? value : {};
14436
+ }
14437
+ function isNodeError13(error, code) {
14438
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
14439
+ }
14440
+
14441
+ // src/conversations/run-tool-event-ids.ts
14442
+ import { createHash as createHash4 } from "crypto";
14443
+ var RunToolEventIdCoalescer = class {
14444
+ scope;
14445
+ ordinal = 0;
14446
+ pendingByToolKey = /* @__PURE__ */ new Map();
14447
+ startedByFingerprint = /* @__PURE__ */ new Map();
14448
+ completedByFingerprint = /* @__PURE__ */ new Map();
14449
+ constructor(options) {
14450
+ this.scope = `${options.runId}:${options.hermesRunId ?? ""}`;
14451
+ }
14452
+ normalize(event) {
14453
+ const type = readToolEventType(event);
14454
+ if (!type || hasStableToolEventId(event.payload)) {
14455
+ return event;
14456
+ }
14457
+ const toolKey = normalizeToolKey(readToolName(event.payload));
14458
+ if (type === "tool.started") {
14459
+ return withGeneratedToolEventId(
14460
+ event,
14461
+ this.idForStartedToolEvent(toolKey, event.payload)
14462
+ );
14463
+ }
14464
+ if (isTerminalToolEvent(type)) {
14465
+ return withGeneratedToolEventId(
14466
+ event,
14467
+ this.idForTerminalToolEvent(type, toolKey, event.payload)
14468
+ );
14469
+ }
14470
+ return event;
14471
+ }
14472
+ idForStartedToolEvent(toolKey, payload) {
14473
+ const fingerprint = startedToolEventFingerprint(toolKey, payload);
14474
+ if (fingerprint) {
14475
+ const existing = this.startedByFingerprint.get(fingerprint);
14476
+ if (existing) {
14477
+ return existing.id;
14478
+ }
14479
+ }
14480
+ const pending = {
14481
+ id: this.nextId(toolKey),
14482
+ toolKey
14483
+ };
14484
+ const pendingForTool = this.pendingByToolKey.get(toolKey) ?? [];
14485
+ pendingForTool.push(pending);
14486
+ this.pendingByToolKey.set(toolKey, pendingForTool);
14487
+ if (fingerprint) {
14488
+ this.startedByFingerprint.set(fingerprint, pending);
14489
+ }
14490
+ return pending.id;
14491
+ }
14492
+ idForTerminalToolEvent(type, toolKey, payload) {
14493
+ const pending = this.shiftPendingToolEvent(toolKey);
14494
+ const fingerprint = terminalToolEventFingerprint(type, toolKey, payload);
14495
+ if (pending) {
14496
+ this.completedByFingerprint.set(fingerprint, pending.id);
14497
+ return pending.id;
14498
+ }
14499
+ const existing = this.completedByFingerprint.get(fingerprint);
14500
+ if (existing) {
14501
+ return existing;
14502
+ }
14503
+ const id = this.nextId(toolKey);
14504
+ this.completedByFingerprint.set(fingerprint, id);
14505
+ return id;
14506
+ }
14507
+ shiftPendingToolEvent(toolKey) {
14508
+ const exact = this.shiftPendingByToolKey(toolKey);
14509
+ if (exact) {
14510
+ return exact;
14511
+ }
14512
+ for (const [candidateToolKey] of this.pendingByToolKey) {
14513
+ const pending = this.shiftPendingByToolKey(candidateToolKey);
14514
+ if (pending) {
14515
+ return pending;
14516
+ }
14517
+ }
14518
+ return void 0;
14519
+ }
14520
+ shiftPendingByToolKey(toolKey) {
14521
+ const pendingForTool = this.pendingByToolKey.get(toolKey);
14522
+ const pending = pendingForTool?.shift();
14523
+ if (pendingForTool && pendingForTool.length === 0) {
14524
+ this.pendingByToolKey.delete(toolKey);
14525
+ }
14526
+ return pending;
14527
+ }
14528
+ nextId(toolKey) {
14529
+ this.ordinal += 1;
14530
+ return `tool_${hashStableValue(`${this.scope}:${this.ordinal}:${toolKey}`)}`;
14531
+ }
14532
+ };
14533
+ function readToolEventType(event) {
14534
+ const type = readString13(event.payload, "type") ?? readString13(event.payload, "event") ?? event.payloadType;
14535
+ return type.startsWith("tool.") ? type : null;
14536
+ }
14537
+ function isTerminalToolEvent(type) {
14538
+ return type === "tool.completed" || type === "tool.failed" || type === "tool.error";
14539
+ }
14540
+ function hasStableToolEventId(payload) {
14541
+ const tool = toRecord11(payload.tool);
14542
+ const call = toRecord11(payload.tool_call ?? payload.toolCall);
14543
+ const fn = toRecord11(call.function ?? payload.function);
14544
+ return Boolean(
14545
+ readString13(payload, "tool_call_id") ?? readString13(payload, "toolCallId") ?? readString13(payload, "call_id") ?? readString13(payload, "id") ?? readString13(tool, "id") ?? readString13(call, "id") ?? readString13(fn, "id")
14546
+ );
14547
+ }
14548
+ function readToolName(payload) {
14549
+ const tool = toRecord11(payload.tool);
14550
+ const call = toRecord11(payload.tool_call ?? payload.toolCall);
14551
+ const fn = toRecord11(call.function ?? payload.function);
14552
+ return readString13(payload, "tool_name") ?? readString13(payload, "toolName") ?? readString13(payload, "name") ?? readString13(payload, "tool") ?? readString13(tool, "name") ?? readString13(call, "name") ?? readString13(fn, "name") ?? "tool";
14553
+ }
14554
+ function withGeneratedToolEventId(event, id) {
14555
+ return {
14556
+ ...event,
14557
+ payload: {
14558
+ ...event.payload,
14559
+ id,
14560
+ link_tool_event_id: id,
14561
+ link_generated_tool_event_id: true
14562
+ }
14563
+ };
14564
+ }
14565
+ function startedToolEventFingerprint(toolKey, payload) {
14566
+ const timestamp = payload.timestamp ?? payload.started_at ?? payload.created_at;
14567
+ if (timestamp === void 0 || timestamp === null) {
14568
+ return null;
14569
+ }
14570
+ return stableStringify2({
14571
+ toolKey,
14572
+ timestamp,
14573
+ preview: payload.preview,
14574
+ arguments: payload.arguments ?? payload.args ?? payload.input
14575
+ });
14576
+ }
14577
+ function terminalToolEventFingerprint(type, toolKey, payload) {
14578
+ return stableStringify2({
14579
+ type,
14580
+ toolKey,
14581
+ timestamp: payload.timestamp ?? payload.completed_at ?? payload.created_at,
14582
+ duration: payload.duration,
14583
+ error: payload.error,
14584
+ output: payload.output ?? payload.content ?? payload.result ?? payload.message ?? null
14585
+ });
14586
+ }
14587
+ function normalizeToolKey(value) {
14588
+ return value.trim().toLowerCase().replace(/[\s-]+/gu, "_") || "tool";
14589
+ }
14590
+ function stableStringify2(value) {
14591
+ try {
14592
+ return JSON.stringify(value, (_key, data) => {
14593
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
14594
+ return data;
14595
+ }
14596
+ return Object.fromEntries(
14597
+ Object.entries(data).sort(
14598
+ ([left], [right]) => left.localeCompare(right)
14599
+ )
14600
+ );
14601
+ });
14602
+ } catch {
14603
+ return String(value);
14604
+ }
14605
+ }
14606
+ function hashStableValue(value) {
14607
+ return createHash4("sha256").update(value).digest("hex").slice(0, 16);
14608
+ }
14609
+ function readString13(payload, key) {
14610
+ const value = payload[key];
14611
+ return typeof value === "string" && value.trim() ? value.trim() : null;
14612
+ }
14613
+ function toRecord11(value) {
14614
+ return typeof value === "object" && value !== null ? value : {};
14615
+ }
14616
+
14617
+ // src/conversations/stream-events.ts
14618
+ function normalizeHermesStreamEvent(event) {
14619
+ const responseEvent = normalizeHermesResponseEvent(event);
14620
+ if (responseEvent || isHermesResponseEvent(event)) {
14621
+ return responseEvent;
14622
+ }
14623
+ if (isTopLevelErrorEvent(event)) {
14624
+ return {
14625
+ ...event,
14626
+ payloadType: "run.failed",
14627
+ payload: {
14628
+ ...event.payload,
14629
+ type: "run.failed",
14630
+ error: {
14631
+ message: readErrorMessage2(event.payload) ?? readDelta(event.payload) ?? "Hermes run failed"
14632
+ }
14633
+ }
14634
+ };
14635
+ }
14636
+ if (event.eventName === "hermes.tool.progress") {
14637
+ const toolName = readString14(event.payload, "tool") ?? readString14(event.payload, "name") ?? "tool";
14638
+ const preview = readString14(event.payload, "label") ?? readString14(event.payload, "preview") ?? toolName;
14639
+ return {
14640
+ ...event,
14641
+ payloadType: "tool.started",
14642
+ payload: {
14643
+ ...event.payload,
14644
+ type: "tool.started",
14645
+ tool: toolName,
14646
+ tool_name: toolName,
14647
+ preview
14648
+ }
14649
+ };
14650
+ }
14651
+ if (event.payloadType !== "chat.completion.chunk") {
14652
+ return event;
14653
+ }
14654
+ const delta = readChatCompletionDelta(event.payload);
14655
+ if (delta) {
14656
+ return {
14657
+ ...event,
14658
+ payloadType: "message.delta",
14659
+ payload: { type: "message.delta", delta }
14660
+ };
14661
+ }
14662
+ const finishReason = readChatCompletionFinishReason(event.payload);
14663
+ const usage = readChatCompletionUsage(event.payload);
14664
+ if (finishReason || usage) {
14665
+ return {
14666
+ ...event,
14667
+ payloadType: "run.completed",
14668
+ payload: {
14669
+ type: "run.completed",
14670
+ ...finishReason ? { finish_reason: finishReason } : {},
14671
+ ...usage ? { usage } : {}
14672
+ }
14673
+ };
14674
+ }
14675
+ return null;
14676
+ }
14677
+ function isHermesResponseEvent(event) {
14678
+ return event.payloadType.startsWith("response.") || event.eventName.startsWith("response.");
14679
+ }
14680
+ function normalizeHermesResponseEvent(event) {
14681
+ switch (event.payloadType) {
14682
+ case "response.output_text.delta": {
14683
+ const delta = readDelta(event.payload);
14684
+ return delta ? {
14685
+ ...event,
14686
+ payloadType: "message.delta",
14687
+ payload: { type: "message.delta", delta }
14688
+ } : null;
14689
+ }
14690
+ case "response.output_item.added":
14691
+ return normalizeResponseOutputItemAdded(event);
14692
+ case "response.output_item.done":
14693
+ return normalizeResponseOutputItemDone(event);
14694
+ case "response.completed":
14695
+ return normalizeResponseCompleted(event);
14696
+ case "response.failed":
14697
+ return normalizeResponseFailed(event);
14698
+ case "response.output_text.done": {
14699
+ const delta = readDelta(event.payload);
14700
+ return delta ? {
14701
+ ...event,
14702
+ payloadType: "message.delta",
14703
+ payload: { type: "message.delta", delta }
14704
+ } : null;
14705
+ }
14706
+ case "response.created":
14707
+ return null;
14708
+ default:
14709
+ return null;
14710
+ }
14711
+ }
14712
+ function normalizeResponseOutputItemAdded(event) {
14713
+ const item = toRecord12(event.payload.item);
14714
+ if (readString14(item, "type") !== "function_call") {
14715
+ return null;
14716
+ }
14717
+ const toolName = readString14(item, "name") ?? "tool";
14718
+ const argumentsValue = parseJsonValue3(item.arguments) ?? item.arguments;
14719
+ return {
14720
+ ...event,
14721
+ payloadType: "tool.started",
14722
+ payload: {
14723
+ type: "tool.started",
14724
+ tool: toolName,
14725
+ tool_name: toolName,
14726
+ name: toolName,
14727
+ tool_call_id: readString14(item, "call_id") ?? readString14(item, "id"),
14728
+ arguments: argumentsValue,
14729
+ preview: toolName,
14730
+ response_item_id: readString14(item, "id") ?? void 0
14731
+ }
14732
+ };
14733
+ }
14734
+ function normalizeResponseOutputItemDone(event) {
14735
+ const item = toRecord12(event.payload.item);
14736
+ if (readString14(item, "type") === "message") {
14737
+ const delta = extractResponseAssistantText({ output: [item] });
14738
+ return delta ? {
14739
+ ...event,
14740
+ payloadType: "message.delta",
14070
14741
  payload: { type: "message.delta", delta }
14071
14742
  } : null;
14072
14743
  }
14073
- if (readString12(item, "type") !== "function_call_output") {
14744
+ if (readString14(item, "type") !== "function_call_output") {
14074
14745
  return null;
14075
14746
  }
14076
14747
  const output = readResponseItemOutput(item.output);
14077
- const parsedOutput = parseJsonValue2(output);
14748
+ const parsedOutput = parseJsonValue3(output);
14078
14749
  return {
14079
14750
  ...event,
14080
14751
  payloadType: "tool.completed",
14081
14752
  payload: {
14082
14753
  type: "tool.completed",
14083
- tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
14084
- status: readString12(item, "status") ?? "completed",
14754
+ tool_call_id: readString14(item, "call_id") ?? readString14(item, "id"),
14755
+ status: readString14(item, "status") ?? "completed",
14085
14756
  output,
14086
14757
  content: output,
14087
14758
  result: parsedOutput ?? output,
14088
- response_item_id: readString12(item, "id") ?? void 0
14759
+ response_item_id: readString14(item, "id") ?? void 0
14089
14760
  }
14090
14761
  };
14091
14762
  }
14092
14763
  function normalizeResponseCompleted(event) {
14093
- const response = toRecord10(event.payload.response);
14764
+ const response = toRecord12(event.payload.response);
14094
14765
  return {
14095
14766
  ...event,
14096
14767
  payloadType: "run.completed",
14097
14768
  payload: {
14098
14769
  type: "run.completed",
14099
- response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
14100
- usage: toRecord10(response.usage),
14770
+ response_id: readString14(response, "id") ?? readString14(event.payload, "id"),
14771
+ usage: toRecord12(response.usage),
14101
14772
  response
14102
14773
  }
14103
14774
  };
14104
14775
  }
14105
14776
  function normalizeResponseFailed(event) {
14106
- const response = toRecord10(event.payload.response);
14107
- const error = toRecord10(response.error);
14777
+ const response = toRecord12(event.payload.response);
14778
+ const error = toRecord12(response.error);
14108
14779
  return {
14109
14780
  ...event,
14110
14781
  payloadType: "run.failed",
14111
14782
  payload: {
14112
14783
  type: "run.failed",
14113
- response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
14784
+ response_id: readString14(response, "id") ?? readString14(event.payload, "id"),
14114
14785
  error: {
14115
- message: readString12(error, "message") ?? readString12(event.payload, "message") ?? "Hermes run failed"
14786
+ message: readString14(error, "message") ?? readString14(event.payload, "message") ?? "Hermes run failed"
14116
14787
  },
14117
- usage: toRecord10(response.usage),
14788
+ usage: toRecord12(response.usage),
14118
14789
  response
14119
14790
  }
14120
14791
  };
@@ -14138,8 +14809,8 @@ function readErrorMessage2(payload) {
14138
14809
  if (typeof payload.error === "string" && payload.error.trim()) {
14139
14810
  return payload.error.trim();
14140
14811
  }
14141
- const error = toRecord10(payload.error);
14142
- return readString12(error, "message") ?? readString12(payload, "message");
14812
+ const error = toRecord12(payload.error);
14813
+ return readString14(error, "message") ?? readString14(payload, "message");
14143
14814
  }
14144
14815
  function readDelta(payload) {
14145
14816
  return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
@@ -14148,8 +14819,12 @@ function extractResponseAssistantText(value) {
14148
14819
  if (typeof value === "string") {
14149
14820
  return value.trim().length > 0 ? value : null;
14150
14821
  }
14151
- const payload = toRecord10(value);
14152
- const response = toRecord10(payload.response ?? value);
14822
+ const payload = toRecord12(value);
14823
+ const response = toRecord12(payload.response ?? value);
14824
+ const directOutput = readText2(response, "output");
14825
+ if (directOutput?.trim()) {
14826
+ return directOutput;
14827
+ }
14153
14828
  const directText = readText2(response, "output_text");
14154
14829
  if (directText?.trim()) {
14155
14830
  return directText;
@@ -14198,15 +14873,15 @@ function isTopLevelErrorEvent(event) {
14198
14873
  }
14199
14874
  function readChatCompletionDelta(payload) {
14200
14875
  const choice = readFirstChoice(payload);
14201
- const delta = toRecord10(choice.delta);
14876
+ const delta = toRecord12(choice.delta);
14202
14877
  return readText2(delta, "content");
14203
14878
  }
14204
14879
  function readChatCompletionFinishReason(payload) {
14205
14880
  const choice = readFirstChoice(payload);
14206
- return readString12(choice, "finish_reason") ?? readString12(choice, "finishReason");
14881
+ return readString14(choice, "finish_reason") ?? readString14(choice, "finishReason");
14207
14882
  }
14208
14883
  function readChatCompletionUsage(payload) {
14209
- const usage = toRecord10(payload.usage);
14884
+ const usage = toRecord12(payload.usage);
14210
14885
  const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
14211
14886
  const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
14212
14887
  const total = readInteger2(usage, "total_tokens");
@@ -14235,15 +14910,15 @@ function readFirstChoice(payload) {
14235
14910
  if (!Array.isArray(choices)) {
14236
14911
  return {};
14237
14912
  }
14238
- return toRecord10(choices[0]);
14913
+ return toRecord12(choices[0]);
14239
14914
  }
14240
14915
  function readAssistantTextFromChoices(payload) {
14241
14916
  const choices = payload.choices;
14242
14917
  if (!Array.isArray(choices)) {
14243
14918
  return null;
14244
14919
  }
14245
- const messages = choices.map(toRecord10).map((choice) => toRecord10(choice.message ?? choice.delta)).filter((message) => {
14246
- const role = readString12(message, "role");
14920
+ const messages = choices.map(toRecord12).map((choice) => toRecord12(choice.message ?? choice.delta)).filter((message) => {
14921
+ const role = readString14(message, "role");
14247
14922
  return !role || role === "assistant";
14248
14923
  }).map(readResponseMessageText).filter((text) => Boolean(text?.trim()));
14249
14924
  return messages.length > 0 ? messages.join("\n\n") : null;
@@ -14259,7 +14934,7 @@ function readInteger2(payload, key) {
14259
14934
  }
14260
14935
  return void 0;
14261
14936
  }
14262
- function readString12(payload, key) {
14937
+ function readString14(payload, key) {
14263
14938
  const value = payload[key];
14264
14939
  return typeof value === "string" && value.trim() ? value.trim() : null;
14265
14940
  }
@@ -14271,9 +14946,9 @@ function readResponseOutputItemText(value) {
14271
14946
  if (typeof value === "string") {
14272
14947
  return value;
14273
14948
  }
14274
- const item = toRecord10(value);
14275
- const type = readString12(item, "type");
14276
- const role = readString12(item, "role");
14949
+ const item = toRecord12(value);
14950
+ const type = readString14(item, "type");
14951
+ const role = readString14(item, "role");
14277
14952
  if (type && type !== "message" && type !== "output_text" && type !== "text") {
14278
14953
  return null;
14279
14954
  }
@@ -14291,15 +14966,15 @@ function readResponseContentText(value) {
14291
14966
  return value;
14292
14967
  }
14293
14968
  if (!Array.isArray(value)) {
14294
- const record = toRecord10(value);
14969
+ const record = toRecord12(value);
14295
14970
  return readText2(record, "text") ?? readText2(record, "content") ?? readText2(record, "output_text") ?? readText2(record, "refusal");
14296
14971
  }
14297
14972
  const chunks = value.map((partValue) => {
14298
14973
  if (typeof partValue === "string") {
14299
14974
  return partValue;
14300
14975
  }
14301
- const part = toRecord10(partValue);
14302
- const type = readString12(part, "type");
14976
+ const part = toRecord12(partValue);
14977
+ const type = readString14(part, "type");
14303
14978
  if (type && !isVisibleResponseTextPart(type)) {
14304
14979
  return null;
14305
14980
  }
@@ -14318,10 +14993,10 @@ function readResponseItemOutput(value) {
14318
14993
  if (!Array.isArray(value)) {
14319
14994
  return stringifyJsonValue(value);
14320
14995
  }
14321
- const text = value.map(toRecord10).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
14996
+ const text = value.map(toRecord12).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
14322
14997
  return text || stringifyJsonValue(value);
14323
14998
  }
14324
- function parseJsonValue2(value) {
14999
+ function parseJsonValue3(value) {
14325
15000
  if (typeof value !== "string" || !value.trim()) {
14326
15001
  return null;
14327
15002
  }
@@ -14344,11 +15019,14 @@ function stringifyJsonValue(value) {
14344
15019
  return String(value);
14345
15020
  }
14346
15021
  }
14347
- function toRecord10(value) {
15022
+ function toRecord12(value) {
14348
15023
  return typeof value === "object" && value !== null ? value : {};
14349
15024
  }
14350
15025
 
14351
15026
  // src/conversations/run-lifecycle.ts
15027
+ var RUN_STATUS_RECOVERY_TIMEOUT_MS = 10 * 60 * 1e3;
15028
+ var RUN_STATUS_RECOVERY_INITIAL_DELAY_MS = 500;
15029
+ var RUN_STATUS_RECOVERY_MAX_DELAY_MS = 2500;
14352
15030
  var ConversationRunLifecycle = class {
14353
15031
  constructor(deps) {
14354
15032
  this.deps = deps;
@@ -14363,6 +15041,7 @@ var ConversationRunLifecycle = class {
14363
15041
  const controller = new AbortController();
14364
15042
  this.deps.activeRunControllers.set(runId, { conversationId, controller });
14365
15043
  try {
15044
+ const backend = resolveConversationRunBackend();
14366
15045
  const hermesSessionId = await readHermesCompressionTip(
14367
15046
  run.hermes_session_id,
14368
15047
  this.deps.paths,
@@ -14407,7 +15086,7 @@ var ConversationRunLifecycle = class {
14407
15086
  fallbackInput: input,
14408
15087
  snapshot
14409
15088
  });
14410
- const previousResponseId = findPreviousHermesResponseId(snapshot, run);
15089
+ const previousResponseId = backend === "responses" ? findPreviousHermesResponseId(snapshot, run) : void 0;
14411
15090
  if (previousResponseId) {
14412
15091
  await this.updateRun(conversationId, runId, {
14413
15092
  previous_response_id: previousResponseId
@@ -14435,99 +15114,98 @@ var ConversationRunLifecycle = class {
14435
15114
  if (estimatedUsage) {
14436
15115
  await this.updateRun(conversationId, runId, { usage: estimatedUsage });
14437
15116
  }
14438
- const response = await streamHermesResponses(
14439
- {
14440
- input: resolvedInput,
14441
- instructions,
14442
- session_id: hermesSessionId,
14443
- session_key: await this.buildHermesSessionKey(
14444
- conversationId,
14445
- run.profile ?? "default"
14446
- ),
14447
- model: run.model,
14448
- ...previousResponseId ? { previous_response_id: previousResponseId } : {},
14449
- ...conversationHistory.messages.length > 0 ? { conversation_history: conversationHistory.messages } : {}
14450
- },
14451
- {
14452
- logger: this.deps.logger,
14453
- profileName: run.profile,
14454
- signal: controller.signal
14455
- }
15117
+ const sessionKey = await this.buildHermesSessionKey(
15118
+ conversationId,
15119
+ run.profile ?? "default"
14456
15120
  );
14457
- const responseSessionId = response.headers.get("x-hermes-session-id")?.trim();
14458
- if (responseSessionId) {
14459
- await this.rememberRunHermesSessionId(
14460
- conversationId,
14461
- runId,
14462
- responseSessionId
15121
+ if (backend === "responses") {
15122
+ const response = await streamHermesResponses(
15123
+ {
15124
+ input: resolvedInput,
15125
+ instructions,
15126
+ session_id: hermesSessionId,
15127
+ session_key: sessionKey,
15128
+ model: run.model,
15129
+ ...previousResponseId ? { previous_response_id: previousResponseId } : {},
15130
+ ...conversationHistory.messages.length > 0 ? { conversation_history: conversationHistory.messages } : {}
15131
+ },
15132
+ {
15133
+ logger: this.deps.logger,
15134
+ profileName: run.profile,
15135
+ signal: controller.signal
15136
+ }
14463
15137
  );
14464
- }
14465
- for await (const rawEvent of parseSseResponse(response)) {
14466
- if (controller.signal.aborted) {
14467
- await this.deps.withConversationLock(
15138
+ const responseSessionId = response.headers.get("x-hermes-session-id")?.trim();
15139
+ if (responseSessionId) {
15140
+ await this.rememberRunHermesSessionId(
14468
15141
  conversationId,
14469
- () => this.cancelRunLocked(conversationId, runId, {
14470
- abortUpstream: false,
14471
- reason: "cancelled by app"
14472
- })
15142
+ runId,
15143
+ responseSessionId
14473
15144
  );
14474
- return;
14475
- }
14476
- if (!await this.deps.isConversationRunnable(conversationId)) {
14477
- return;
14478
15145
  }
14479
- const event = normalizeHermesStreamEvent(rawEvent);
14480
- if (!event) {
14481
- continue;
14482
- }
14483
- if (event.payloadType === "run.completed") {
14484
- if (cronJobIdsBeforeRun) {
14485
- await this.bindNewCronJobsCreatedByRun({
14486
- profileName: run.profile,
14487
- conversationId,
14488
- beforeJobIds: cronJobIdsBeforeRun
14489
- });
15146
+ await this.consumeHermesEventStream({
15147
+ backend,
15148
+ conversationId,
15149
+ runId,
15150
+ response,
15151
+ controller,
15152
+ profileName: run.profile,
15153
+ cronJobIdsBeforeRun
15154
+ });
15155
+ } else {
15156
+ const hermesRun = await createHermesRun(
15157
+ {
15158
+ input: resolvedInput,
15159
+ instructions,
15160
+ session_id: hermesSessionId,
15161
+ session_key: sessionKey,
15162
+ model: run.model,
15163
+ ...conversationHistory.messages.length > 0 ? { conversation_history: conversationHistory.messages } : {}
15164
+ },
15165
+ {
15166
+ logger: this.deps.logger,
15167
+ profileName: run.profile,
15168
+ signal: controller.signal
14490
15169
  }
14491
- await this.deps.syncCronDeliveries().catch((error) => {
14492
- void this.deps.logger.warn("cron_link_delivery_sync_failed", {
14493
- conversation_id: conversationId,
14494
- run_id: runId,
14495
- error: error instanceof Error ? error.message : String(error)
14496
- });
14497
- });
14498
- await this.appendAssistantTextFromCompletedResponse(
14499
- conversationId,
14500
- runId,
14501
- event
14502
- );
14503
- await this.importMediaReferencesForEvent(conversationId, runId, event);
14504
- if (!await this.runHasAssistantOutput(conversationId, runId)) {
14505
- await this.failRun(
14506
- conversationId,
14507
- runId,
14508
- await this.buildEmptyHermesResponseMessage({
14509
- source: "final-empty-response"
14510
- }),
14511
- event
14512
- );
14513
- return;
15170
+ );
15171
+ await this.updateRun(conversationId, runId, {
15172
+ hermes_run_id: hermesRun.run_id
15173
+ });
15174
+ const response = await streamHermesRunEvents(hermesRun.run_id, {
15175
+ logger: this.deps.logger,
15176
+ profileName: run.profile,
15177
+ signal: controller.signal
15178
+ }).catch(async (error) => {
15179
+ if (controller.signal.aborted) {
15180
+ throw error;
14514
15181
  }
14515
- await this.completeRun(conversationId, runId, event);
14516
- this.deps.scheduleTitleRefresh(conversationId);
14517
- return;
14518
- }
14519
- if (event.payloadType === "run.failed") {
14520
- await this.importMediaReferencesForEvent(conversationId, runId, event);
14521
- await this.failRun(
15182
+ await this.recoverRunAfterEventStreamOpenFailure({
15183
+ backend,
14522
15184
  conversationId,
14523
15185
  runId,
14524
- readErrorMessage2(event.payload) ?? "Hermes run failed",
14525
- event
14526
- );
15186
+ hermesRunId: hermesRun.run_id,
15187
+ controller,
15188
+ profileName: run.profile,
15189
+ cronJobIdsBeforeRun,
15190
+ error
15191
+ });
15192
+ return null;
15193
+ });
15194
+ if (!response) {
14527
15195
  return;
14528
15196
  }
14529
- await this.persistHermesEvent(conversationId, runId, event);
15197
+ await this.consumeHermesEventStream({
15198
+ backend,
15199
+ conversationId,
15200
+ runId,
15201
+ response,
15202
+ controller,
15203
+ profileName: run.profile,
15204
+ hermesRunId: hermesRun.run_id,
15205
+ cronJobIdsBeforeRun
15206
+ });
14530
15207
  }
15208
+ } catch (error) {
14531
15209
  if (controller.signal.aborted) {
14532
15210
  await this.deps.withConversationLock(
14533
15211
  conversationId,
@@ -14538,60 +15216,381 @@ var ConversationRunLifecycle = class {
14538
15216
  );
14539
15217
  return;
14540
15218
  }
14541
- if (await this.runHasAssistantOutput(conversationId, runId)) {
14542
- await this.completeRun(conversationId, runId);
14543
- } else {
15219
+ throw error;
15220
+ } finally {
15221
+ if (this.deps.activeRunControllers.get(runId)?.controller === controller) {
15222
+ this.deps.activeRunControllers.delete(runId);
15223
+ }
15224
+ }
15225
+ }
15226
+ async cancelRun(conversationId, runId, options) {
15227
+ return this.deps.withConversationLock(
15228
+ conversationId,
15229
+ () => this.cancelRunLocked(conversationId, runId, options)
15230
+ );
15231
+ }
15232
+ async failRun(conversationId, runId, message, source) {
15233
+ return this.deps.withConversationLock(
15234
+ conversationId,
15235
+ () => this.failRunLocked(conversationId, runId, message, source)
15236
+ );
15237
+ }
15238
+ async findConversationIdForRun(runId) {
15239
+ const entries = await readdirWithDirs(this.deps.paths.conversationsDir);
15240
+ for (const entry of entries) {
15241
+ if (!entry.isDirectory()) {
15242
+ continue;
15243
+ }
15244
+ const snapshot = await this.deps.readSnapshot(entry.name).catch(() => null);
15245
+ if (snapshot?.runs.some((run) => run.id === runId)) {
15246
+ return entry.name;
15247
+ }
15248
+ }
15249
+ return void 0;
15250
+ }
15251
+ async consumeHermesEventStream(input) {
15252
+ let streamError;
15253
+ const iterator = parseSseResponse(input.response)[Symbol.asyncIterator]();
15254
+ const toolEventIds = input.backend === "runs" ? new RunToolEventIdCoalescer({
15255
+ runId: input.runId,
15256
+ hermesRunId: input.hermesRunId
15257
+ }) : null;
15258
+ while (true) {
15259
+ let next;
15260
+ try {
15261
+ next = await iterator.next();
15262
+ } catch (error) {
15263
+ if (input.controller.signal.aborted) {
15264
+ await this.cancelRunAfterAbort(input.conversationId, input.runId);
15265
+ return;
15266
+ }
15267
+ streamError = error;
15268
+ await this.deps.logger.warn("hermes_event_stream_interrupted", {
15269
+ backend: input.backend,
15270
+ conversation_id: input.conversationId,
15271
+ run_id: input.runId,
15272
+ ...input.hermesRunId ? { hermes_run_id: input.hermesRunId } : {},
15273
+ error: error instanceof Error ? error.message : String(error)
15274
+ });
15275
+ break;
15276
+ }
15277
+ if (next.done) {
15278
+ break;
15279
+ }
15280
+ if (input.controller.signal.aborted) {
15281
+ await closeSseIterator(iterator);
15282
+ await this.cancelRunAfterAbort(input.conversationId, input.runId);
15283
+ return;
15284
+ }
15285
+ if (!await this.deps.isConversationRunnable(input.conversationId)) {
15286
+ await closeSseIterator(iterator);
15287
+ return;
15288
+ }
15289
+ const event = normalizeHermesStreamEvent(next.value);
15290
+ if (!event) {
15291
+ continue;
15292
+ }
15293
+ const normalizedEvent = toolEventIds?.normalize(event) ?? event;
15294
+ const handled = await this.handleNormalizedHermesEvent({
15295
+ ...input,
15296
+ event: normalizedEvent
15297
+ });
15298
+ if (handled) {
15299
+ await closeSseIterator(iterator);
15300
+ return;
15301
+ }
15302
+ }
15303
+ if (input.controller.signal.aborted) {
15304
+ await this.cancelRunAfterAbort(input.conversationId, input.runId);
15305
+ return;
15306
+ }
15307
+ if (!await this.deps.isConversationRunnable(input.conversationId)) {
15308
+ return;
15309
+ }
15310
+ const recoveredEvent = input.backend === "runs" && input.hermesRunId ? await this.recoverRunTerminalEvent({
15311
+ conversationId: input.conversationId,
15312
+ runId: input.runId,
15313
+ hermesRunId: input.hermesRunId,
15314
+ profileName: input.profileName,
15315
+ signal: input.controller.signal
15316
+ }) : null;
15317
+ if (recoveredEvent) {
15318
+ const handled = await this.handleNormalizedHermesEvent({
15319
+ ...input,
15320
+ event: recoveredEvent
15321
+ });
15322
+ if (handled) {
15323
+ return;
15324
+ }
15325
+ }
15326
+ if (input.controller.signal.aborted) {
15327
+ await this.cancelRunAfterAbort(input.conversationId, input.runId);
15328
+ return;
15329
+ }
15330
+ if (input.backend === "responses" && !streamError && await this.runHasAssistantOutput(input.conversationId, input.runId)) {
15331
+ await this.completeRun(input.conversationId, input.runId);
15332
+ } else {
15333
+ await this.failRun(
15334
+ input.conversationId,
15335
+ input.runId,
15336
+ await this.buildEmptyHermesResponseMessage({
15337
+ source: streamError ? "stream-error" : "stream-ended-without-terminal-event",
15338
+ ...streamError ? { eventError: formatUnknownErrorMessage(streamError) } : {}
15339
+ })
15340
+ );
15341
+ }
15342
+ this.deps.scheduleTitleRefresh(input.conversationId);
15343
+ }
15344
+ async handleNormalizedHermesEvent(input) {
15345
+ if (input.event.payloadType === "run.completed") {
15346
+ if (input.cronJobIdsBeforeRun) {
15347
+ await this.bindNewCronJobsCreatedByRun({
15348
+ profileName: input.profileName,
15349
+ conversationId: input.conversationId,
15350
+ beforeJobIds: input.cronJobIdsBeforeRun
15351
+ });
15352
+ }
15353
+ await this.deps.syncCronDeliveries().catch((error) => {
15354
+ void this.deps.logger.warn("cron_link_delivery_sync_failed", {
15355
+ conversation_id: input.conversationId,
15356
+ run_id: input.runId,
15357
+ error: error instanceof Error ? error.message : String(error)
15358
+ });
15359
+ });
15360
+ if (input.backend === "runs" && isRunToolResultCompensationEnabled()) {
15361
+ await this.enrichRunFromHermesTranscript({
15362
+ conversationId: input.conversationId,
15363
+ runId: input.runId,
15364
+ profileName: input.profileName
15365
+ });
15366
+ }
15367
+ await this.appendAssistantTextFromCompletedResponse(
15368
+ input.conversationId,
15369
+ input.runId,
15370
+ input.event
15371
+ );
15372
+ await this.importMediaReferencesForEvent(
15373
+ input.conversationId,
15374
+ input.runId,
15375
+ input.event
15376
+ );
15377
+ if (!await this.runHasAssistantOutput(input.conversationId, input.runId)) {
14544
15378
  await this.failRun(
14545
- conversationId,
14546
- runId,
15379
+ input.conversationId,
15380
+ input.runId,
14547
15381
  await this.buildEmptyHermesResponseMessage({
14548
- source: "stream-ended-without-terminal-event"
14549
- })
15382
+ source: "final-empty-response"
15383
+ }),
15384
+ input.event
14550
15385
  );
15386
+ return true;
14551
15387
  }
14552
- this.deps.scheduleTitleRefresh(conversationId);
14553
- } catch (error) {
14554
- if (controller.signal.aborted) {
14555
- await this.deps.withConversationLock(
14556
- conversationId,
14557
- () => this.cancelRunLocked(conversationId, runId, {
14558
- abortUpstream: false,
14559
- reason: "cancelled by app"
14560
- })
14561
- );
14562
- return;
15388
+ await this.completeRun(input.conversationId, input.runId, input.event);
15389
+ this.deps.scheduleTitleRefresh(input.conversationId);
15390
+ return true;
15391
+ }
15392
+ if (input.event.payloadType === "run.failed") {
15393
+ if (input.backend === "runs" && isRunToolResultCompensationEnabled()) {
15394
+ await this.enrichRunFromHermesTranscript({
15395
+ conversationId: input.conversationId,
15396
+ runId: input.runId,
15397
+ profileName: input.profileName
15398
+ });
14563
15399
  }
14564
- throw error;
14565
- } finally {
14566
- if (this.deps.activeRunControllers.get(runId)?.controller === controller) {
14567
- this.deps.activeRunControllers.delete(runId);
15400
+ await this.importMediaReferencesForEvent(
15401
+ input.conversationId,
15402
+ input.runId,
15403
+ input.event
15404
+ );
15405
+ await this.failRun(
15406
+ input.conversationId,
15407
+ input.runId,
15408
+ readErrorMessage2(input.event.payload) ?? "Hermes run failed",
15409
+ input.event
15410
+ );
15411
+ return true;
15412
+ }
15413
+ if (input.event.payloadType === "run.cancelled") {
15414
+ await this.completeCancelledRun(input.conversationId, input.runId);
15415
+ return true;
15416
+ }
15417
+ await this.persistHermesEvent(input.conversationId, input.runId, input.event);
15418
+ return false;
15419
+ }
15420
+ async recoverRunAfterEventStreamOpenFailure(input) {
15421
+ await this.deps.logger.warn("hermes_event_stream_open_failed", {
15422
+ backend: input.backend,
15423
+ conversation_id: input.conversationId,
15424
+ run_id: input.runId,
15425
+ hermes_run_id: input.hermesRunId,
15426
+ error: input.error instanceof Error ? input.error.message : String(input.error)
15427
+ });
15428
+ const recoveredEvent = await this.recoverRunTerminalEvent({
15429
+ conversationId: input.conversationId,
15430
+ runId: input.runId,
15431
+ hermesRunId: input.hermesRunId,
15432
+ profileName: input.profileName,
15433
+ signal: input.controller.signal
15434
+ });
15435
+ if (recoveredEvent) {
15436
+ const handled = await this.handleNormalizedHermesEvent({
15437
+ backend: input.backend,
15438
+ conversationId: input.conversationId,
15439
+ runId: input.runId,
15440
+ event: recoveredEvent,
15441
+ profileName: input.profileName,
15442
+ cronJobIdsBeforeRun: input.cronJobIdsBeforeRun
15443
+ });
15444
+ if (handled) {
15445
+ return;
14568
15446
  }
14569
15447
  }
15448
+ if (input.controller.signal.aborted) {
15449
+ await this.cancelRunAfterAbort(input.conversationId, input.runId);
15450
+ return;
15451
+ }
15452
+ await this.failRun(
15453
+ input.conversationId,
15454
+ input.runId,
15455
+ await this.buildEmptyHermesResponseMessage({
15456
+ source: "stream-error",
15457
+ eventError: formatUnknownErrorMessage(input.error)
15458
+ })
15459
+ );
14570
15460
  }
14571
- async cancelRun(conversationId, runId, options) {
14572
- return this.deps.withConversationLock(
15461
+ async cancelRunAfterAbort(conversationId, runId) {
15462
+ await this.deps.withConversationLock(
14573
15463
  conversationId,
14574
- () => this.cancelRunLocked(conversationId, runId, options)
15464
+ () => this.cancelRunLocked(conversationId, runId, {
15465
+ abortUpstream: false,
15466
+ reason: "cancelled by app"
15467
+ })
14575
15468
  );
14576
15469
  }
14577
- async failRun(conversationId, runId, message, source) {
14578
- return this.deps.withConversationLock(
15470
+ async completeCancelledRun(conversationId, runId) {
15471
+ await this.deps.withConversationLock(
14579
15472
  conversationId,
14580
- () => this.failRunLocked(conversationId, runId, message, source)
15473
+ () => this.cancelRunLocked(conversationId, runId, {
15474
+ abortUpstream: false,
15475
+ reason: "cancelled by Hermes"
15476
+ })
14581
15477
  );
14582
15478
  }
14583
- async findConversationIdForRun(runId) {
14584
- const entries = await readdirWithDirs(this.deps.paths.conversationsDir);
14585
- for (const entry of entries) {
14586
- if (!entry.isDirectory()) {
15479
+ async recoverRunTerminalEvent(input) {
15480
+ const deadline = Date.now() + RUN_STATUS_RECOVERY_TIMEOUT_MS;
15481
+ let delayMs = RUN_STATUS_RECOVERY_INITIAL_DELAY_MS;
15482
+ while (!input.signal.aborted) {
15483
+ let status;
15484
+ try {
15485
+ status = await readHermesRunStatus(input.hermesRunId, {
15486
+ logger: this.deps.logger,
15487
+ profileName: input.profileName,
15488
+ signal: input.signal
15489
+ });
15490
+ } catch (error) {
15491
+ if (input.signal.aborted) {
15492
+ return null;
15493
+ }
15494
+ await this.deps.logger.warn("hermes_run_status_recovery_failed", {
15495
+ conversation_id: input.conversationId,
15496
+ run_id: input.runId,
15497
+ hermes_run_id: input.hermesRunId,
15498
+ error: error instanceof Error ? error.message : String(error)
15499
+ });
15500
+ if (Date.now() >= deadline) {
15501
+ return null;
15502
+ }
15503
+ await sleep(Math.min(delayMs, deadline - Date.now()), input.signal);
15504
+ delayMs = Math.min(RUN_STATUS_RECOVERY_MAX_DELAY_MS, delayMs + 250);
14587
15505
  continue;
14588
15506
  }
14589
- const snapshot = await this.deps.readSnapshot(entry.name).catch(() => null);
14590
- if (snapshot?.runs.some((run) => run.id === runId)) {
14591
- return entry.name;
15507
+ if (!status) {
15508
+ return null;
15509
+ }
15510
+ if (status.session_id) {
15511
+ await this.rememberRunHermesSessionId(
15512
+ input.conversationId,
15513
+ input.runId,
15514
+ status.session_id
15515
+ );
15516
+ }
15517
+ const normalizedStatus = status.status.trim().toLowerCase();
15518
+ if (isCompletedRunStatus(normalizedStatus)) {
15519
+ return {
15520
+ eventName: "run.completed",
15521
+ payloadType: "run.completed",
15522
+ payload: {
15523
+ type: "run.completed",
15524
+ run_id: status.run_id,
15525
+ output: status.output,
15526
+ usage: status.usage,
15527
+ status: status.status
15528
+ },
15529
+ rawPayload: status.raw
15530
+ };
15531
+ }
15532
+ if (isFailedRunStatus(normalizedStatus)) {
15533
+ return {
15534
+ eventName: "run.failed",
15535
+ payloadType: "run.failed",
15536
+ payload: {
15537
+ type: "run.failed",
15538
+ run_id: status.run_id,
15539
+ error: {
15540
+ message: readStatusErrorMessage(status.error) ?? "Hermes run failed"
15541
+ },
15542
+ status: status.status,
15543
+ usage: status.usage
15544
+ },
15545
+ rawPayload: status.raw
15546
+ };
15547
+ }
15548
+ if (isCancelledRunStatus(normalizedStatus)) {
15549
+ return {
15550
+ eventName: "run.cancelled",
15551
+ payloadType: "run.cancelled",
15552
+ payload: {
15553
+ type: "run.cancelled",
15554
+ run_id: status.run_id,
15555
+ status: status.status
15556
+ },
15557
+ rawPayload: status.raw
15558
+ };
15559
+ }
15560
+ if (Date.now() >= deadline) {
15561
+ return null;
14592
15562
  }
15563
+ await sleep(Math.min(delayMs, deadline - Date.now()), input.signal);
15564
+ delayMs = Math.min(RUN_STATUS_RECOVERY_MAX_DELAY_MS, delayMs + 250);
15565
+ }
15566
+ return null;
15567
+ }
15568
+ async enrichRunFromHermesTranscript(input) {
15569
+ const snapshot = await this.deps.readSnapshot(input.conversationId).catch(() => null);
15570
+ const run = snapshot?.runs.find((item) => item.id === input.runId);
15571
+ const assistant = snapshot?.messages.find(
15572
+ (message) => message.id === run?.assistant_message_id
15573
+ );
15574
+ if (!snapshot || !run) {
15575
+ return;
15576
+ }
15577
+ const events = await buildRunTranscriptEvents({
15578
+ profileName: input.profileName,
15579
+ hermesSessionId: run.hermes_session_id,
15580
+ runStartedAt: run.started_at,
15581
+ assistant
15582
+ }).catch(async (error) => {
15583
+ await this.deps.logger.warn("hermes_run_transcript_enrichment_failed", {
15584
+ conversation_id: input.conversationId,
15585
+ run_id: input.runId,
15586
+ hermes_session_id: run.hermes_session_id,
15587
+ error: error instanceof Error ? error.message : String(error)
15588
+ });
15589
+ return [];
15590
+ });
15591
+ for (const event of events) {
15592
+ await this.persistHermesEvent(input.conversationId, input.runId, event);
14593
15593
  }
14594
- return void 0;
14595
15594
  }
14596
15595
  async resolveRunInput(input) {
14597
15596
  const userMessage = input.snapshot.messages.find(
@@ -14738,6 +15737,12 @@ ${attachmentLines.join("\n")}`
14738
15737
  "\u8FD9\u66F4\u50CF\u662F Gateway \u6216 provider \u7684\u6D41\u5F0F\u4E8B\u4EF6\u4E2D\u65AD\uFF0C\u4E0D\u662F\u6A21\u578B\u660E\u786E\u5B8C\u6210\u4E86\u7A7A\u56DE\u590D\u3002"
14739
15738
  );
14740
15739
  }
15740
+ if (input?.source === "stream-error") {
15741
+ details.unshift("Hermes \u7684\u8FD0\u884C\u4E8B\u4EF6\u6D41\u8BFB\u53D6\u8FC7\u7A0B\u4E2D\u65AD\u3002");
15742
+ details.push(
15743
+ "Link \u5DF2\u5C1D\u8BD5\u67E5\u8BE2 Hermes run \u7EC8\u6001\uFF0C\u4F46\u4ECD\u6CA1\u6709\u6062\u590D\u5230\u5B8C\u6210\u6216\u5931\u8D25\u4E8B\u4EF6\u3002"
15744
+ );
15745
+ }
14741
15746
  return details.length > 0 ? `Hermes \u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u5185\u5BB9\u3002
14742
15747
  ${details.join("\n")}` : emptyHermesResponseMessage();
14743
15748
  }
@@ -14882,8 +15887,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
14882
15887
  }
14883
15888
  const textPart = assistant.parts.find((part) => part.type === "text");
14884
15889
  const currentText = textPart?.text ?? "";
14885
- const pendingDeliveryText = readString13(
14886
- toRecord11(assistant.hermes),
15890
+ const pendingDeliveryText = readString15(
15891
+ toRecord13(assistant.hermes),
14887
15892
  "pending_media_delivery_text"
14888
15893
  );
14889
15894
  const normalizedDelta = normalizeStreamingTextDelta(
@@ -14898,7 +15903,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
14898
15903
  pendingDeliveryText ?? ""
14899
15904
  );
14900
15905
  const nextHermes = {
14901
- ...toRecord11(assistant.hermes),
15906
+ ...toRecord13(assistant.hermes),
14902
15907
  ...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
14903
15908
  };
14904
15909
  if (!extracted.pendingText) {
@@ -14977,11 +15982,15 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
14977
15982
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
14978
15983
  const usage = readUsage(source?.payload);
14979
15984
  const responseId = readResponseId(source?.payload);
15985
+ const hermesRunId = readRunId(source?.payload);
14980
15986
  run.status = "completed";
14981
15987
  run.completed_at = completedAt;
14982
15988
  if (responseId) {
14983
15989
  run.hermes_response_id = responseId;
14984
15990
  }
15991
+ if (hermesRunId) {
15992
+ run.hermes_run_id = hermesRunId;
15993
+ }
14985
15994
  if (usage) {
14986
15995
  run.usage = mergeRunUsage(run.usage, usage);
14987
15996
  }
@@ -15155,7 +16164,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
15155
16164
  if (raw.length <= 200) {
15156
16165
  return raw;
15157
16166
  }
15158
- return `hermespilot:${createHash4("sha256").update(raw).digest("hex")}`;
16167
+ return `hermespilot:${createHash5("sha256").update(raw).digest("hex")}`;
15159
16168
  }
15160
16169
  async assistantMessageIdForRun(conversationId, runId) {
15161
16170
  const snapshot = await this.deps.readSnapshot(conversationId).catch(() => null);
@@ -15176,7 +16185,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
15176
16185
  includeDisabled: true
15177
16186
  });
15178
16187
  return new Set(
15179
- jobs.map((job) => readString13(job, "id") ?? readString13(job, "job_id")).filter((id) => Boolean(id))
16188
+ jobs.map((job) => readString15(job, "id") ?? readString15(job, "job_id")).filter((id) => Boolean(id))
15180
16189
  );
15181
16190
  }
15182
16191
  async bindNewCronJobsCreatedByRun(input) {
@@ -15212,7 +16221,7 @@ function buildRunInstructions(run, deliveryStagingDir) {
15212
16221
  ].join("\n");
15213
16222
  }
15214
16223
  function appendMediaImportFailureNotice(message) {
15215
- const hermes = toRecord11(message.hermes);
16224
+ const hermes = toRecord13(message.hermes);
15216
16225
  if (hermes.media_import_failure_notice_appended === true) {
15217
16226
  return;
15218
16227
  }
@@ -15256,17 +16265,17 @@ function formatFilenameList(filenames) {
15256
16265
  }
15257
16266
  async function readdirWithDirs(directory) {
15258
16267
  return readdir8(directory, { withFileTypes: true }).catch((error) => {
15259
- if (isNodeError13(error, "ENOENT")) {
16268
+ if (isNodeError14(error, "ENOENT")) {
15260
16269
  return [];
15261
16270
  }
15262
16271
  throw error;
15263
16272
  });
15264
16273
  }
15265
- function readString13(payload, key) {
16274
+ function readString15(payload, key) {
15266
16275
  const value = payload[key];
15267
16276
  return typeof value === "string" && value.trim() ? value.trim() : null;
15268
16277
  }
15269
- function toRecord11(value) {
16278
+ function toRecord13(value) {
15270
16279
  return typeof value === "object" && value !== null ? value : {};
15271
16280
  }
15272
16281
  function formatFailureMessage(message, detail) {
@@ -15282,17 +16291,17 @@ function isFileSearchCompletion(payloadType, payload) {
15282
16291
  if (payloadType !== "tool.completed") {
15283
16292
  return false;
15284
16293
  }
15285
- const tool = toRecord11(payload.tool);
15286
- const toolCall = toRecord11(payload.tool_call ?? payload.toolCall);
15287
- const fn = toRecord11(toolCall.function ?? payload.function);
16294
+ const tool = toRecord13(payload.tool);
16295
+ const toolCall = toRecord13(payload.tool_call ?? payload.toolCall);
16296
+ const fn = toRecord13(toolCall.function ?? payload.function);
15288
16297
  const candidates = [
15289
- readString13(payload, "tool_name"),
15290
- readString13(payload, "toolName"),
15291
- readString13(payload, "name"),
15292
- readString13(payload, "tool"),
15293
- readString13(tool, "name"),
15294
- readString13(toolCall, "name"),
15295
- readString13(fn, "name")
16298
+ readString15(payload, "tool_name"),
16299
+ readString15(payload, "toolName"),
16300
+ readString15(payload, "name"),
16301
+ readString15(payload, "tool"),
16302
+ readString15(tool, "name"),
16303
+ readString15(toolCall, "name"),
16304
+ readString15(fn, "name")
15296
16305
  ].filter((value) => Boolean(value)).map(normalizeToolName);
15297
16306
  return candidates.some(
15298
16307
  (name) => [
@@ -15412,22 +16421,15 @@ function appendAgentEventBlock2(message, event, updatedAt) {
15412
16421
  }
15413
16422
  function contextUsagePayload(run) {
15414
16423
  const usage = run.usage;
15415
- if (!usage) {
16424
+ const runtimeContext = resolveRuntimeContextUsage({
16425
+ usage,
16426
+ contextWindow: run.context_window
16427
+ });
16428
+ const contextTokens = runtimeContext.contextTokens;
16429
+ const contextWindow = runtimeContext.contextWindow;
16430
+ if (!usage || contextTokens === void 0) {
15416
16431
  return null;
15417
16432
  }
15418
- const contextTokens = usage?.context_tokens;
15419
- const contextWindow = usage?.context_window ?? run.context_window;
15420
- if (contextTokens === void 0) {
15421
- return {
15422
- input_tokens: 0,
15423
- output_tokens: usage.output_tokens ?? 0,
15424
- total_tokens: usage.total_tokens ?? 0,
15425
- ...contextWindow ? { context_window: contextWindow } : {},
15426
- ...contextWindow ? { window_tokens: contextWindow } : {},
15427
- source: "unknown"
15428
- };
15429
- }
15430
- const contextSource = usage.context_source ?? "explicit";
15431
16433
  return {
15432
16434
  input_tokens: contextTokens,
15433
16435
  output_tokens: usage?.output_tokens ?? 0,
@@ -15435,13 +16437,8 @@ function contextUsagePayload(run) {
15435
16437
  ...contextWindow ? { context_window: contextWindow } : {},
15436
16438
  used_tokens: contextTokens,
15437
16439
  ...contextWindow ? { window_tokens: contextWindow } : {},
15438
- source: contextSource,
15439
- ...usage?.usage_percent !== void 0 ? { usage_percent: usage.usage_percent } : contextWindow ? {
15440
- usage_percent: Math.min(
15441
- 100,
15442
- Math.round(contextTokens / contextWindow * 100)
15443
- )
15444
- } : {}
16440
+ source: runtimeContext.source,
16441
+ ...runtimeContext.usagePercent !== void 0 ? { usage_percent: runtimeContext.usagePercent } : {}
15445
16442
  };
15446
16443
  }
15447
16444
  function mergeRunUsage(previous, next) {
@@ -15506,10 +16503,57 @@ function readResponseId(payload) {
15506
16503
  if (!payload) {
15507
16504
  return null;
15508
16505
  }
15509
- const response = toRecord11(payload.response);
15510
- return readString13(payload, "response_id") ?? readString13(response, "id");
16506
+ const response = toRecord13(payload.response);
16507
+ return readString15(payload, "response_id") ?? readString15(response, "id");
15511
16508
  }
15512
- function isNodeError13(error, code) {
16509
+ function readRunId(payload) {
16510
+ if (!payload) {
16511
+ return null;
16512
+ }
16513
+ return readString15(payload, "run_id") ?? readString15(payload, "runId");
16514
+ }
16515
+ function isCompletedRunStatus(status) {
16516
+ return status === "completed" || status === "complete" || status === "succeeded" || status === "success" || status === "done";
16517
+ }
16518
+ function isFailedRunStatus(status) {
16519
+ return status === "failed" || status === "failure" || status === "error";
16520
+ }
16521
+ function isCancelledRunStatus(status) {
16522
+ return status === "cancelled" || status === "canceled" || status === "stopped" || status === "aborted";
16523
+ }
16524
+ function readStatusErrorMessage(value) {
16525
+ if (typeof value === "string" && value.trim()) {
16526
+ return value.trim();
16527
+ }
16528
+ const record = toRecord13(value);
16529
+ return readString15(record, "message") ?? readString15(record, "error");
16530
+ }
16531
+ function formatUnknownErrorMessage(error) {
16532
+ return error instanceof Error ? error.message : String(error);
16533
+ }
16534
+ async function closeSseIterator(iterator) {
16535
+ if (!iterator.return) {
16536
+ return;
16537
+ }
16538
+ await iterator.return().catch(() => void 0);
16539
+ }
16540
+ async function sleep(ms, signal) {
16541
+ if (ms <= 0 || signal.aborted) {
16542
+ return;
16543
+ }
16544
+ await new Promise((resolve) => {
16545
+ const timeout = setTimeout(resolve, ms);
16546
+ signal.addEventListener(
16547
+ "abort",
16548
+ () => {
16549
+ clearTimeout(timeout);
16550
+ resolve();
16551
+ },
16552
+ { once: true }
16553
+ );
16554
+ });
16555
+ }
16556
+ function isNodeError14(error, code) {
15513
16557
  if (typeof error !== "object" || error === null || !("code" in error)) {
15514
16558
  return false;
15515
16559
  }
@@ -16433,7 +17477,7 @@ function findApproval(snapshot, approvalId) {
16433
17477
  }
16434
17478
 
16435
17479
  // src/security/devices.ts
16436
- import { randomBytes as randomBytes2, randomUUID as randomUUID11, timingSafeEqual, createHash as createHash5 } from "crypto";
17480
+ import { randomBytes as randomBytes2, randomUUID as randomUUID11, timingSafeEqual, createHash as createHash6 } from "crypto";
16437
17481
  var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
16438
17482
  var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
16439
17483
  var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
@@ -16729,7 +17773,7 @@ function randomToken(prefix) {
16729
17773
  return `${prefix}${randomBytes2(24).toString("base64url")}`;
16730
17774
  }
16731
17775
  function sha256(value) {
16732
- return createHash5("sha256").update(value).digest("hex");
17776
+ return createHash6("sha256").update(value).digest("hex");
16733
17777
  }
16734
17778
  function safeEqual(left, right) {
16735
17779
  const leftBytes = Buffer.from(left);
@@ -16900,12 +17944,12 @@ async function readRawBody(request, maxBytes) {
16900
17944
  }
16901
17945
  return Buffer.concat(chunks);
16902
17946
  }
16903
- function readString14(body, key) {
17947
+ function readString16(body, key) {
16904
17948
  const value = body[key];
16905
17949
  return typeof value === "string" && value.trim() ? value.trim() : null;
16906
17950
  }
16907
17951
  function readOptionalProfileName(body) {
16908
- return readString14(body, "profile") ?? readString14(body, "profile_name") ?? readString14(body, "profileName") ?? void 0;
17952
+ return readString16(body, "profile") ?? readString16(body, "profile_name") ?? readString16(body, "profileName") ?? void 0;
16909
17953
  }
16910
17954
  function readStringArray(body, ...keys) {
16911
17955
  for (const key of keys) {
@@ -17273,7 +18317,7 @@ function registerConversationRoutes(router, options) {
17273
18317
  ctx.body = {
17274
18318
  ok: true,
17275
18319
  conversation: await conversations.createConversation({
17276
- title: readString14(body, "title") ?? void 0,
18320
+ title: readString16(body, "title") ?? void 0,
17277
18321
  profileName: readOptionalProfileName(body)
17278
18322
  })
17279
18323
  };
@@ -17345,7 +18389,7 @@ function registerConversationRoutes(router, options) {
17345
18389
  router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
17346
18390
  await authenticateRequest(ctx, paths);
17347
18391
  const body = await readJsonBody(ctx.req);
17348
- const content = readString14(body, "content") ?? readString14(body, "text") ?? readString14(body, "input") ?? "";
18392
+ const content = readString16(body, "content") ?? readString16(body, "text") ?? readString16(body, "input") ?? "";
17349
18393
  const attachments = readMessageAttachments(body.attachments ?? body.blobs);
17350
18394
  if (!content && attachments.length === 0) {
17351
18395
  throw new LinkHttpError(
@@ -17361,7 +18405,7 @@ function registerConversationRoutes(router, options) {
17361
18405
  conversationId: ctx.params.conversationId,
17362
18406
  content,
17363
18407
  attachments,
17364
- clientMessageId: readString14(body, "client_message_id") ?? readString14(body, "clientMessageId") ?? void 0,
18408
+ clientMessageId: readString16(body, "client_message_id") ?? readString16(body, "clientMessageId") ?? void 0,
17365
18409
  idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
17366
18410
  profileName: readOptionalProfileName(body)
17367
18411
  })
@@ -17370,7 +18414,7 @@ function registerConversationRoutes(router, options) {
17370
18414
  router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
17371
18415
  await authenticateRequest(ctx, paths);
17372
18416
  const body = await readJsonBody(ctx.req);
17373
- const modelId = readString14(body, "model_id") ?? readString14(body, "modelId") ?? readString14(body, "model");
18417
+ const modelId = readString16(body, "model_id") ?? readString16(body, "modelId") ?? readString16(body, "model");
17374
18418
  if (!modelId) {
17375
18419
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
17376
18420
  }
@@ -17400,7 +18444,7 @@ function registerConversationRoutes(router, options) {
17400
18444
  router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
17401
18445
  await authenticateRequest(ctx, paths);
17402
18446
  const body = await readJsonBody(ctx.req);
17403
- const title = readString14(body, "title") ?? readString14(body, "name") ?? readString14(body, "display_name");
18447
+ const title = readString16(body, "title") ?? readString16(body, "name") ?? readString16(body, "display_name");
17404
18448
  if (!title) {
17405
18449
  throw new LinkHttpError(400, "title_required", "title is required");
17406
18450
  }
@@ -17539,7 +18583,7 @@ function registerConversationRoutes(router, options) {
17539
18583
  async (ctx) => {
17540
18584
  await authenticateRequest(ctx, paths);
17541
18585
  const body = await readJsonBody(ctx.req);
17542
- const scope = readString14(body, "scope") ?? "always";
18586
+ const scope = readString16(body, "scope") ?? "always";
17543
18587
  ctx.body = {
17544
18588
  ok: true,
17545
18589
  ...await conversations.resolveApproval({
@@ -17640,7 +18684,7 @@ function resolveConversationEventCursor(input) {
17640
18684
  return Math.max(queryAfter, headerAfter);
17641
18685
  }
17642
18686
  function readConversationClearPlanTargetStatus(body) {
17643
- const raw = readString14(body, "target_status") ?? readString14(body, "targetStatus") ?? "active";
18687
+ const raw = readString16(body, "target_status") ?? readString16(body, "targetStatus") ?? "active";
17644
18688
  if (raw === "active" || raw === "archived") {
17645
18689
  return raw;
17646
18690
  }
@@ -17731,8 +18775,8 @@ function createHttpErrorMiddleware(logger) {
17731
18775
 
17732
18776
  // src/hermes/profiles.ts
17733
18777
  import { execFile as execFile4 } from "child_process";
17734
- import { readdir as readdir9, readFile as readFile12, rename as rename3, stat as stat12 } from "fs/promises";
17735
- import path19 from "path";
18778
+ import { readdir as readdir9, readFile as readFile13, rename as rename3, stat as stat13 } from "fs/promises";
18779
+ import path20 from "path";
17736
18780
  import { setTimeout as delay2 } from "timers/promises";
17737
18781
  import { promisify as promisify4 } from "util";
17738
18782
  import YAML2 from "yaml";
@@ -17749,7 +18793,7 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
17749
18793
  const profilesDir = resolveHermesProfilesDir();
17750
18794
  const entries = await readdir9(profilesDir, { withFileTypes: true }).catch(
17751
18795
  (error) => {
17752
- if (isNodeError14(error, "ENOENT")) {
18796
+ if (isNodeError15(error, "ENOENT")) {
17753
18797
  return [];
17754
18798
  }
17755
18799
  throw error;
@@ -17773,8 +18817,8 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
17773
18817
  async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
17774
18818
  assertProfileName(name);
17775
18819
  const profile = await profileInfo(name, paths);
17776
- const exists = await stat12(profile.path).then((value) => value.isDirectory()).catch((error) => {
17777
- if (isNodeError14(error, "ENOENT")) {
18820
+ const exists = await stat13(profile.path).then((value) => value.isDirectory()).catch((error) => {
18821
+ if (isNodeError15(error, "ENOENT")) {
17778
18822
  return false;
17779
18823
  }
17780
18824
  throw error;
@@ -17843,7 +18887,7 @@ async function readHermesProfileCapabilities(name) {
17843
18887
  return {
17844
18888
  defaultModel: listedModels?.defaultModel ?? null,
17845
18889
  modelCount: listedModels?.models.length ?? 0,
17846
- skillCount: await countSkills(path19.join(profileDir, "skills")).catch(
18890
+ skillCount: await countSkills(path20.join(profileDir, "skills")).catch(
17847
18891
  () => 0
17848
18892
  ),
17849
18893
  toolCount: await countConfiguredTools(name).catch(() => 0)
@@ -17886,8 +18930,8 @@ function assertProfileName(name) {
17886
18930
  }
17887
18931
  }
17888
18932
  async function pathExists(targetPath) {
17889
- return await stat12(targetPath).then(() => true).catch((error) => {
17890
- if (isNodeError14(error, "ENOENT")) {
18933
+ return await stat13(targetPath).then(() => true).catch((error) => {
18934
+ if (isNodeError15(error, "ENOENT")) {
17891
18935
  return false;
17892
18936
  }
17893
18937
  throw error;
@@ -18004,7 +19048,7 @@ function isProcessRunning(pid) {
18004
19048
  process.kill(pid, 0);
18005
19049
  return true;
18006
19050
  } catch (error) {
18007
- return isNodeError14(error, "EPERM");
19051
+ return isNodeError15(error, "EPERM");
18008
19052
  }
18009
19053
  }
18010
19054
  async function waitForProfilePathToRemainAbsent(profilePath) {
@@ -18041,7 +19085,7 @@ function readExecErrorOutput2(error) {
18041
19085
  }
18042
19086
  return parts.join("\n");
18043
19087
  }
18044
- function isNodeError14(error, code) {
19088
+ function isNodeError15(error, code) {
18045
19089
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
18046
19090
  }
18047
19091
  function escapeRegExp2(value) {
@@ -18050,7 +19094,7 @@ function escapeRegExp2(value) {
18050
19094
  async function countSkills(root) {
18051
19095
  const entries = await readdir9(root, { withFileTypes: true }).catch(
18052
19096
  (error) => {
18053
- if (isNodeError14(error, "ENOENT")) {
19097
+ if (isNodeError15(error, "ENOENT")) {
18054
19098
  return [];
18055
19099
  }
18056
19100
  throw error;
@@ -18058,7 +19102,7 @@ async function countSkills(root) {
18058
19102
  );
18059
19103
  let count = 0;
18060
19104
  for (const entry of entries) {
18061
- const entryPath = path19.join(root, entry.name);
19105
+ const entryPath = path20.join(root, entry.name);
18062
19106
  if (entry.name === ".git" || entry.name === ".hub") {
18063
19107
  continue;
18064
19108
  }
@@ -18073,11 +19117,11 @@ async function countSkills(root) {
18073
19117
  return count;
18074
19118
  }
18075
19119
  async function countConfiguredTools(profileName) {
18076
- const raw = await readFile12(
19120
+ const raw = await readFile13(
18077
19121
  resolveHermesConfigPath(profileName),
18078
19122
  "utf8"
18079
19123
  ).catch((error) => {
18080
- if (isNodeError14(error, "ENOENT")) {
19124
+ if (isNodeError15(error, "ENOENT")) {
18081
19125
  return "";
18082
19126
  }
18083
19127
  throw error;
@@ -18085,14 +19129,14 @@ async function countConfiguredTools(profileName) {
18085
19129
  if (!raw.trim()) {
18086
19130
  return 0;
18087
19131
  }
18088
- const config = toRecord12(YAML2.parse(raw));
19132
+ const config = toRecord14(YAML2.parse(raw));
18089
19133
  const toolsets = /* @__PURE__ */ new Set();
18090
19134
  collectToolsetValues(config.toolsets, toolsets);
18091
- const platformToolsets = toRecord12(config.platform_toolsets);
19135
+ const platformToolsets = toRecord14(config.platform_toolsets);
18092
19136
  for (const value of Object.values(platformToolsets)) {
18093
19137
  collectToolsetValues(value, toolsets);
18094
19138
  }
18095
- const mcpServers = Object.keys(toRecord12(config.mcp_servers)).length;
19139
+ const mcpServers = Object.keys(toRecord14(config.mcp_servers)).length;
18096
19140
  return toolsets.size + mcpServers;
18097
19141
  }
18098
19142
  function collectToolsetValues(value, target) {
@@ -18106,7 +19150,7 @@ function collectToolsetValues(value, target) {
18106
19150
  target.add(value.trim());
18107
19151
  }
18108
19152
  }
18109
- function toRecord12(value) {
19153
+ function toRecord14(value) {
18110
19154
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
18111
19155
  }
18112
19156
 
@@ -18303,7 +19347,7 @@ function toHermesCronJobInput(input) {
18303
19347
  };
18304
19348
  }
18305
19349
  async function bindAndDecorateCronJobForHermesLink(input) {
18306
- const jobId = readString14(input.job, "id") ?? readString14(input.job, "job_id");
19350
+ const jobId = readString16(input.job, "id") ?? readString16(input.job, "job_id");
18307
19351
  if (!jobId) {
18308
19352
  return input.job;
18309
19353
  }
@@ -18320,9 +19364,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
18320
19364
  }
18321
19365
  function readCronJobCreateInput(body) {
18322
19366
  const input = {};
18323
- const name = readString14(body, "name") ?? readString14(body, "title");
18324
- const prompt = readString14(body, "prompt") ?? readString14(body, "description") ?? readString14(body, "task");
18325
- const schedule = readString14(body, "schedule");
19367
+ const name = readString16(body, "name") ?? readString16(body, "title");
19368
+ const prompt = readString16(body, "prompt") ?? readString16(body, "description") ?? readString16(body, "task");
19369
+ const schedule = readString16(body, "schedule");
18326
19370
  if (!name) {
18327
19371
  throw new LinkHttpError(400, "cron_job_name_required", "name is required");
18328
19372
  }
@@ -18343,7 +19387,7 @@ function readCronJobCreateInput(body) {
18343
19387
  input.name = name;
18344
19388
  input.prompt = prompt;
18345
19389
  input.schedule = schedule;
18346
- input.deliver = readString14(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
19390
+ input.deliver = readString16(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
18347
19391
  const skills = readOptionalCronSkills(body);
18348
19392
  if (skills) {
18349
19393
  input.skills = skills;
@@ -18587,7 +19631,7 @@ function registerModelConfigRoutes(router, options) {
18587
19631
  router.delete("/api/v1/model-configs", async (ctx) => {
18588
19632
  await authenticateRequest(ctx, paths);
18589
19633
  const body = await readJsonBody(ctx.req);
18590
- const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
19634
+ const modelId = readString16(body, "model_id") ?? readString16(body, "modelId");
18591
19635
  if (!modelId) {
18592
19636
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
18593
19637
  }
@@ -18659,7 +19703,7 @@ function registerModelConfigRoutes(router, options) {
18659
19703
  await authenticateRequest(ctx, paths);
18660
19704
  await getHermesProfileStatus(ctx.params.name, paths);
18661
19705
  const body = await readJsonBody(ctx.req);
18662
- const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
19706
+ const modelId = readString16(body, "model_id") ?? readString16(body, "modelId");
18663
19707
  if (!modelId) {
18664
19708
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
18665
19709
  }
@@ -18676,9 +19720,9 @@ function registerModelConfigRoutes(router, options) {
18676
19720
  });
18677
19721
  }
18678
19722
  function readModelConfigInput(body) {
18679
- const id = readString14(body, "id") ?? readString14(body, "model_id") ?? readString14(body, "modelId");
18680
- const provider = readString14(body, "provider") ?? readString14(body, "provider_key") ?? readString14(body, "providerKey");
18681
- const baseUrl = readString14(body, "base_url") ?? readString14(body, "baseUrl");
19723
+ const id = readString16(body, "id") ?? readString16(body, "model_id") ?? readString16(body, "modelId");
19724
+ const provider = readString16(body, "provider") ?? readString16(body, "provider_key") ?? readString16(body, "providerKey");
19725
+ const baseUrl = readString16(body, "base_url") ?? readString16(body, "baseUrl");
18682
19726
  if (!id || !provider || !baseUrl) {
18683
19727
  throw new LinkHttpError(
18684
19728
  400,
@@ -18688,29 +19732,29 @@ function readModelConfigInput(body) {
18688
19732
  }
18689
19733
  return {
18690
19734
  id,
18691
- originalModelId: readString14(body, "original_model_id") ?? readString14(body, "originalModelId") ?? readString14(body, "original_id") ?? void 0,
19735
+ originalModelId: readString16(body, "original_model_id") ?? readString16(body, "originalModelId") ?? readString16(body, "original_id") ?? void 0,
18692
19736
  provider,
18693
- providerName: readString14(body, "provider_name") ?? readString14(body, "providerName") ?? void 0,
19737
+ providerName: readString16(body, "provider_name") ?? readString16(body, "providerName") ?? void 0,
18694
19738
  baseUrl,
18695
- apiKey: readString14(body, "api_key") ?? readString14(body, "apiKey") ?? void 0,
18696
- apiMode: readString14(body, "api_mode") ?? readString14(body, "apiMode") ?? void 0,
19739
+ apiKey: readString16(body, "api_key") ?? readString16(body, "apiKey") ?? void 0,
19740
+ apiMode: readString16(body, "api_mode") ?? readString16(body, "apiMode") ?? void 0,
18697
19741
  contextLength: readPositiveInteger2(
18698
19742
  body.context_length ?? body.contextLength
18699
19743
  ),
18700
- keyEnv: readString14(body, "key_env") ?? readString14(body, "keyEnv") ?? void 0,
19744
+ keyEnv: readString16(body, "key_env") ?? readString16(body, "keyEnv") ?? void 0,
18701
19745
  setDefault: readBoolean3(body.set_default ?? body.setDefault),
18702
- reasoningEffort: readString14(body, "reasoning_effort") ?? readString14(body, "reasoningEffort") ?? void 0
19746
+ reasoningEffort: readString16(body, "reasoning_effort") ?? readString16(body, "reasoningEffort") ?? void 0
18703
19747
  };
18704
19748
  }
18705
19749
  function readModelDefaultsInput(body) {
18706
19750
  return {
18707
- taskModelId: readString14(body, "task_model_id") ?? readString14(body, "taskModelId") ?? readString14(body, "default_model_id") ?? readString14(body, "defaultModelId") ?? void 0,
18708
- compressionModelId: readString14(body, "compression_model_id") ?? readString14(body, "compressionModelId") ?? void 0
19751
+ taskModelId: readString16(body, "task_model_id") ?? readString16(body, "taskModelId") ?? readString16(body, "default_model_id") ?? readString16(body, "defaultModelId") ?? void 0,
19752
+ compressionModelId: readString16(body, "compression_model_id") ?? readString16(body, "compressionModelId") ?? void 0
18709
19753
  };
18710
19754
  }
18711
19755
  function readModelConfigImportInput(body) {
18712
- const sourceProfileName = readString14(body, "source_profile") ?? readString14(body, "sourceProfile") ?? readString14(body, "source_profile_name") ?? readString14(body, "sourceProfileName");
18713
- const modelId = readString14(body, "model_id") ?? readString14(body, "modelId") ?? readString14(body, "id");
19756
+ const sourceProfileName = readString16(body, "source_profile") ?? readString16(body, "sourceProfile") ?? readString16(body, "source_profile_name") ?? readString16(body, "sourceProfileName");
19757
+ const modelId = readString16(body, "model_id") ?? readString16(body, "modelId") ?? readString16(body, "id");
18714
19758
  if (!sourceProfileName || !modelId) {
18715
19759
  throw new LinkHttpError(
18716
19760
  400,
@@ -18721,9 +19765,9 @@ function readModelConfigImportInput(body) {
18721
19765
  return {
18722
19766
  sourceProfileName,
18723
19767
  modelId,
18724
- provider: readString14(body, "provider") ?? readString14(body, "provider_key") ?? readString14(body, "providerKey") ?? void 0,
18725
- baseUrl: readString14(body, "base_url") ?? readString14(body, "baseUrl") ?? void 0,
18726
- apiMode: readString14(body, "api_mode") ?? readString14(body, "apiMode") ?? void 0,
19768
+ provider: readString16(body, "provider") ?? readString16(body, "provider_key") ?? readString16(body, "providerKey") ?? void 0,
19769
+ baseUrl: readString16(body, "base_url") ?? readString16(body, "baseUrl") ?? void 0,
19770
+ apiMode: readString16(body, "api_mode") ?? readString16(body, "apiMode") ?? void 0,
18727
19771
  setDefault: readBoolean3(body.set_default ?? body.setDefault)
18728
19772
  };
18729
19773
  }
@@ -18808,11 +19852,11 @@ import { EventEmitter as EventEmitter2 } from "events";
18808
19852
  import {
18809
19853
  cp,
18810
19854
  mkdir as mkdir11,
18811
- readFile as readFile13,
19855
+ readFile as readFile14,
18812
19856
  rm as rm6,
18813
- stat as stat13
19857
+ stat as stat14
18814
19858
  } from "fs/promises";
18815
- import path20 from "path";
19859
+ import path21 from "path";
18816
19860
  import YAML3 from "yaml";
18817
19861
  var PROFILE_CREATE_LOG_FILE = "profile-create.log";
18818
19862
  var PROFILE_CREATE_LOG_MAX_FILES = 3;
@@ -19202,7 +20246,7 @@ function copyModelConfig(source, target) {
19202
20246
  copied[key] = cloneJson(source[key]);
19203
20247
  }
19204
20248
  }
19205
- const sourceAuxiliary = toRecord13(source.auxiliary);
20249
+ const sourceAuxiliary = toRecord15(source.auxiliary);
19206
20250
  if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
19207
20251
  const targetAuxiliary = ensureRecord2(target, "auxiliary");
19208
20252
  targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
@@ -19211,12 +20255,12 @@ function copyModelConfig(source, target) {
19211
20255
  return copied;
19212
20256
  }
19213
20257
  function copyToolPermissionsConfig(source, target) {
19214
- const sourcePlatformToolsets = toRecord13(source.platform_toolsets);
20258
+ const sourcePlatformToolsets = toRecord15(source.platform_toolsets);
19215
20259
  if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
19216
20260
  const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
19217
20261
  targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
19218
20262
  }
19219
- const sourceStt = toRecord13(source.stt);
20263
+ const sourceStt = toRecord15(source.stt);
19220
20264
  if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
19221
20265
  const targetStt = ensureRecord2(target, "stt");
19222
20266
  targetStt.enabled = cloneJson(sourceStt.enabled);
@@ -19263,9 +20307,9 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
19263
20307
  return keys;
19264
20308
  }
19265
20309
  async function writeEnvValues(profileName, values) {
19266
- const envPath = path20.join(resolveHermesProfileDir(profileName), ".env");
19267
- const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
19268
- if (isNodeError15(error, "ENOENT")) {
20310
+ const envPath = path21.join(resolveHermesProfileDir(profileName), ".env");
20311
+ const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
20312
+ if (isNodeError16(error, "ENOENT")) {
19269
20313
  return "";
19270
20314
  }
19271
20315
  throw error;
@@ -19300,8 +20344,8 @@ async function writeEnvValues(profileName, values) {
19300
20344
  await atomicWriteFilePreservingMetadata(envPath, nextRaw);
19301
20345
  }
19302
20346
  async function copySkills(sourceProfile, targetProfile) {
19303
- const sourceSkills = path20.join(resolveHermesProfileDir(sourceProfile), "skills");
19304
- const targetSkills = path20.join(resolveHermesProfileDir(targetProfile), "skills");
20347
+ const sourceSkills = path21.join(resolveHermesProfileDir(sourceProfile), "skills");
20348
+ const targetSkills = path21.join(resolveHermesProfileDir(targetProfile), "skills");
19305
20349
  if (!await pathExists2(sourceSkills)) {
19306
20350
  return;
19307
20351
  }
@@ -19324,16 +20368,16 @@ function copyProperty(source, target, key) {
19324
20368
  }
19325
20369
  }
19326
20370
  async function readYamlConfig(configPath) {
19327
- const existingRaw = await readFile13(configPath, "utf8").catch(
20371
+ const existingRaw = await readFile14(configPath, "utf8").catch(
19328
20372
  (error) => {
19329
- if (isNodeError15(error, "ENOENT")) {
20373
+ if (isNodeError16(error, "ENOENT")) {
19330
20374
  return null;
19331
20375
  }
19332
20376
  throw error;
19333
20377
  }
19334
20378
  );
19335
20379
  return {
19336
- config: toRecord13(existingRaw ? YAML3.parse(existingRaw) : {}),
20380
+ config: toRecord15(existingRaw ? YAML3.parse(existingRaw) : {}),
19337
20381
  existingRaw
19338
20382
  };
19339
20383
  }
@@ -19387,7 +20431,7 @@ async function writeProfileCreationState(paths, state) {
19387
20431
  await writeJsonFile(profileCreationStatePath(paths), state);
19388
20432
  }
19389
20433
  async function readProfileCreationLogLines(paths) {
19390
- const raw = await readFile13(profileCreationLogPath(paths), "utf8").catch(() => "");
20434
+ const raw = await readFile14(profileCreationLogPath(paths), "utf8").catch(() => "");
19391
20435
  if (!raw.trim()) {
19392
20436
  return [];
19393
20437
  }
@@ -19396,10 +20440,10 @@ async function readProfileCreationLogLines(paths) {
19396
20440
  );
19397
20441
  }
19398
20442
  function profileCreationStatePath(paths) {
19399
- return path20.join(paths.runDir, "profile-create-state.json");
20443
+ return path21.join(paths.runDir, "profile-create-state.json");
19400
20444
  }
19401
20445
  function profileCreationLogPath(paths) {
19402
- return path20.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
20446
+ return path21.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
19403
20447
  }
19404
20448
  async function clearProfileCreationLogFiles(paths) {
19405
20449
  const primary = profileCreationLogPath(paths);
@@ -19412,8 +20456,8 @@ async function clearProfileCreationLogFiles(paths) {
19412
20456
  ]);
19413
20457
  }
19414
20458
  async function pathExists2(targetPath) {
19415
- return await stat13(targetPath).then(() => true).catch((error) => {
19416
- if (isNodeError15(error, "ENOENT")) {
20459
+ return await stat14(targetPath).then(() => true).catch((error) => {
20460
+ if (isNodeError16(error, "ENOENT")) {
19417
20461
  return false;
19418
20462
  }
19419
20463
  throw error;
@@ -19445,7 +20489,7 @@ function ensureRecord2(target, key) {
19445
20489
  target[key] = next;
19446
20490
  return next;
19447
20491
  }
19448
- function toRecord13(value) {
20492
+ function toRecord15(value) {
19449
20493
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
19450
20494
  }
19451
20495
  function cloneJson(value) {
@@ -19460,7 +20504,7 @@ function formatEnvValue2(value) {
19460
20504
  function escapeRegExp3(value) {
19461
20505
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
19462
20506
  }
19463
- function isNodeError15(error, code) {
20507
+ function isNodeError16(error, code) {
19464
20508
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
19465
20509
  }
19466
20510
 
@@ -19545,16 +20589,16 @@ function readProfilePermissionsInput(body) {
19545
20589
  const approvals = readOptionalObject(body, "approvals");
19546
20590
  if (approvals) {
19547
20591
  input.approvals = {
19548
- mode: readString14(approvals, "mode") ?? readString14(approvals, "approval_mode") ?? readString14(approvals, "approvalMode") ?? void 0,
20592
+ mode: readString16(approvals, "mode") ?? readString16(approvals, "approval_mode") ?? readString16(approvals, "approvalMode") ?? void 0,
19549
20593
  timeout: readPositiveInteger2(approvals.timeout),
19550
- cronMode: readString14(approvals, "cron_mode") ?? readString14(approvals, "cronMode") ?? void 0
20594
+ cronMode: readString16(approvals, "cron_mode") ?? readString16(approvals, "cronMode") ?? void 0
19551
20595
  };
19552
20596
  }
19553
20597
  const terminal = readOptionalObject(body, "terminal");
19554
20598
  if (terminal) {
19555
20599
  input.terminal = {
19556
- backend: readString14(terminal, "backend") ?? void 0,
19557
- cwd: readString14(terminal, "cwd") ?? void 0,
20600
+ backend: readString16(terminal, "backend") ?? void 0,
20601
+ cwd: readString16(terminal, "cwd") ?? void 0,
19558
20602
  containerCpu: readPositiveInteger2(
19559
20603
  terminal.container_cpu ?? terminal.containerCpu
19560
20604
  ),
@@ -19670,10 +20714,10 @@ function toProfileToolConfigHttpError(error) {
19670
20714
  import {
19671
20715
  access as access3,
19672
20716
  readdir as readdir10,
19673
- readFile as readFile14,
19674
- stat as stat14
20717
+ readFile as readFile15,
20718
+ stat as stat15
19675
20719
  } from "fs/promises";
19676
- import path21 from "path";
20720
+ import path22 from "path";
19677
20721
  import YAML4 from "yaml";
19678
20722
  var ENTRY_DELIMITER = "\n\xA7\n";
19679
20723
  var DEFAULT_MEMORY_LIMIT = 2200;
@@ -19861,9 +20905,9 @@ async function testHindsightProviderSettings(profileName, patch) {
19861
20905
  const mode = normalizeHindsightMode(
19862
20906
  patch.mode ?? config.mode ?? env.HINDSIGHT_MODE
19863
20907
  );
19864
- const apiUrl = readString15(patch.apiUrl) ?? readString15(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
19865
- const bankId = readString15(patch.bankId) ?? readString15(config.bank_id) ?? "hermes";
19866
- const apiKey = readString15(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString15(config.apiKey) ?? readString15(config.api_key);
20908
+ const apiUrl = readString17(patch.apiUrl) ?? readString17(config.api_url) ?? env.HINDSIGHT_API_URL ?? (mode === "cloud" ? HINDSIGHT_DEFAULT_API_URL : HINDSIGHT_DEFAULT_LOCAL_URL);
20909
+ const bankId = readString17(patch.bankId) ?? readString17(config.bank_id) ?? "hermes";
20910
+ const apiKey = readString17(patch.apiKey) ?? env.HINDSIGHT_API_KEY ?? readString17(config.apiKey) ?? readString17(config.api_key);
19867
20911
  const baseUrl = normalizeHttpUrl(apiUrl);
19868
20912
  if (!baseUrl) {
19869
20913
  return {
@@ -19995,7 +21039,7 @@ async function saveProviderSettings(profileName, provider, patch) {
19995
21039
  });
19996
21040
  await patchJsonProviderConfig(
19997
21041
  profileName,
19998
- path21.join("hindsight", "config.json"),
21042
+ path22.join("hindsight", "config.json"),
19999
21043
  {
20000
21044
  mode: patch.mode,
20001
21045
  api_url: patch.apiUrl,
@@ -20052,7 +21096,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
20052
21096
  "\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
20053
21097
  );
20054
21098
  }
20055
- const config = toRecord14(parsed);
21099
+ const config = toRecord16(parsed);
20056
21100
  if (Object.keys(config).length === 0 && parsed !== null) {
20057
21101
  throw new HermesMemoryError(
20058
21102
  "memory_provider_config_invalid",
@@ -20143,17 +21187,17 @@ function normalizeCustomProviderId(provider) {
20143
21187
  }
20144
21188
  async function patchHermesMemoryProvider(profileName, provider) {
20145
21189
  const configPath = resolveHermesConfigPath(profileName);
20146
- const existingRaw = await readFile14(configPath, "utf8").catch(
21190
+ const existingRaw = await readFile15(configPath, "utf8").catch(
20147
21191
  (error) => {
20148
- if (isNodeError16(error, "ENOENT")) {
21192
+ if (isNodeError17(error, "ENOENT")) {
20149
21193
  return null;
20150
21194
  }
20151
21195
  throw error;
20152
21196
  }
20153
21197
  );
20154
21198
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
20155
- const config = toRecord14(document.toJSON());
20156
- const memory = toRecord14(config.memory);
21199
+ const config = toRecord16(document.toJSON());
21200
+ const memory = toRecord16(config.memory);
20157
21201
  memory.provider = provider === "built-in" ? "" : provider;
20158
21202
  config.memory = memory;
20159
21203
  const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
@@ -20166,13 +21210,13 @@ async function patchHermesMemoryProvider(profileName, provider) {
20166
21210
  await atomicWriteFilePreservingMetadata(configPath, document.toString());
20167
21211
  }
20168
21212
  function resolveMemoryDir(profileName) {
20169
- return path21.join(resolveHermesProfileDir(profileName), "memories");
21213
+ return path22.join(resolveHermesProfileDir(profileName), "memories");
20170
21214
  }
20171
21215
  async function readMemoryStore(profileName, target, limits) {
20172
21216
  const filePath = memoryFilePath(profileName, target);
20173
21217
  const entries = await readMemoryEntries(filePath);
20174
- const fileStat = await stat14(filePath).catch((error) => {
20175
- if (isNodeError16(error, "ENOENT")) {
21218
+ const fileStat = await stat15(filePath).catch((error) => {
21219
+ if (isNodeError17(error, "ENOENT")) {
20176
21220
  return null;
20177
21221
  }
20178
21222
  throw error;
@@ -20200,8 +21244,8 @@ async function readMemoryStore(profileName, target, limits) {
20200
21244
  };
20201
21245
  }
20202
21246
  async function readMemoryEntries(filePath) {
20203
- const raw = await readFile14(filePath, "utf8").catch((error) => {
20204
- if (isNodeError16(error, "ENOENT")) {
21247
+ const raw = await readFile15(filePath, "utf8").catch((error) => {
21248
+ if (isNodeError17(error, "ENOENT")) {
20205
21249
  return "";
20206
21250
  }
20207
21251
  throw error;
@@ -20227,7 +21271,7 @@ async function writeMemoryEntries(profileName, target, entries) {
20227
21271
  );
20228
21272
  }
20229
21273
  function memoryFilePath(profileName, target) {
20230
- return path21.join(
21274
+ return path22.join(
20231
21275
  resolveMemoryDir(profileName),
20232
21276
  target === "user" ? "USER.md" : "MEMORY.md"
20233
21277
  );
@@ -20287,7 +21331,7 @@ async function readCustomProviderSetupSummary(profileName) {
20287
21331
  configurable: true,
20288
21332
  configured: true,
20289
21333
  configurationIssue: null,
20290
- providerConfigPath: path21.join(
21334
+ providerConfigPath: path22.join(
20291
21335
  resolveHermesProfileDir(profileName),
20292
21336
  "<provider>.json"
20293
21337
  ),
@@ -20365,7 +21409,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
20365
21409
  const config2 = await readJsonObject(
20366
21410
  memoryProviderConfigPath(profileName, "honcho") ?? ""
20367
21411
  );
20368
- return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString15(config2.apiKey)) || isConfiguredEnvValue(readString15(config2.api_key)) || isConfiguredEnvValue(readString15(config2.baseUrl)) ? { configured: true, issue: null } : {
21412
+ return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString17(config2.apiKey)) || isConfiguredEnvValue(readString17(config2.api_key)) || isConfiguredEnvValue(readString17(config2.baseUrl)) ? { configured: true, issue: null } : {
20369
21413
  configured: false,
20370
21414
  issue: "Honcho \u9700\u8981\u5148\u586B\u5199 API Key\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
20371
21415
  };
@@ -20374,7 +21418,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
20374
21418
  const config2 = await readJsonObject(
20375
21419
  memoryProviderConfigPath(profileName, "mem0") ?? ""
20376
21420
  );
20377
- return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString15(config2.api_key)) ? { configured: true, issue: null } : {
21421
+ return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString17(config2.api_key)) ? { configured: true, issue: null } : {
20378
21422
  configured: false,
20379
21423
  issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u9875\u586B\u5199 API Key\uFF0CLink \u4F1A\u5199\u5165\u5F53\u524D Profile \u7684 .env\u3002"
20380
21424
  };
@@ -20416,7 +21460,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
20416
21460
  memoryProviderConfigPath(profileName, provider) ?? ""
20417
21461
  );
20418
21462
  const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
20419
- const apiKey = readString15(config.apiKey) ?? readString15(config.api_key) ?? env.HINDSIGHT_API_KEY;
21463
+ const apiKey = readString17(config.apiKey) ?? readString17(config.api_key) ?? env.HINDSIGHT_API_KEY;
20420
21464
  if (mode === "cloud") {
20421
21465
  return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
20422
21466
  configured: false,
@@ -20424,15 +21468,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
20424
21468
  };
20425
21469
  }
20426
21470
  if (mode === "local_external") {
20427
- const apiUrl = readString15(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
21471
+ const apiUrl = readString17(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
20428
21472
  return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
20429
21473
  configured: false,
20430
21474
  issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
20431
21475
  };
20432
21476
  }
20433
21477
  if (mode === "local_embedded") {
20434
- const llmProvider = readString15(config.llm_provider) ?? "openai";
20435
- const llmModel = readString15(config.llm_model);
21478
+ const llmProvider = readString17(config.llm_provider) ?? "openai";
21479
+ const llmModel = readString17(config.llm_model);
20436
21480
  if (!llmModel) {
20437
21481
  return {
20438
21482
  configured: false,
@@ -20440,7 +21484,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
20440
21484
  };
20441
21485
  }
20442
21486
  if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
20443
- readString15(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
21487
+ readString17(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
20444
21488
  )) {
20445
21489
  return {
20446
21490
  configured: false,
@@ -20448,7 +21492,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
20448
21492
  };
20449
21493
  }
20450
21494
  if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
20451
- readString15(config.llmApiKey) ?? readString15(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
21495
+ readString17(config.llmApiKey) ?? readString17(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
20452
21496
  )) {
20453
21497
  return {
20454
21498
  configured: false,
@@ -20504,8 +21548,8 @@ async function readProviderSettings(profileName, provider) {
20504
21548
  secretSetting(
20505
21549
  "apiKey",
20506
21550
  "API Key",
20507
- env.HONCHO_API_KEY ?? readString15(config.apiKey) ?? readString15(config.api_key),
20508
- isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString15(config.apiKey)) || isConfiguredEnvValue(readString15(config.api_key))
21551
+ env.HONCHO_API_KEY ?? readString17(config.apiKey) ?? readString17(config.api_key),
21552
+ isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString17(config.apiKey)) || isConfiguredEnvValue(readString17(config.api_key))
20509
21553
  ),
20510
21554
  stringSetting("workspace", "Workspace", config.workspace ?? "hermes"),
20511
21555
  stringSetting("peerName", "\u7528\u6237 Peer", config.peerName ?? ""),
@@ -20547,8 +21591,8 @@ async function readProviderSettings(profileName, provider) {
20547
21591
  secretSetting(
20548
21592
  "apiKey",
20549
21593
  "API Key",
20550
- env.MEM0_API_KEY ?? readString15(config.apiKey) ?? readString15(config.api_key),
20551
- isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString15(config.apiKey)) || isConfiguredEnvValue(readString15(config.api_key))
21594
+ env.MEM0_API_KEY ?? readString17(config.apiKey) ?? readString17(config.api_key),
21595
+ isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString17(config.apiKey)) || isConfiguredEnvValue(readString17(config.api_key))
20552
21596
  ),
20553
21597
  stringSetting("userId", "User ID", config.user_id ?? "hermes-user"),
20554
21598
  stringSetting("agentId", "Agent ID", config.agent_id ?? "hermes"),
@@ -20616,8 +21660,8 @@ async function readProviderSettings(profileName, provider) {
20616
21660
  memoryProviderConfigPath(profileName, provider) ?? ""
20617
21661
  );
20618
21662
  const env = await readHermesMemoryEnv(profileName);
20619
- const banks = toRecord14(config.banks);
20620
- const hermesBank = toRecord14(banks.hermes);
21663
+ const banks = toRecord16(config.banks);
21664
+ const hermesBank = toRecord16(banks.hermes);
20621
21665
  const mode = normalizeHindsightMode(config.mode);
20622
21666
  return [
20623
21667
  selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
@@ -20633,8 +21677,8 @@ async function readProviderSettings(profileName, provider) {
20633
21677
  secretSetting(
20634
21678
  "apiKey",
20635
21679
  "Hindsight API Key",
20636
- env.HINDSIGHT_API_KEY ?? readString15(config.apiKey) ?? readString15(config.api_key),
20637
- isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString15(config.apiKey)) || isConfiguredEnvValue(readString15(config.api_key))
21680
+ env.HINDSIGHT_API_KEY ?? readString17(config.apiKey) ?? readString17(config.api_key),
21681
+ isConfiguredEnvValue(env.HINDSIGHT_API_KEY) || isConfiguredEnvValue(readString17(config.apiKey)) || isConfiguredEnvValue(readString17(config.api_key))
20638
21682
  ),
20639
21683
  stringSetting(
20640
21684
  "bankId",
@@ -20656,8 +21700,8 @@ async function readProviderSettings(profileName, provider) {
20656
21700
  secretSetting(
20657
21701
  "llmApiKey",
20658
21702
  "LLM API Key",
20659
- env.HINDSIGHT_LLM_API_KEY ?? readString15(config.llmApiKey) ?? readString15(config.llm_api_key),
20660
- isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString15(config.llmApiKey)) || isConfiguredEnvValue(readString15(config.llm_api_key))
21703
+ env.HINDSIGHT_LLM_API_KEY ?? readString17(config.llmApiKey) ?? readString17(config.llm_api_key),
21704
+ isConfiguredEnvValue(env.HINDSIGHT_LLM_API_KEY) || isConfiguredEnvValue(readString17(config.llmApiKey)) || isConfiguredEnvValue(readString17(config.llm_api_key))
20661
21705
  ),
20662
21706
  booleanSetting("autoRecall", "\u81EA\u52A8\u56DE\u5FC6", config.auto_recall ?? true),
20663
21707
  booleanSetting("autoRetain", "\u81EA\u52A8\u6C89\u6DC0", config.auto_retain ?? true),
@@ -20681,7 +21725,7 @@ async function readProviderSettings(profileName, provider) {
20681
21725
  stringSetting(
20682
21726
  "dbPath",
20683
21727
  "SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
20684
- config.db_path ?? path21.join(resolveHermesProfileDir(profileName), "memory_store.db")
21728
+ config.db_path ?? path22.join(resolveHermesProfileDir(profileName), "memory_store.db")
20685
21729
  ),
20686
21730
  booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
20687
21731
  numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
@@ -20717,7 +21761,7 @@ async function readProviderSettings(profileName, provider) {
20717
21761
  stringSetting(
20718
21762
  "workingDirectory",
20719
21763
  "\u5DE5\u4F5C\u76EE\u5F55",
20720
- path21.join(resolveHermesProfileDir(profileName), "byterover"),
21764
+ path22.join(resolveHermesProfileDir(profileName), "byterover"),
20721
21765
  false
20722
21766
  )
20723
21767
  ];
@@ -20726,16 +21770,16 @@ async function readProviderSettings(profileName, provider) {
20726
21770
  }
20727
21771
  function memoryProviderConfigPath(profileName, provider) {
20728
21772
  if (provider === "honcho") {
20729
- return path21.join(resolveHermesProfileDir(profileName), "honcho.json");
21773
+ return path22.join(resolveHermesProfileDir(profileName), "honcho.json");
20730
21774
  }
20731
21775
  if (provider === "mem0") {
20732
- return path21.join(resolveHermesProfileDir(profileName), "mem0.json");
21776
+ return path22.join(resolveHermesProfileDir(profileName), "mem0.json");
20733
21777
  }
20734
21778
  if (provider === "supermemory") {
20735
- return path21.join(resolveHermesProfileDir(profileName), "supermemory.json");
21779
+ return path22.join(resolveHermesProfileDir(profileName), "supermemory.json");
20736
21780
  }
20737
21781
  if (provider === "hindsight") {
20738
- return path21.join(
21782
+ return path22.join(
20739
21783
  resolveHermesProfileDir(profileName),
20740
21784
  "hindsight",
20741
21785
  "config.json"
@@ -20744,21 +21788,21 @@ function memoryProviderConfigPath(profileName, provider) {
20744
21788
  return null;
20745
21789
  }
20746
21790
  function customProviderConfigPath(profileName, provider) {
20747
- return path21.join(
21791
+ return path22.join(
20748
21792
  resolveHermesProfileDir(profileName),
20749
21793
  `${normalizeCustomProviderId(provider)}.json`
20750
21794
  );
20751
21795
  }
20752
21796
  function customProviderRegistryPath(profileName) {
20753
- return path21.join(
21797
+ return path22.join(
20754
21798
  resolveHermesProfileDir(profileName),
20755
21799
  CUSTOM_PROVIDER_REGISTRY_FILE
20756
21800
  );
20757
21801
  }
20758
21802
  async function readCustomProviderRegistry(profileName) {
20759
- const raw = await readFile14(customProviderRegistryPath(profileName), "utf8").catch(
21803
+ const raw = await readFile15(customProviderRegistryPath(profileName), "utf8").catch(
20760
21804
  (error) => {
20761
- if (isNodeError16(error, "ENOENT")) {
21805
+ if (isNodeError17(error, "ENOENT")) {
20762
21806
  return "";
20763
21807
  }
20764
21808
  throw error;
@@ -20769,18 +21813,18 @@ async function readCustomProviderRegistry(profileName) {
20769
21813
  }
20770
21814
  try {
20771
21815
  const parsed = JSON.parse(raw);
20772
- const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord14(parsed).providers) ? toRecord14(parsed).providers : [];
21816
+ const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord16(parsed).providers) ? toRecord16(parsed).providers : [];
20773
21817
  return providers.map((item) => {
20774
21818
  if (typeof item === "string") {
20775
21819
  const id2 = normalizeCustomProviderId(item);
20776
21820
  return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
20777
21821
  }
20778
- const record = toRecord14(item);
20779
- const id = normalizeCustomProviderId(readString15(record.id) ?? "");
21822
+ const record = toRecord16(item);
21823
+ const id = normalizeCustomProviderId(readString17(record.id) ?? "");
20780
21824
  return {
20781
21825
  id,
20782
- label: readString15(record.label) ?? id,
20783
- description: readString15(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
21826
+ label: readString17(record.label) ?? id,
21827
+ description: readString17(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
20784
21828
  };
20785
21829
  }).filter((item) => item.id);
20786
21830
  } catch {
@@ -20802,10 +21846,10 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
20802
21846
  );
20803
21847
  }
20804
21848
  async function discoverUserMemoryProviderDescriptors(profileName) {
20805
- const pluginsDir = path21.join(resolveHermesProfileDir(profileName), "plugins");
21849
+ const pluginsDir = path22.join(resolveHermesProfileDir(profileName), "plugins");
20806
21850
  const entries = await readdir10(pluginsDir, { withFileTypes: true }).catch(
20807
21851
  (error) => {
20808
- if (isNodeError16(error, "ENOENT")) {
21852
+ if (isNodeError17(error, "ENOENT")) {
20809
21853
  return [];
20810
21854
  }
20811
21855
  throw error;
@@ -20822,21 +21866,21 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
20822
21866
  } catch {
20823
21867
  continue;
20824
21868
  }
20825
- const providerDir = path21.join(pluginsDir, entry.name);
21869
+ const providerDir = path22.join(pluginsDir, entry.name);
20826
21870
  if (!await isMemoryProviderPluginDir(providerDir)) {
20827
21871
  continue;
20828
21872
  }
20829
21873
  const meta = await readPluginMetadata(providerDir);
20830
21874
  descriptors.push({
20831
21875
  id: providerId,
20832
- label: readString15(meta.name) ?? providerId,
20833
- description: readString15(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
21876
+ label: readString17(meta.name) ?? providerId,
21877
+ description: readString17(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
20834
21878
  });
20835
21879
  }
20836
21880
  return descriptors;
20837
21881
  }
20838
21882
  async function isUserMemoryProviderInstalled(profileName, provider) {
20839
- const providerDir = path21.join(
21883
+ const providerDir = path22.join(
20840
21884
  resolveHermesProfileDir(profileName),
20841
21885
  "plugins",
20842
21886
  normalizeCustomProviderId(provider)
@@ -20844,9 +21888,9 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
20844
21888
  return isMemoryProviderPluginDir(providerDir);
20845
21889
  }
20846
21890
  async function isMemoryProviderPluginDir(providerDir) {
20847
- const source = await readFile14(path21.join(providerDir, "__init__.py"), "utf8").catch(
21891
+ const source = await readFile15(path22.join(providerDir, "__init__.py"), "utf8").catch(
20848
21892
  (error) => {
20849
- if (isNodeError16(error, "ENOENT")) {
21893
+ if (isNodeError17(error, "ENOENT")) {
20850
21894
  return "";
20851
21895
  }
20852
21896
  throw error;
@@ -20856,22 +21900,22 @@ async function isMemoryProviderPluginDir(providerDir) {
20856
21900
  return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
20857
21901
  }
20858
21902
  async function readPluginMetadata(providerDir) {
20859
- const raw = await readFile14(path21.join(providerDir, "plugin.yaml"), "utf8").catch(
21903
+ const raw = await readFile15(path22.join(providerDir, "plugin.yaml"), "utf8").catch(
20860
21904
  (error) => {
20861
- if (isNodeError16(error, "ENOENT")) {
21905
+ if (isNodeError17(error, "ENOENT")) {
20862
21906
  return "";
20863
21907
  }
20864
21908
  throw error;
20865
21909
  }
20866
21910
  );
20867
- return raw ? toRecord14(YAML4.parse(raw)) : {};
21911
+ return raw ? toRecord16(YAML4.parse(raw)) : {};
20868
21912
  }
20869
21913
  async function resolveByteRoverCli() {
20870
21914
  const candidates = [
20871
- ...(process.env.PATH ?? "").split(path21.delimiter).filter(Boolean).map((dir) => path21.join(dir, "brv")),
20872
- path21.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
21915
+ ...(process.env.PATH ?? "").split(path22.delimiter).filter(Boolean).map((dir) => path22.join(dir, "brv")),
21916
+ path22.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
20873
21917
  "/usr/local/bin/brv",
20874
- path21.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
21918
+ path22.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
20875
21919
  ].filter(Boolean);
20876
21920
  for (const candidate of candidates) {
20877
21921
  const found = await access3(candidate).then(() => true).catch(() => false);
@@ -20882,32 +21926,32 @@ async function resolveByteRoverCli() {
20882
21926
  return null;
20883
21927
  }
20884
21928
  async function readHolographicProviderConfig(profileName) {
20885
- const raw = await readFile14(resolveHermesConfigPath(profileName), "utf8").catch(
21929
+ const raw = await readFile15(resolveHermesConfigPath(profileName), "utf8").catch(
20886
21930
  (error) => {
20887
- if (isNodeError16(error, "ENOENT")) {
21931
+ if (isNodeError17(error, "ENOENT")) {
20888
21932
  return "";
20889
21933
  }
20890
21934
  throw error;
20891
21935
  }
20892
21936
  );
20893
- const config = raw ? toRecord14(YAML4.parse(raw)) : {};
20894
- const plugins = toRecord14(config.plugins);
20895
- return toRecord14(plugins["hermes-memory-store"]);
21937
+ const config = raw ? toRecord16(YAML4.parse(raw)) : {};
21938
+ const plugins = toRecord16(config.plugins);
21939
+ return toRecord16(plugins["hermes-memory-store"]);
20896
21940
  }
20897
21941
  async function patchHolographicProviderConfig(profileName, patch) {
20898
21942
  const configPath = resolveHermesConfigPath(profileName);
20899
- const existingRaw = await readFile14(configPath, "utf8").catch(
21943
+ const existingRaw = await readFile15(configPath, "utf8").catch(
20900
21944
  (error) => {
20901
- if (isNodeError16(error, "ENOENT")) {
21945
+ if (isNodeError17(error, "ENOENT")) {
20902
21946
  return null;
20903
21947
  }
20904
21948
  throw error;
20905
21949
  }
20906
21950
  );
20907
21951
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
20908
- const config = toRecord14(document.toJSON());
20909
- const plugins = toRecord14(config.plugins);
20910
- const memoryStore = toRecord14(plugins["hermes-memory-store"]);
21952
+ const config = toRecord16(document.toJSON());
21953
+ const plugins = toRecord16(config.plugins);
21954
+ const memoryStore = toRecord16(plugins["hermes-memory-store"]);
20911
21955
  for (const [key, value] of Object.entries(patch)) {
20912
21956
  if (value !== void 0) {
20913
21957
  memoryStore[key] = value;
@@ -20932,9 +21976,9 @@ async function patchHermesMemoryEnv(profileName, patch) {
20932
21976
  if (entries.length === 0) {
20933
21977
  return;
20934
21978
  }
20935
- const envPath = path21.join(resolveHermesProfileDir(profileName), ".env");
20936
- const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
20937
- if (isNodeError16(error, "ENOENT")) {
21979
+ const envPath = path22.join(resolveHermesProfileDir(profileName), ".env");
21980
+ const existingRaw = await readFile15(envPath, "utf8").catch((error) => {
21981
+ if (isNodeError17(error, "ENOENT")) {
20938
21982
  return "";
20939
21983
  }
20940
21984
  throw error;
@@ -20992,7 +22036,7 @@ function isMemoryEnvKeyWritable(key) {
20992
22036
  ].includes(key);
20993
22037
  }
20994
22038
  function normalizeHindsightMode(value) {
20995
- const mode = readString15(value) ?? "cloud";
22039
+ const mode = readString17(value) ?? "cloud";
20996
22040
  return mode === "local" ? "local_embedded" : mode;
20997
22041
  }
20998
22042
  function normalizeHttpUrl(value) {
@@ -21055,55 +22099,55 @@ function joinHindsightUrl(baseUrl, pathName) {
21055
22099
  }
21056
22100
  function parseJsonObject2(text) {
21057
22101
  try {
21058
- return toRecord14(JSON.parse(text));
22102
+ return toRecord16(JSON.parse(text));
21059
22103
  } catch {
21060
22104
  return {};
21061
22105
  }
21062
22106
  }
21063
22107
  function readHindsightError(json) {
21064
- const detail = readString15(json.detail) ?? readString15(json.error);
22108
+ const detail = readString17(json.detail) ?? readString17(json.error);
21065
22109
  return detail ? `\uFF1A${detail}` : "";
21066
22110
  }
21067
22111
  function hindsightSemanticIssue(pathName, json) {
21068
22112
  if (pathName === "/health") {
21069
- const status = readString15(json.status);
22113
+ const status = readString17(json.status);
21070
22114
  return status && ["healthy", "ok"].includes(status.toLowerCase()) ? null : `\u5065\u5EB7\u72B6\u6001\u5F02\u5E38\uFF1A${status ?? "unknown"}`;
21071
22115
  }
21072
22116
  return null;
21073
22117
  }
21074
22118
  function summarizeHindsightProbe(pathName, json) {
21075
22119
  if (pathName === "/health") {
21076
- const status = readString15(json.status) ?? "ok";
21077
- const database = readString15(json.database);
22120
+ const status = readString17(json.status) ?? "ok";
22121
+ const database = readString17(json.database);
21078
22122
  return database ? `${status}, database ${database}` : status;
21079
22123
  }
21080
22124
  if (pathName === "/version") {
21081
- const version = readString15(json.api_version);
22125
+ const version = readString17(json.api_version);
21082
22126
  return version ? `API ${version}` : "version endpoint reachable";
21083
22127
  }
21084
- const bankId = readString15(json.bank_id);
22128
+ const bankId = readString17(json.bank_id);
21085
22129
  return bankId ? `bank ${bankId} reachable` : "bank config reachable";
21086
22130
  }
21087
22131
  async function readActiveMemoryProvider(profileName) {
21088
- const raw = await readFile14(
22132
+ const raw = await readFile15(
21089
22133
  resolveHermesConfigPath(profileName),
21090
22134
  "utf8"
21091
22135
  ).catch((error) => {
21092
- if (isNodeError16(error, "ENOENT")) {
22136
+ if (isNodeError17(error, "ENOENT")) {
21093
22137
  return "";
21094
22138
  }
21095
22139
  throw error;
21096
22140
  });
21097
- const config = raw ? toRecord14(YAML4.parse(raw)) : {};
21098
- const memory = toRecord14(config.memory);
21099
- const provider = readString15(memory.provider);
22141
+ const config = raw ? toRecord16(YAML4.parse(raw)) : {};
22142
+ const memory = toRecord16(config.memory);
22143
+ const provider = readString17(memory.provider);
21100
22144
  if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
21101
22145
  return null;
21102
22146
  }
21103
22147
  return provider;
21104
22148
  }
21105
22149
  async function patchJsonProviderConfig(profileName, relativePath, patch) {
21106
- const configPath = path21.join(
22150
+ const configPath = path22.join(
21107
22151
  resolveHermesProfileDir(profileName),
21108
22152
  relativePath
21109
22153
  );
@@ -21121,18 +22165,18 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
21121
22165
  );
21122
22166
  }
21123
22167
  async function readJsonObject(filePath) {
21124
- const raw = await readFile14(filePath, "utf8").catch((error) => {
21125
- if (isNodeError16(error, "ENOENT")) {
22168
+ const raw = await readFile15(filePath, "utf8").catch((error) => {
22169
+ if (isNodeError17(error, "ENOENT")) {
21126
22170
  return "{}";
21127
22171
  }
21128
22172
  throw error;
21129
22173
  });
21130
22174
  try {
21131
- return toRecord14(JSON.parse(raw || "{}"));
22175
+ return toRecord16(JSON.parse(raw || "{}"));
21132
22176
  } catch {
21133
22177
  throw new HermesMemoryError(
21134
22178
  "memory_provider_config_invalid",
21135
- `${path21.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
22179
+ `${path22.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
21136
22180
  );
21137
22181
  }
21138
22182
  }
@@ -21153,7 +22197,7 @@ function stringSetting(key, label, value, editable = true) {
21153
22197
  return {
21154
22198
  key,
21155
22199
  label,
21156
- value: readString15(value) ?? "",
22200
+ value: readString17(value) ?? "",
21157
22201
  editable,
21158
22202
  kind: "string"
21159
22203
  };
@@ -21165,7 +22209,7 @@ function secretSetting(key, label, value, configured) {
21165
22209
  value: "",
21166
22210
  editable: true,
21167
22211
  kind: "secret",
21168
- configured: configured || isConfiguredEnvValue(readString15(value))
22212
+ configured: configured || isConfiguredEnvValue(readString17(value))
21169
22213
  };
21170
22214
  }
21171
22215
  function textSetting(key, label, value, editable = true) {
@@ -21178,21 +22222,21 @@ function textSetting(key, label, value, editable = true) {
21178
22222
  };
21179
22223
  }
21180
22224
  function selectSetting(key, label, value, options, editable = true) {
21181
- const stringValue = readString15(value) ?? options[0] ?? null;
22225
+ const stringValue = readString17(value) ?? options[0] ?? null;
21182
22226
  return { key, label, value: stringValue, editable, kind: "select", options };
21183
22227
  }
21184
22228
  async function readMemoryLimits(profileName) {
21185
- const raw = await readFile14(
22229
+ const raw = await readFile15(
21186
22230
  resolveHermesConfigPath(profileName),
21187
22231
  "utf8"
21188
22232
  ).catch((error) => {
21189
- if (isNodeError16(error, "ENOENT")) {
22233
+ if (isNodeError17(error, "ENOENT")) {
21190
22234
  return "";
21191
22235
  }
21192
22236
  throw error;
21193
22237
  });
21194
- const config = raw ? toRecord14(YAML4.parse(raw)) : {};
21195
- const memory = toRecord14(config.memory);
22238
+ const config = raw ? toRecord16(YAML4.parse(raw)) : {};
22239
+ const memory = toRecord16(config.memory);
21196
22240
  return {
21197
22241
  memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
21198
22242
  user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
@@ -21248,10 +22292,10 @@ function hashString(value) {
21248
22292
  }
21249
22293
  return hash.toString(16);
21250
22294
  }
21251
- function toRecord14(value) {
22295
+ function toRecord16(value) {
21252
22296
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
21253
22297
  }
21254
- function readString15(value) {
22298
+ function readString17(value) {
21255
22299
  return typeof value === "string" && value.trim() ? value.trim() : null;
21256
22300
  }
21257
22301
  function readPositiveInteger3(value) {
@@ -21279,7 +22323,7 @@ function formatEnvValue3(value) {
21279
22323
  function escapeRegExp4(value) {
21280
22324
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
21281
22325
  }
21282
- function isNodeError16(error, code) {
22326
+ function isNodeError17(error, code) {
21283
22327
  return error instanceof Error && "code" in error && error.code === code;
21284
22328
  }
21285
22329
 
@@ -21410,7 +22454,7 @@ function registerProfileMemoryRoutes(router, options) {
21410
22454
  );
21411
22455
  }
21412
22456
  function readMemoryTarget(body) {
21413
- const raw = readString14(body, "target");
22457
+ const raw = readString16(body, "target");
21414
22458
  if (raw === "memory" || raw === "user") {
21415
22459
  return raw;
21416
22460
  }
@@ -21421,7 +22465,7 @@ function readMemoryTarget(body) {
21421
22465
  );
21422
22466
  }
21423
22467
  function readMemoryResetTarget(body) {
21424
- const raw = readString14(body, "target") ?? "all";
22468
+ const raw = readString16(body, "target") ?? "all";
21425
22469
  if (raw === "all" || raw === "memory" || raw === "user") {
21426
22470
  return raw;
21427
22471
  }
@@ -21432,7 +22476,7 @@ function readMemoryResetTarget(body) {
21432
22476
  );
21433
22477
  }
21434
22478
  function readRequiredMemoryContent(body) {
21435
- const content = readString14(body, "content") ?? readString14(body, "text");
22479
+ const content = readString16(body, "content") ?? readString16(body, "text");
21436
22480
  if (!content) {
21437
22481
  throw new LinkHttpError(
21438
22482
  400,
@@ -21443,7 +22487,7 @@ function readRequiredMemoryContent(body) {
21443
22487
  return content;
21444
22488
  }
21445
22489
  function readRequiredMemoryMatch(body) {
21446
- const oldText = readString14(body, "old_text") ?? readString14(body, "oldText") ?? readString14(body, "match");
22490
+ const oldText = readString16(body, "old_text") ?? readString16(body, "oldText") ?? readString16(body, "match");
21447
22491
  if (!oldText) {
21448
22492
  throw new LinkHttpError(
21449
22493
  400,
@@ -21454,7 +22498,7 @@ function readRequiredMemoryMatch(body) {
21454
22498
  return oldText;
21455
22499
  }
21456
22500
  function readRequiredMemoryProvider(body) {
21457
- const provider = readString14(body, "provider") ?? readString14(body, "provider_id") ?? readString14(body, "providerId");
22501
+ const provider = readString16(body, "provider") ?? readString16(body, "provider_id") ?? readString16(body, "providerId");
21458
22502
  if (!provider) {
21459
22503
  throw new LinkHttpError(
21460
22504
  400,
@@ -21466,7 +22510,7 @@ function readRequiredMemoryProvider(body) {
21466
22510
  }
21467
22511
  function readMemorySettingsPatch(body) {
21468
22512
  const input = {};
21469
- const mode = readString14(body, "mode");
22513
+ const mode = readString16(body, "mode");
21470
22514
  if (mode) {
21471
22515
  input.mode = mode;
21472
22516
  }
@@ -21482,7 +22526,7 @@ function readMemorySettingsPatch(body) {
21482
22526
  if (bankId !== void 0) {
21483
22527
  input.bankId = bankId;
21484
22528
  }
21485
- const llmProvider = readString14(body, "llm_provider") ?? readString14(body, "llmProvider");
22529
+ const llmProvider = readString16(body, "llm_provider") ?? readString16(body, "llmProvider");
21486
22530
  if (llmProvider) {
21487
22531
  input.llmProvider = llmProvider;
21488
22532
  }
@@ -21518,11 +22562,11 @@ function readMemorySettingsPatch(body) {
21518
22562
  if (autoRetain !== void 0) {
21519
22563
  input.autoRetain = autoRetain;
21520
22564
  }
21521
- const memoryMode = readString14(body, "memory_mode") ?? readString14(body, "memoryMode");
22565
+ const memoryMode = readString16(body, "memory_mode") ?? readString16(body, "memoryMode");
21522
22566
  if (memoryMode) {
21523
22567
  input.memoryMode = memoryMode;
21524
22568
  }
21525
- const recallBudget = readString14(body, "recall_budget") ?? readString14(body, "recallBudget");
22569
+ const recallBudget = readString16(body, "recall_budget") ?? readString16(body, "recallBudget");
21526
22570
  if (recallBudget) {
21527
22571
  input.recallBudget = recallBudget;
21528
22572
  }
@@ -21538,11 +22582,11 @@ function readMemorySettingsPatch(body) {
21538
22582
  if (profileFrequency !== void 0) {
21539
22583
  input.profileFrequency = profileFrequency;
21540
22584
  }
21541
- const captureMode = readString14(body, "capture_mode") ?? readString14(body, "captureMode");
22585
+ const captureMode = readString16(body, "capture_mode") ?? readString16(body, "captureMode");
21542
22586
  if (captureMode) {
21543
22587
  input.captureMode = captureMode;
21544
22588
  }
21545
- const searchMode = readString14(body, "search_mode") ?? readString14(body, "searchMode");
22589
+ const searchMode = readString16(body, "search_mode") ?? readString16(body, "searchMode");
21546
22590
  if (searchMode) {
21547
22591
  input.searchMode = searchMode;
21548
22592
  }
@@ -21576,11 +22620,11 @@ function readMemorySettingsPatch(body) {
21576
22620
  if (aiPeer !== void 0) {
21577
22621
  input.aiPeer = aiPeer;
21578
22622
  }
21579
- const recallMode = readString14(body, "recall_mode") ?? readString14(body, "recallMode");
22623
+ const recallMode = readString16(body, "recall_mode") ?? readString16(body, "recallMode");
21580
22624
  if (recallMode) {
21581
22625
  input.recallMode = recallMode;
21582
22626
  }
21583
- const writeFrequency = readString14(body, "write_frequency") ?? readString14(body, "writeFrequency");
22627
+ const writeFrequency = readString16(body, "write_frequency") ?? readString16(body, "writeFrequency");
21584
22628
  if (writeFrequency) {
21585
22629
  input.writeFrequency = writeFrequency;
21586
22630
  }
@@ -21588,7 +22632,7 @@ function readMemorySettingsPatch(body) {
21588
22632
  if (saveMessages !== void 0) {
21589
22633
  input.saveMessages = saveMessages;
21590
22634
  }
21591
- const sessionStrategy = readString14(body, "session_strategy") ?? readString14(body, "sessionStrategy");
22635
+ const sessionStrategy = readString16(body, "session_strategy") ?? readString16(body, "sessionStrategy");
21592
22636
  if (sessionStrategy) {
21593
22637
  input.sessionStrategy = sessionStrategy;
21594
22638
  }
@@ -21720,8 +22764,8 @@ function toMemoryHttpError(error) {
21720
22764
  }
21721
22765
 
21722
22766
  // src/hermes/skills.ts
21723
- import { readFile as readFile15, readdir as readdir11 } from "fs/promises";
21724
- import path22 from "path";
22767
+ import { readFile as readFile16, readdir as readdir11 } from "fs/promises";
22768
+ import path23 from "path";
21725
22769
  import YAML5 from "yaml";
21726
22770
  var HermesSkillNotFoundError = class extends Error {
21727
22771
  constructor(skillName) {
@@ -21735,7 +22779,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
21735
22779
  async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
21736
22780
  const profile = await readExistingProfile(profileName, paths);
21737
22781
  const profileDir = resolveHermesProfileDir(profile.name);
21738
- const skillsRoot = path22.join(profileDir, "skills");
22782
+ const skillsRoot = path23.join(profileDir, "skills");
21739
22783
  const [skillFiles, disabled, provenance] = await Promise.all([
21740
22784
  findSkillFiles(skillsRoot),
21741
22785
  readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
@@ -21814,7 +22858,7 @@ async function findSkillFiles(root) {
21814
22858
  async function collectSkillFiles(directory, results) {
21815
22859
  const entries = await readdir11(directory, { withFileTypes: true }).catch(
21816
22860
  (error) => {
21817
- if (isNodeError17(error, "ENOENT")) {
22861
+ if (isNodeError18(error, "ENOENT")) {
21818
22862
  return [];
21819
22863
  }
21820
22864
  throw error;
@@ -21826,7 +22870,7 @@ async function collectSkillFiles(directory, results) {
21826
22870
  if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
21827
22871
  continue;
21828
22872
  }
21829
- const entryPath = path22.join(directory, entry.name);
22873
+ const entryPath = path23.join(directory, entry.name);
21830
22874
  if (entry.isDirectory()) {
21831
22875
  await collectSkillFiles(entryPath, results);
21832
22876
  continue;
@@ -21837,9 +22881,9 @@ async function collectSkillFiles(directory, results) {
21837
22881
  }
21838
22882
  }
21839
22883
  async function readSkillMetadata(input) {
21840
- const raw = await readFile15(input.skillFile, "utf8").catch(
22884
+ const raw = await readFile16(input.skillFile, "utf8").catch(
21841
22885
  (error) => {
21842
- if (isNodeError17(error, "ENOENT") || isNodeError17(error, "EACCES")) {
22886
+ if (isNodeError18(error, "ENOENT") || isNodeError18(error, "EACCES")) {
21843
22887
  return null;
21844
22888
  }
21845
22889
  throw error;
@@ -21848,16 +22892,16 @@ async function readSkillMetadata(input) {
21848
22892
  if (raw === null) {
21849
22893
  return null;
21850
22894
  }
21851
- const skillDir = path22.dirname(input.skillFile);
22895
+ const skillDir = path23.dirname(input.skillFile);
21852
22896
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
21853
22897
  const name = normalizeSkillName(
21854
- readString16(frontmatter.name) ?? path22.basename(skillDir)
22898
+ readString18(frontmatter.name) ?? path23.basename(skillDir)
21855
22899
  );
21856
22900
  if (!name) {
21857
22901
  return null;
21858
22902
  }
21859
22903
  const description = normalizeDescription(
21860
- readString16(frontmatter.description) ?? firstBodyDescription(body)
22904
+ readString18(frontmatter.description) ?? firstBodyDescription(body)
21861
22905
  );
21862
22906
  const provenance = input.provenance.get(name) ?? {
21863
22907
  source: "local",
@@ -21870,7 +22914,7 @@ async function readSkillMetadata(input) {
21870
22914
  enabled: !input.disabled.has(name),
21871
22915
  source: provenance.source,
21872
22916
  trust: provenance.trust,
21873
- relativePath: path22.relative(input.skillsRoot, skillDir)
22917
+ relativePath: path23.relative(input.skillsRoot, skillDir)
21874
22918
  };
21875
22919
  }
21876
22920
  function parseSkillDocument(raw) {
@@ -21883,7 +22927,7 @@ function parseSkillDocument(raw) {
21883
22927
  }
21884
22928
  try {
21885
22929
  return {
21886
- frontmatter: toRecord15(YAML5.parse(match[1] ?? "")),
22930
+ frontmatter: toRecord17(YAML5.parse(match[1] ?? "")),
21887
22931
  body: content.slice(match[0].length)
21888
22932
  };
21889
22933
  } catch {
@@ -21891,8 +22935,8 @@ function parseSkillDocument(raw) {
21891
22935
  }
21892
22936
  }
21893
22937
  function categoryFromPath(skillsRoot, skillFile) {
21894
- const relative = path22.relative(skillsRoot, skillFile);
21895
- const parts = relative.split(path22.sep).filter(Boolean);
22938
+ const relative = path23.relative(skillsRoot, skillFile);
22939
+ const parts = relative.split(path23.sep).filter(Boolean);
21896
22940
  return parts.length >= 3 ? parts[0] : null;
21897
22941
  }
21898
22942
  function firstBodyDescription(body) {
@@ -21915,8 +22959,8 @@ function normalizeDescription(value) {
21915
22959
  return `${description.slice(0, MAX_DESCRIPTION_LENGTH - 3)}...`;
21916
22960
  }
21917
22961
  async function readDisabledSkillNames(configPath) {
21918
- const raw = await readFile15(configPath, "utf8").catch((error) => {
21919
- if (isNodeError17(error, "ENOENT")) {
22962
+ const raw = await readFile16(configPath, "utf8").catch((error) => {
22963
+ if (isNodeError18(error, "ENOENT")) {
21920
22964
  return "";
21921
22965
  }
21922
22966
  throw error;
@@ -21924,8 +22968,8 @@ async function readDisabledSkillNames(configPath) {
21924
22968
  if (!raw.trim()) {
21925
22969
  return /* @__PURE__ */ new Set();
21926
22970
  }
21927
- const config = toRecord15(YAML5.parse(raw));
21928
- const skills = toRecord15(config.skills);
22971
+ const config = toRecord17(YAML5.parse(raw));
22972
+ const skills = toRecord17(config.skills);
21929
22973
  return new Set(readStringList3(skills.disabled));
21930
22974
  }
21931
22975
  async function readSkillProvenance(root) {
@@ -21939,9 +22983,9 @@ async function readSkillProvenance(root) {
21939
22983
  return provenance;
21940
22984
  }
21941
22985
  async function readBundledSkillNames(root) {
21942
- const raw = await readFile15(path22.join(root, ".bundled_manifest"), "utf8").catch(
22986
+ const raw = await readFile16(path23.join(root, ".bundled_manifest"), "utf8").catch(
21943
22987
  (error) => {
21944
- if (isNodeError17(error, "ENOENT")) {
22988
+ if (isNodeError18(error, "ENOENT")) {
21945
22989
  return "";
21946
22990
  }
21947
22991
  throw error;
@@ -21962,9 +23006,9 @@ async function readBundledSkillNames(root) {
21962
23006
  return names;
21963
23007
  }
21964
23008
  async function readHubInstalledSkills(root) {
21965
- const raw = await readFile15(path22.join(root, ".hub", "lock.json"), "utf8").catch(
23009
+ const raw = await readFile16(path23.join(root, ".hub", "lock.json"), "utf8").catch(
21966
23010
  (error) => {
21967
- if (isNodeError17(error, "ENOENT")) {
23011
+ if (isNodeError18(error, "ENOENT")) {
21968
23012
  return "";
21969
23013
  }
21970
23014
  throw error;
@@ -21975,17 +23019,17 @@ async function readHubInstalledSkills(root) {
21975
23019
  }
21976
23020
  let lock;
21977
23021
  try {
21978
- lock = toRecord15(JSON.parse(raw));
23022
+ lock = toRecord17(JSON.parse(raw));
21979
23023
  } catch {
21980
23024
  return /* @__PURE__ */ new Map();
21981
23025
  }
21982
- const installed = toRecord15(lock.installed);
23026
+ const installed = toRecord17(lock.installed);
21983
23027
  const result = /* @__PURE__ */ new Map();
21984
23028
  for (const [name, rawEntry] of Object.entries(installed)) {
21985
- const entry = toRecord15(rawEntry);
23029
+ const entry = toRecord17(rawEntry);
21986
23030
  result.set(normalizeSkillName(name), {
21987
- source: readString16(entry.source) ?? "hub",
21988
- trust: readString16(entry.trust_level) ?? null
23031
+ source: readString18(entry.source) ?? "hub",
23032
+ trust: readString18(entry.trust_level) ?? null
21989
23033
  });
21990
23034
  }
21991
23035
  return result;
@@ -22034,9 +23078,9 @@ function compareCategoryNames(left, right) {
22034
23078
  return left.localeCompare(right);
22035
23079
  }
22036
23080
  async function readHermesConfigDocument2(configPath) {
22037
- const existingRaw = await readFile15(configPath, "utf8").catch(
23081
+ const existingRaw = await readFile16(configPath, "utf8").catch(
22038
23082
  (error) => {
22039
- if (isNodeError17(error, "ENOENT")) {
23083
+ if (isNodeError18(error, "ENOENT")) {
22040
23084
  return null;
22041
23085
  }
22042
23086
  throw error;
@@ -22045,7 +23089,7 @@ async function readHermesConfigDocument2(configPath) {
22045
23089
  const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
22046
23090
  return {
22047
23091
  document,
22048
- config: toRecord15(document.toJSON()),
23092
+ config: toRecord17(document.toJSON()),
22049
23093
  existingRaw
22050
23094
  };
22051
23095
  }
@@ -22069,21 +23113,21 @@ function readStringList3(value) {
22069
23113
  }
22070
23114
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
22071
23115
  }
22072
- function readString16(value) {
23116
+ function readString18(value) {
22073
23117
  return typeof value === "string" && value.trim() ? value.trim() : null;
22074
23118
  }
22075
- function toRecord15(value) {
23119
+ function toRecord17(value) {
22076
23120
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
22077
23121
  }
22078
23122
  function ensureRecord3(target, key) {
22079
- const current = toRecord15(target[key]);
23123
+ const current = toRecord17(target[key]);
22080
23124
  if (current === target[key]) {
22081
23125
  return current;
22082
23126
  }
22083
23127
  target[key] = current;
22084
23128
  return current;
22085
23129
  }
22086
- function isNodeError17(error, code) {
23130
+ function isNodeError18(error, code) {
22087
23131
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
22088
23132
  }
22089
23133
 
@@ -22382,7 +23426,7 @@ function registerRunRoutes(router, options) {
22382
23426
  router.post("/api/v1/runs", async (ctx) => {
22383
23427
  await authenticateRequest(ctx, paths);
22384
23428
  const body = await readJsonBody(ctx.req);
22385
- const input = readString14(body, "input");
23429
+ const input = readString16(body, "input");
22386
23430
  if (!input) {
22387
23431
  throw new LinkHttpError(400, "run_input_required", "input is required");
22388
23432
  }
@@ -22390,12 +23434,12 @@ function registerRunRoutes(router, options) {
22390
23434
  ctx.body = await createHermesRun(
22391
23435
  {
22392
23436
  input,
22393
- instructions: readString14(body, "instructions") ?? void 0,
23437
+ instructions: readString16(body, "instructions") ?? void 0,
22394
23438
  conversation_history: readConversationHistory(
22395
23439
  body.conversation_history ?? body.conversationHistory
22396
23440
  ),
22397
- session_id: readString14(body, "session_id") ?? readString14(body, "sessionId") ?? void 0,
22398
- session_key: readString14(body, "session_key") ?? readString14(body, "sessionKey") ?? void 0
23441
+ session_id: readString16(body, "session_id") ?? readString16(body, "sessionId") ?? void 0,
23442
+ session_key: readString16(body, "session_key") ?? readString16(body, "sessionKey") ?? void 0
22399
23443
  },
22400
23444
  { logger, profileName: readOptionalProfileName(body) }
22401
23445
  );
@@ -22566,8 +23610,8 @@ function readModelList(payload) {
22566
23610
  // src/hermes/updates.ts
22567
23611
  import { EventEmitter as EventEmitter3 } from "events";
22568
23612
  import { spawn as spawn3 } from "child_process";
22569
- import { mkdir as mkdir12, readFile as readFile16, rm as rm7 } from "fs/promises";
22570
- import path23 from "path";
23613
+ import { mkdir as mkdir12, readFile as readFile17, rm as rm7 } from "fs/promises";
23614
+ import path24 from "path";
22571
23615
  var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
22572
23616
  var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
22573
23617
  var RELEASE_FETCH_TIMEOUT_MS = 5e3;
@@ -22800,24 +23844,24 @@ async function readRemoteRelease(options, now) {
22800
23844
  }
22801
23845
  }
22802
23846
  function normalizeServerReleaseSnapshot(payload) {
22803
- const snapshot = toRecord16(payload);
23847
+ const snapshot = toRecord18(payload);
22804
23848
  const remote = toNullableRecord(snapshot.remote);
22805
23849
  return {
22806
23850
  remote: remote ? normalizeServerRelease(remote) : null,
22807
- cacheState: readString17(snapshot, "cache_state") ?? readString17(snapshot, "cacheState"),
22808
- issue: readString17(snapshot, "issue")
23851
+ cacheState: readString19(snapshot, "cache_state") ?? readString19(snapshot, "cacheState"),
23852
+ issue: readString19(snapshot, "issue")
22809
23853
  };
22810
23854
  }
22811
23855
  function normalizeServerRelease(payload) {
22812
- const tag = readString17(payload, "tag");
22813
- const name = readString17(payload, "name");
23856
+ const tag = readString19(payload, "tag");
23857
+ const name = readString19(payload, "name");
22814
23858
  return {
22815
- version: readString17(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
23859
+ version: readString19(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
22816
23860
  tag,
22817
23861
  name,
22818
- releaseUrl: readString17(payload, "releaseUrl") ?? readString17(payload, "release_url"),
22819
- publishedAt: readString17(payload, "publishedAt") ?? readString17(payload, "published_at"),
22820
- fetchedAt: readString17(payload, "fetchedAt") ?? readString17(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
23862
+ releaseUrl: readString19(payload, "releaseUrl") ?? readString19(payload, "release_url"),
23863
+ publishedAt: readString19(payload, "publishedAt") ?? readString19(payload, "published_at"),
23864
+ fetchedAt: readString19(payload, "fetchedAt") ?? readString19(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
22821
23865
  };
22822
23866
  }
22823
23867
  async function readReleaseCache(paths) {
@@ -22836,7 +23880,7 @@ async function writeUpdateState(paths, state) {
22836
23880
  await writeJsonFile(updateStatePath(paths), state);
22837
23881
  }
22838
23882
  async function readUpdateLogLines(paths) {
22839
- const raw = await readFile16(updateLogPath(paths), "utf8").catch(() => "");
23883
+ const raw = await readFile17(updateLogPath(paths), "utf8").catch(() => "");
22840
23884
  if (!raw.trim()) {
22841
23885
  return [];
22842
23886
  }
@@ -22845,13 +23889,13 @@ async function readUpdateLogLines(paths) {
22845
23889
  );
22846
23890
  }
22847
23891
  function releaseCachePath(paths) {
22848
- return path23.join(paths.indexesDir, "hermes-release-check.json");
23892
+ return path24.join(paths.indexesDir, "hermes-release-check.json");
22849
23893
  }
22850
23894
  function updateStatePath(paths) {
22851
- return path23.join(paths.runDir, "hermes-update-state.json");
23895
+ return path24.join(paths.runDir, "hermes-update-state.json");
22852
23896
  }
22853
23897
  function updateLogPath(paths) {
22854
- return path23.join(paths.logsDir, UPDATE_LOG_FILE);
23898
+ return path24.join(paths.logsDir, UPDATE_LOG_FILE);
22855
23899
  }
22856
23900
  async function clearUpdateLogFiles(paths) {
22857
23901
  const primary = updateLogPath(paths);
@@ -22889,7 +23933,7 @@ function compareSemver2(left, right) {
22889
23933
  }
22890
23934
  return 0;
22891
23935
  }
22892
- function toRecord16(value) {
23936
+ function toRecord18(value) {
22893
23937
  return typeof value === "object" && value !== null ? value : {};
22894
23938
  }
22895
23939
  function toNullableRecord(value) {
@@ -22943,7 +23987,7 @@ function isRecentRunningState2(state) {
22943
23987
  const startedAt = Date.parse(state.started_at);
22944
23988
  return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
22945
23989
  }
22946
- function readString17(payload, key) {
23990
+ function readString19(payload, key) {
22947
23991
  const value = payload[key];
22948
23992
  return typeof value === "string" && value.trim() ? value.trim() : null;
22949
23993
  }
@@ -22951,13 +23995,13 @@ function readString17(payload, key) {
22951
23995
  // src/link/updates.ts
22952
23996
  import { spawn as spawn5 } from "child_process";
22953
23997
  import { EventEmitter as EventEmitter4 } from "events";
22954
- import { mkdir as mkdir15, readFile as readFile18, rm as rm10 } from "fs/promises";
22955
- import path25 from "path";
23998
+ import { mkdir as mkdir15, readFile as readFile19, rm as rm10 } from "fs/promises";
23999
+ import path26 from "path";
22956
24000
 
22957
24001
  // src/daemon/process.ts
22958
24002
  import { spawn as spawn4 } from "child_process";
22959
- import { mkdir as mkdir14, readFile as readFile17, rm as rm9 } from "fs/promises";
22960
- import path24 from "path";
24003
+ import { mkdir as mkdir14, readFile as readFile18, rm as rm9 } from "fs/promises";
24004
+ import path25 from "path";
22961
24005
 
22962
24006
  // src/daemon/service.ts
22963
24007
  import { createServer } from "http";
@@ -22968,8 +24012,8 @@ import WebSocket from "ws";
22968
24012
 
22969
24013
  // src/relay/stream-policy.ts
22970
24014
  var DEFAULT_RELAY_STREAM_BATCH_POLICY = {
22971
- flushIntervalMs: 50,
22972
- flushBytes: 2 * 1024
24015
+ flushIntervalMs: 1e3,
24016
+ flushBytes: 4 * 1024
22973
24017
  };
22974
24018
  var RELAY_STREAM_POLICY_CONSTRAINTS = {
22975
24019
  flushIntervalMs: {
@@ -24291,7 +25335,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
24291
25335
  await mkdir14(paths.logsDir, { recursive: true, mode: 448 });
24292
25336
  const log = createRotatingTextLogWriter({
24293
25337
  paths,
24294
- fileName: path24.basename(daemonLogFile(paths))
25338
+ fileName: path25.basename(daemonLogFile(paths))
24295
25339
  });
24296
25340
  const scriptPath = currentCliScriptPath();
24297
25341
  const child = spawn4(process.execPath, [scriptPath, "daemon", "--foreground"], {
@@ -24420,7 +25464,7 @@ function currentCliScriptPath() {
24420
25464
  return process.argv[1];
24421
25465
  }
24422
25466
  async function readPid(filePath) {
24423
- const raw = await readFile17(filePath, "utf8").catch(() => null);
25467
+ const raw = await readFile18(filePath, "utf8").catch(() => null);
24424
25468
  if (!raw) {
24425
25469
  return null;
24426
25470
  }
@@ -24812,13 +25856,15 @@ async function writeFailedStartState(options, error, targetVersion = null) {
24812
25856
  return readLinkUpdateStatus(options.paths);
24813
25857
  }
24814
25858
  async function readRemoteLinkPolicy(options) {
24815
- const context = await readLinkReleaseCheckContext(options.paths).catch(
25859
+ const channel = normalizeLinkReleaseChannel(options.channel);
25860
+ const context = await readLinkReleaseCheckContext(options.paths, channel).catch(
24816
25861
  () => null
24817
25862
  );
24818
25863
  try {
24819
25864
  const response = await fetchCurrentLinkReleaseFromServer(
24820
25865
  options,
24821
- options.fetchImpl ?? fetch
25866
+ options.fetchImpl ?? fetch,
25867
+ channel
24822
25868
  );
24823
25869
  if (!response.ok) {
24824
25870
  throw new Error(`HermesPilot Server returned HTTP ${response.status}`);
@@ -24847,21 +25893,21 @@ async function readRemoteLinkPolicy(options) {
24847
25893
  }
24848
25894
  }
24849
25895
  function normalizeServerSnapshot(payload) {
24850
- const snapshot = toRecord17(payload);
25896
+ const snapshot = toRecord19(payload);
24851
25897
  const policy = toNullableRecord2(snapshot.policy);
24852
25898
  if (!policy) {
24853
25899
  return {
24854
25900
  remote: null,
24855
- issue: readString18(snapshot, "issue")
25901
+ issue: readString20(snapshot, "issue")
24856
25902
  };
24857
25903
  }
24858
25904
  const release = toNullableRecord2(snapshot.release);
24859
- const currentVersion = readString18(policy, "current_version") ?? readString18(policy, "currentVersion");
24860
- const minSafeVersion = readString18(policy, "min_safe_version") ?? readString18(policy, "minSafeVersion");
25905
+ const currentVersion = readString20(policy, "current_version") ?? readString20(policy, "currentVersion");
25906
+ const minSafeVersion = readString20(policy, "min_safe_version") ?? readString20(policy, "minSafeVersion");
24861
25907
  if (!currentVersion) {
24862
25908
  return {
24863
25909
  remote: null,
24864
- issue: readString18(snapshot, "issue")
25910
+ issue: readString20(snapshot, "issue")
24865
25911
  };
24866
25912
  }
24867
25913
  return {
@@ -24869,16 +25915,16 @@ function normalizeServerSnapshot(payload) {
24869
25915
  current_version: currentVersion,
24870
25916
  min_safe_version: minSafeVersion,
24871
25917
  target_version: currentVersion,
24872
- release_url: release ? readString18(release, "release_url") ?? readString18(release, "releaseUrl") : null,
24873
- published_at: release ? readString18(release, "published_at") ?? readString18(release, "publishedAt") : null
25918
+ release_url: release ? readString20(release, "release_url") ?? readString20(release, "releaseUrl") : null,
25919
+ published_at: release ? readString20(release, "published_at") ?? readString20(release, "publishedAt") : null
24874
25920
  },
24875
- issue: readString18(snapshot, "issue")
25921
+ issue: readString20(snapshot, "issue")
24876
25922
  };
24877
25923
  }
24878
- async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
25924
+ async function fetchCurrentLinkReleaseFromServer(options, fetcher, channel) {
24879
25925
  const config = await loadConfig(options.paths);
24880
25926
  const url = new URL(SERVER_LINK_CURRENT_RELEASE_PATH, config.serverBaseUrl);
24881
- url.searchParams.set("channel", "stable");
25927
+ url.searchParams.set("channel", channel);
24882
25928
  url.searchParams.set("lang", "en");
24883
25929
  const controller = new AbortController();
24884
25930
  const timer = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
@@ -25153,16 +26199,20 @@ function spawnInstallCommand(input) {
25153
26199
  shell: false
25154
26200
  });
25155
26201
  }
25156
- async function readLinkReleaseCheckContext(paths) {
26202
+ async function readLinkReleaseCheckContext(paths, channel) {
25157
26203
  const config = await loadConfig(paths);
25158
26204
  const url = new URL(SERVER_LINK_CURRENT_RELEASE_PATH, config.serverBaseUrl);
25159
- url.searchParams.set("channel", "stable");
26205
+ url.searchParams.set("channel", channel);
25160
26206
  url.searchParams.set("lang", "en");
25161
26207
  return {
25162
26208
  serverBaseUrl: config.serverBaseUrl,
25163
26209
  releaseCheckUrl: url.toString()
25164
26210
  };
25165
26211
  }
26212
+ function normalizeLinkReleaseChannel(value) {
26213
+ const channel = value?.trim().toLowerCase();
26214
+ return channel === "beta" || channel === "dev" ? channel : "stable";
26215
+ }
25166
26216
  function computeLinkUpdateState(localVersion, remote) {
25167
26217
  if (!remote?.current_version) {
25168
26218
  return "unknown";
@@ -25186,7 +26236,7 @@ async function writeUpdateState2(paths, state) {
25186
26236
  await writeJsonFile(updateStatePath2(paths), state);
25187
26237
  }
25188
26238
  async function readUpdateLogLines2(paths) {
25189
- const raw = await readFile18(updateLogPath2(paths), "utf8").catch(() => "");
26239
+ const raw = await readFile19(updateLogPath2(paths), "utf8").catch(() => "");
25190
26240
  if (!raw.trim()) {
25191
26241
  return [];
25192
26242
  }
@@ -25195,10 +26245,10 @@ async function readUpdateLogLines2(paths) {
25195
26245
  );
25196
26246
  }
25197
26247
  function updateStatePath2(paths) {
25198
- return path25.join(paths.runDir, "link-update-state.json");
26248
+ return path26.join(paths.runDir, "link-update-state.json");
25199
26249
  }
25200
26250
  function updateLogPath2(paths) {
25201
- return path25.join(paths.logsDir, UPDATE_LOG_FILE2);
26251
+ return path26.join(paths.logsDir, UPDATE_LOG_FILE2);
25202
26252
  }
25203
26253
  async function clearUpdateLogFiles2(paths) {
25204
26254
  const primary = updateLogPath2(paths);
@@ -25270,19 +26320,19 @@ function isProcessAlive4(pid) {
25270
26320
  return false;
25271
26321
  }
25272
26322
  }
25273
- function toRecord17(value) {
26323
+ function toRecord19(value) {
25274
26324
  return typeof value === "object" && value !== null ? value : {};
25275
26325
  }
25276
26326
  function toNullableRecord2(value) {
25277
26327
  return typeof value === "object" && value !== null ? value : null;
25278
26328
  }
25279
- function readString18(payload, key) {
26329
+ function readString20(payload, key) {
25280
26330
  const value = payload[key];
25281
26331
  return typeof value === "string" && value.trim() ? value.trim() : null;
25282
26332
  }
25283
26333
 
25284
26334
  // src/pairing/pairing.ts
25285
- import path26 from "path";
26335
+ import path27 from "path";
25286
26336
  import { rm as rm11 } from "fs/promises";
25287
26337
 
25288
26338
  // src/relay/bootstrap.ts
@@ -25622,10 +26672,10 @@ async function loadRequiredIdentity2(paths) {
25622
26672
  }
25623
26673
  return identity;
25624
26674
  }
25625
- async function postServerJson(serverBaseUrl, path27, body, options) {
26675
+ async function postServerJson(serverBaseUrl, path28, body, options) {
25626
26676
  let response;
25627
26677
  try {
25628
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path27}`, {
26678
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path28}`, {
25629
26679
  method: "POST",
25630
26680
  headers: {
25631
26681
  accept: "application/json",
@@ -25673,10 +26723,10 @@ function pairingErrorSnapshot(stage, error) {
25673
26723
  occurred_at: (/* @__PURE__ */ new Date()).toISOString()
25674
26724
  };
25675
26725
  }
25676
- async function patchServerJson(serverBaseUrl, path27, token, body, options) {
26726
+ async function patchServerJson(serverBaseUrl, path28, token, body, options) {
25677
26727
  let response;
25678
26728
  try {
25679
- response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path27}`, {
26729
+ response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path28}`, {
25680
26730
  method: "PATCH",
25681
26731
  headers: {
25682
26732
  accept: "application/json",
@@ -25724,10 +26774,10 @@ function createPairingNetworkError(input) {
25724
26774
  );
25725
26775
  }
25726
26776
  function pairingClaimPath(sessionId, paths) {
25727
- return path26.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
26777
+ return path27.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
25728
26778
  }
25729
26779
  function pairingSessionPath(sessionId, paths) {
25730
- return path26.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
26780
+ return path27.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
25731
26781
  }
25732
26782
  function qrPreferredUrls(routes) {
25733
26783
  return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
@@ -25803,8 +26853,8 @@ function registerSystemRoutes(router, options) {
25803
26853
  });
25804
26854
  router.post("/api/v1/pairing/claim", async (ctx) => {
25805
26855
  const body = await readJsonBody(ctx.req);
25806
- const sessionId = readString14(body, "session_id") ?? readString14(body, "sessionId");
25807
- const claimToken = readString14(body, "claim_token") ?? readString14(body, "claimToken");
26856
+ const sessionId = readString16(body, "session_id") ?? readString16(body, "sessionId");
26857
+ const claimToken = readString16(body, "claim_token") ?? readString16(body, "claimToken");
25808
26858
  if (!sessionId || !claimToken) {
25809
26859
  throw new LinkHttpError(
25810
26860
  400,
@@ -25815,10 +26865,10 @@ function registerSystemRoutes(router, options) {
25815
26865
  const claimed = await claimPairing({
25816
26866
  sessionId,
25817
26867
  claimToken,
25818
- deviceLabel: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
25819
- devicePlatform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
25820
- deviceModel: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
25821
- appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
26868
+ deviceLabel: readString16(body, "device_label") ?? readString16(body, "deviceLabel") ?? "HermesPilot App",
26869
+ devicePlatform: readString16(body, "device_platform") ?? readString16(body, "devicePlatform") ?? "unknown",
26870
+ deviceModel: readString16(body, "device_model") ?? readString16(body, "deviceModel"),
26871
+ appInstanceId: readString16(body, "app_instance_id") ?? readString16(body, "appInstanceId"),
25822
26872
  paths
25823
26873
  });
25824
26874
  ctx.body = claimed;
@@ -25893,9 +26943,9 @@ function registerSystemRoutes(router, options) {
25893
26943
  const body = await readJsonBody(ctx.req);
25894
26944
  const session = await createDeviceSession(
25895
26945
  {
25896
- label: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
25897
- platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
25898
- model: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
26946
+ label: readString16(body, "device_label") ?? readString16(body, "deviceLabel") ?? "HermesPilot App",
26947
+ platform: readString16(body, "device_platform") ?? readString16(body, "devicePlatform") ?? "unknown",
26948
+ model: readString16(body, "device_model") ?? readString16(body, "deviceModel"),
25899
26949
  appInstanceId: auth.appInstanceId
25900
26950
  },
25901
26951
  paths
@@ -25924,7 +26974,7 @@ function registerSystemRoutes(router, options) {
25924
26974
  });
25925
26975
  router.post("/api/v1/auth/refresh", async (ctx) => {
25926
26976
  const body = await readJsonBody(ctx.req);
25927
- const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
26977
+ const refreshToken = readString16(body, "refresh_token") ?? readString16(body, "refreshToken");
25928
26978
  if (!refreshToken) {
25929
26979
  throw new LinkHttpError(
25930
26980
  400,
@@ -25935,10 +26985,10 @@ function registerSystemRoutes(router, options) {
25935
26985
  const session = await refreshDeviceSession(
25936
26986
  refreshToken,
25937
26987
  {
25938
- appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
25939
- label: readString14(body, "device_label") ?? readString14(body, "deviceLabel"),
25940
- platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform"),
25941
- model: readString14(body, "device_model") ?? readString14(body, "deviceModel")
26988
+ appInstanceId: readString16(body, "app_instance_id") ?? readString16(body, "appInstanceId"),
26989
+ label: readString16(body, "device_label") ?? readString16(body, "deviceLabel"),
26990
+ platform: readString16(body, "device_platform") ?? readString16(body, "devicePlatform"),
26991
+ model: readString16(body, "device_model") ?? readString16(body, "deviceModel")
25942
26992
  },
25943
26993
  paths
25944
26994
  );
@@ -25957,7 +27007,7 @@ function registerSystemRoutes(router, options) {
25957
27007
  });
25958
27008
  router.post("/api/v1/auth/logout", async (ctx) => {
25959
27009
  const body = await readJsonBody(ctx.req);
25960
- const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
27010
+ const refreshToken = readString16(body, "refresh_token") ?? readString16(body, "refreshToken");
25961
27011
  if (refreshToken) {
25962
27012
  await revokeDeviceRefreshToken(refreshToken, paths);
25963
27013
  }
@@ -25966,6 +27016,7 @@ function registerSystemRoutes(router, options) {
25966
27016
  router.get("/api/v1/status", async (ctx) => {
25967
27017
  await authenticateRequest(ctx, paths);
25968
27018
  ctx.set("cache-control", "no-store");
27019
+ const linkUpdateChannel = readQueryString(ctx.query.link_update_channel) ?? readQueryString(ctx.query.channel);
25969
27020
  const [
25970
27021
  identity,
25971
27022
  config,
@@ -26010,22 +27061,24 @@ function registerSystemRoutes(router, options) {
26010
27061
  lines: [],
26011
27062
  error: error instanceof Error ? error.message : String(error)
26012
27063
  })),
26013
- readLinkUpdateCheck({ paths, logger }).catch((error) => ({
26014
- ok: true,
26015
- local: { version: LINK_VERSION, raw: LINK_VERSION },
26016
- remote: null,
26017
- state: "unknown",
26018
- update_available: false,
26019
- unsafe: false,
26020
- blocked: false,
26021
- check_state: "unavailable",
26022
- issue: error instanceof Error ? error.message : String(error),
26023
- manual: {
26024
- command: null,
26025
- package: "@hermespilot/link",
26026
- version: null
26027
- }
26028
- })),
27064
+ readLinkUpdateCheck({ paths, logger, channel: linkUpdateChannel }).catch(
27065
+ (error) => ({
27066
+ ok: true,
27067
+ local: { version: LINK_VERSION, raw: LINK_VERSION },
27068
+ remote: null,
27069
+ state: "unknown",
27070
+ update_available: false,
27071
+ unsafe: false,
27072
+ blocked: false,
27073
+ check_state: "unavailable",
27074
+ issue: error instanceof Error ? error.message : String(error),
27075
+ manual: {
27076
+ command: null,
27077
+ package: "@hermespilot/link",
27078
+ version: null
27079
+ }
27080
+ })
27081
+ ),
26029
27082
  readLinkUsageStatistics(paths, { days: 7 }).catch(() => null),
26030
27083
  readRecentGatewayLogEntries({ paths, limit: 1e3 }).catch(() => [])
26031
27084
  ]);
@@ -26180,7 +27233,7 @@ function registerSystemRoutes(router, options) {
26180
27233
  router.patch("/api/v1/devices/:deviceId", async (ctx) => {
26181
27234
  const auth = await authenticateRequest(ctx, paths);
26182
27235
  const body = await readJsonBody(ctx.req);
26183
- const label = readString14(body, "label") ?? readString14(body, "device_label");
27236
+ const label = readString16(body, "label") ?? readString16(body, "device_label");
26184
27237
  if (!label) {
26185
27238
  throw new LinkHttpError(
26186
27239
  400,
@@ -26224,7 +27277,7 @@ function isActiveCronJob(job) {
26224
27277
  if (!enabled) {
26225
27278
  return false;
26226
27279
  }
26227
- const state = readString14(job, "state")?.toLowerCase();
27280
+ const state = readString16(job, "state")?.toLowerCase();
26228
27281
  return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
26229
27282
  }
26230
27283
  function filterLogsWithinHours(logs, hours, now = Date.now()) {
@@ -26300,7 +27353,11 @@ function registerLinkUpdateRoutes(router, options) {
26300
27353
  router.get("/api/v1/link/update-check", async (ctx) => {
26301
27354
  await authenticateRequest(ctx, paths);
26302
27355
  ctx.set("cache-control", "no-store");
26303
- ctx.body = await readLinkUpdateCheck({ paths, logger });
27356
+ ctx.body = await readLinkUpdateCheck({
27357
+ paths,
27358
+ logger,
27359
+ channel: readQueryString(ctx.query.channel)
27360
+ });
26304
27361
  });
26305
27362
  router.get("/api/v1/link/update/status", async (ctx) => {
26306
27363
  await authenticateRequest(ctx, paths);
@@ -26314,7 +27371,8 @@ function registerLinkUpdateRoutes(router, options) {
26314
27371
  ctx.body = await startLinkUpdate({
26315
27372
  paths,
26316
27373
  logger,
26317
- targetVersion: readString14(body, "target_version") ?? readString14(body, "targetVersion")
27374
+ channel: readString16(body, "channel"),
27375
+ targetVersion: readString16(body, "target_version") ?? readString16(body, "targetVersion")
26318
27376
  });
26319
27377
  });
26320
27378
  router.get("/api/v1/link/update/events", async (ctx) => {
@@ -26344,7 +27402,7 @@ import QRCode from "qrcode";
26344
27402
  function registerPairingRoutes(router, options) {
26345
27403
  const { paths } = options;
26346
27404
  router.get("/pair", async (ctx) => {
26347
- const sessionId = readString14(ctx.query, "session_id");
27405
+ const sessionId = readString16(ctx.query, "session_id");
26348
27406
  if (!sessionId) {
26349
27407
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
26350
27408
  }
@@ -26369,7 +27427,7 @@ function registerPairingRoutes(router, options) {
26369
27427
  ctx.body = page;
26370
27428
  });
26371
27429
  router.get("/api/v1/pairing/session", async (ctx) => {
26372
- const sessionId = readString14(ctx.query, "session_id");
27430
+ const sessionId = readString16(ctx.query, "session_id");
26373
27431
  if (!sessionId) {
26374
27432
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
26375
27433
  }
@@ -26820,7 +27878,7 @@ function registerInternalRoutes(router, options) {
26820
27878
  router.post("/internal/deliver", async (ctx) => {
26821
27879
  assertLoopbackRequest(ctx.req);
26822
27880
  const body = await readJsonBody(ctx.req);
26823
- const stagingDir = readString14(body, "staging_dir") ?? readString14(body, "stagingDir");
27881
+ const stagingDir = readString16(body, "staging_dir") ?? readString16(body, "stagingDir");
26824
27882
  if (!stagingDir) {
26825
27883
  throw new LinkHttpError(
26826
27884
  400,