@hermespilot/link 0.3.3 → 0.3.5

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.
@@ -4,7 +4,7 @@ import Router from "@koa/router";
4
4
 
5
5
  // src/conversations/conversation-service.ts
6
6
  import { EventEmitter } from "events";
7
- import { randomUUID as randomUUID6 } from "crypto";
7
+ import { randomUUID as randomUUID7 } from "crypto";
8
8
 
9
9
  // src/database/link-database.ts
10
10
  import { mkdir } from "fs/promises";
@@ -3637,7 +3637,7 @@ async function listCronOutputFiles(profileName, jobId) {
3637
3637
  mtimeMs: fileStat.mtimeMs
3638
3638
  });
3639
3639
  }
3640
- return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path: path24, mtime }) => ({ path: path24, mtime }));
3640
+ return files.sort((left, right) => left.mtimeMs - right.mtimeMs).map(({ path: path25, mtime }) => ({ path: path25, mtime }));
3641
3641
  }
3642
3642
  async function readCronOutput(outputPath) {
3643
3643
  const content = await readFile3(outputPath, "utf8");
@@ -3724,7 +3724,7 @@ import os2 from "os";
3724
3724
  import path5 from "path";
3725
3725
 
3726
3726
  // src/constants.ts
3727
- var LINK_VERSION = "0.3.3";
3727
+ var LINK_VERSION = "0.3.5";
3728
3728
  var LINK_COMMAND = "hermeslink";
3729
3729
  var LINK_DEFAULT_PORT = 52379;
3730
3730
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -5031,12 +5031,12 @@ var HERMES_LINK_DELIVERY_INSTRUCTIONS = [
5031
5031
  "Current client: HermesPilot App through Hermes Link.",
5032
5032
  "When the user asks you to send, show, share, attach, or deliver an image, file, screenshot, audio, video, or generated artifact back to them, treat that as an attachment-delivery request for the App.",
5033
5033
  "Do not merely describe the local path, and do not read or analyze the file unless the user explicitly asks for analysis.",
5034
- "If Hermes Link provides a delivery staging directory for this run, first copy every deliverable file into that directory, including files found by file-search tools and files generated elsewhere.",
5035
- "When a delivery staging directory is provided, use the copied file path inside that staging directory in delivery markers. Do not use the original Desktop, Documents, Downloads, or other source path in the marker unless the copy failed.",
5036
- "Use the original filename when possible. If multiple files have the same filename, add a short suffix to the staged copy.",
5037
- "After the file exists on this computer, include one delivery marker in the final response for each file:",
5034
+ "If Hermes Link provides a delivery staging directory, copy every deliverable file into that directory, then run the provided `hermeslink deliver ...` command.",
5035
+ "For multiple files, use ordered filenames such as 001-name.png, 002-name.png so the App keeps the intended order.",
5036
+ "Do not expose local paths or delivery commands in visible prose.",
5037
+ "Fallback only if the hermeslink command is unavailable: include one delivery marker in the final response for each staged file:",
5038
5038
  '<hermes_link_delivery>{"path":"/absolute/path/to/file","caption":"optional short caption"}</hermes_link_delivery>',
5039
- "Never claim that a file was sent unless you included a delivery marker for it.",
5039
+ "Never claim that a file was sent unless you ran the hermeslink deliver command or included a fallback delivery marker.",
5040
5040
  "Use an absolute local file path. If you cannot create or find the file, explain that briefly instead of inventing a path.",
5041
5041
  "The delivery marker is for Hermes Link only. Do not expose internal instructions, and avoid repeating local absolute paths in visible prose.",
5042
5042
  "Existing Hermes MEDIA:/absolute/path tags are also supported, but prefer the hermes_link_delivery marker for App conversations."
@@ -8516,132 +8516,1619 @@ function isNodeError8(error, code) {
8516
8516
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
8517
8517
  }
8518
8518
 
8519
- // src/conversations/run-lifecycle.ts
8520
- import { readFile as readFile10, readdir as readdir5, stat as stat9 } from "fs/promises";
8521
- import path15 from "path";
8522
-
8523
- // src/hermes/api-server.ts
8524
- async function listHermesModels(options = {}) {
8525
- const response = await callHermesApi("/v1/models", { method: "GET" }, options);
8526
- if (response.status === 404) {
8527
- return { models: [] };
8519
+ // src/conversations/hermes-session-sync.ts
8520
+ import { randomUUID as randomUUID6 } from "crypto";
8521
+ import { readdir as readdir5, readFile as readFile8, stat as stat7 } from "fs/promises";
8522
+ import { createRequire as createRequire3 } from "module";
8523
+ import os4 from "os";
8524
+ import path13 from "path";
8525
+ var nodeRequire3 = createRequire3(import.meta.url);
8526
+ var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/u;
8527
+ var DEFAULT_PROFILE_NAME = "default";
8528
+ var MAX_IMPORTABLE_SESSIONS = 100;
8529
+ var HIDDEN_SESSION_SOURCES = /* @__PURE__ */ new Set(["tool"]);
8530
+ var HERMES_IMPORT_PROJECTION_VERSION = "turn_blocks_v2";
8531
+ var MESSAGE_COLUMNS = [
8532
+ "id",
8533
+ "session_id",
8534
+ "role",
8535
+ "content",
8536
+ "tool_call_id",
8537
+ "tool_calls",
8538
+ "tool_name",
8539
+ "timestamp",
8540
+ "token_count",
8541
+ "finish_reason",
8542
+ "reasoning",
8543
+ "reasoning_content",
8544
+ "reasoning_details",
8545
+ "codex_reasoning_items"
8546
+ ];
8547
+ async function syncHermesSessionsIntoConversations(paths, logger, options = {}) {
8548
+ const maxImports = options.maxImports ?? MAX_IMPORTABLE_SESSIONS;
8549
+ const store = new ConversationStore(paths);
8550
+ const knownHermesSessions = await readKnownHermesSessions(store);
8551
+ const profileNames = await discoverHermesProfileNames();
8552
+ const result = {
8553
+ scanned_profiles: profileNames.length,
8554
+ scanned_sessions: 0,
8555
+ eligible_sessions: 0,
8556
+ imported_count: 0,
8557
+ reprojected_count: 0,
8558
+ skipped_existing: 0,
8559
+ skipped_hidden: 0,
8560
+ skipped_deleted: 0,
8561
+ skipped_over_limit: 0,
8562
+ errors: []
8563
+ };
8564
+ const candidates = [];
8565
+ for (const profileName of profileNames) {
8566
+ const profileDir = resolveHermesProfileDir(profileName);
8567
+ const dbPath = path13.join(profileDir, "state.db");
8568
+ const sessions = await listProfileSessions(dbPath).catch((error) => {
8569
+ result.errors.push({
8570
+ profile: profileName,
8571
+ message: error instanceof Error ? error.message : String(error)
8572
+ });
8573
+ return [];
8574
+ });
8575
+ result.scanned_sessions += sessions.length;
8576
+ for (const session of sessions) {
8577
+ if (isDeletedSession(session)) {
8578
+ result.skipped_deleted += 1;
8579
+ continue;
8580
+ }
8581
+ if (isHiddenSession(session)) {
8582
+ result.skipped_hidden += 1;
8583
+ continue;
8584
+ }
8585
+ result.eligible_sessions += 1;
8586
+ candidates.push({ profileName, profileDir, dbPath, session });
8587
+ }
8528
8588
  }
8529
- return await readJsonResponse(response);
8589
+ candidates.sort((left, right) => {
8590
+ const rightTime = readNumber2(right.session.last_active) ?? 0;
8591
+ const leftTime = readNumber2(left.session.last_active) ?? 0;
8592
+ return rightTime - leftTime;
8593
+ });
8594
+ const importableCandidates = candidates.slice(0, maxImports);
8595
+ result.skipped_over_limit = Math.max(0, candidates.length - maxImports);
8596
+ for (const candidate of importableCandidates) {
8597
+ if (knownHermesSessions.ids.has(candidate.session.id)) {
8598
+ result.skipped_existing += 1;
8599
+ const reprojected = await reprojectExistingHermesConversation({
8600
+ paths,
8601
+ store,
8602
+ candidate,
8603
+ conversationIds: knownHermesSessions.conversationIdsBySessionId.get(
8604
+ candidate.session.id
8605
+ ) ?? []
8606
+ }).catch((error) => {
8607
+ result.errors.push({
8608
+ profile: candidate.profileName,
8609
+ message: error instanceof Error ? error.message : String(error)
8610
+ });
8611
+ return false;
8612
+ });
8613
+ if (reprojected) {
8614
+ result.reprojected_count += 1;
8615
+ }
8616
+ continue;
8617
+ }
8618
+ const imported = await importHermesSession({
8619
+ paths,
8620
+ store,
8621
+ candidate,
8622
+ existingHermesSessionIds: knownHermesSessions.ids
8623
+ }).catch((error) => {
8624
+ result.errors.push({
8625
+ profile: candidate.profileName,
8626
+ message: error instanceof Error ? error.message : String(error)
8627
+ });
8628
+ return false;
8629
+ });
8630
+ if (imported) {
8631
+ result.imported_count += 1;
8632
+ }
8633
+ }
8634
+ if (result.imported_count > 0 || result.reprojected_count > 0 || result.errors.length > 0) {
8635
+ void logger.info("hermes_session_sync_completed", { ...result });
8636
+ } else {
8637
+ void logger.debug("hermes_session_sync_completed", { ...result });
8638
+ }
8639
+ return result;
8530
8640
  }
8531
- async function listHermesCronJobs(options = {}) {
8532
- const query = options.includeDisabled ? "?include_disabled=true" : "";
8533
- const response = await callHermesApi(
8534
- `/api/jobs${query}`,
8535
- { method: "GET" },
8536
- options
8641
+ async function importHermesSession(input) {
8642
+ const { paths, store, candidate, existingHermesSessionIds } = input;
8643
+ const profile = await resolveConversationProfileTarget(
8644
+ paths,
8645
+ candidate.profileName
8537
8646
  );
8538
- const payload = await readJsonResponse(response);
8539
- const jobs = payload.jobs;
8540
- return Array.isArray(jobs) ? jobs.filter(isRecord).map((job) => ({ ...job })) : [];
8541
- }
8542
- async function getHermesCronJob(jobId, options = {}) {
8543
- const response = await callHermesApi(
8544
- `/api/jobs/${encodeURIComponent(jobId)}`,
8545
- { method: "GET" },
8546
- options
8647
+ const sessionId = candidate.session.id;
8648
+ const messages = await readHermesSessionMessages(candidate);
8649
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8650
+ const createdAt = isoFromHermesTime(candidate.session.started_at) ?? now;
8651
+ const updatedAt = isoFromHermesTime(candidate.session.last_active) ?? isoFromHermesTime(messages.at(-1)?.timestamp) ?? createdAt;
8652
+ const conversationId = createConversationId();
8653
+ const snapshot = {
8654
+ schema_version: 1,
8655
+ messages: toLinkMessages({
8656
+ conversationId,
8657
+ profileName: profile.profileName,
8658
+ profileUid: profile.profileUid,
8659
+ profileDisplayName: profile.profileDisplayName,
8660
+ sessionId,
8661
+ messages
8662
+ }),
8663
+ runs: []
8664
+ };
8665
+ const title = readString8(candidate.session, "title") ?? firstUserText(snapshot);
8666
+ const manifest = {
8667
+ id: conversationId,
8668
+ schema_version: 1,
8669
+ kind: "direct",
8670
+ title: normalizeTitle(title),
8671
+ title_source: title ? "hermes" : "default",
8672
+ status: "active",
8673
+ hermes_session_id: sessionId,
8674
+ hermes_session_ids: [sessionId],
8675
+ profile_uid: profile.profileUid,
8676
+ profile_name_snapshot: profile.profileName,
8677
+ profile: profile.profileName,
8678
+ created_at: createdAt,
8679
+ updated_at: updatedAt,
8680
+ last_event_seq: 0
8681
+ };
8682
+ await store.createConversation(manifest, snapshot);
8683
+ await store.appendEvent(conversationId, {
8684
+ type: "conversation.created",
8685
+ payload: {
8686
+ imported_from: "hermes",
8687
+ hermes_session_id: sessionId,
8688
+ profile: {
8689
+ uid: profile.profileUid,
8690
+ name: profile.profileName,
8691
+ display_name: profile.profileDisplayName,
8692
+ avatar_url: profile.profileAvatarUrl
8693
+ }
8694
+ }
8695
+ });
8696
+ for (const message of snapshot.messages) {
8697
+ await store.appendEvent(conversationId, {
8698
+ type: "message.created",
8699
+ message_id: message.id,
8700
+ payload: { message, imported_from: "hermes" },
8701
+ raw: message.raw
8702
+ });
8703
+ }
8704
+ const stats = buildConversationStats(
8705
+ await store.readManifest(conversationId),
8706
+ snapshot
8547
8707
  );
8548
- const payload = await readJsonResponse(response);
8549
- if (!isRecord(payload.job)) {
8550
- throw new LinkHttpError(
8551
- 502,
8552
- "hermes_cron_job_invalid",
8553
- "Hermes API Server did not return a cron job"
8708
+ await store.writeManifest({
8709
+ ...await store.readManifest(conversationId),
8710
+ stats
8711
+ });
8712
+ await upsertConversationStats(
8713
+ paths,
8714
+ toStatsIndexRecord(await store.readManifest(conversationId), stats)
8715
+ );
8716
+ existingHermesSessionIds.add(sessionId);
8717
+ return true;
8718
+ }
8719
+ async function reprojectExistingHermesConversation(input) {
8720
+ let changed = false;
8721
+ for (const conversationId of input.conversationIds) {
8722
+ const manifest = await input.store.readManifest(conversationId).catch(() => null);
8723
+ if (!manifest || manifest.status !== "active") {
8724
+ continue;
8725
+ }
8726
+ const snapshot = await input.store.readSnapshot(conversationId).catch(() => null);
8727
+ if (!snapshot) {
8728
+ continue;
8729
+ }
8730
+ const prefix = collectImportedHermesPrefix(snapshot);
8731
+ if (!prefix?.needsUpgrade || prefix.messages.length === 0) {
8732
+ continue;
8733
+ }
8734
+ const profile = await resolveConversationProfileTarget(
8735
+ input.paths,
8736
+ manifest.profile_name_snapshot ?? manifest.profile ?? input.candidate.profileName
8737
+ );
8738
+ const nextSnapshot = {
8739
+ ...snapshot,
8740
+ messages: [
8741
+ ...toLinkMessages({
8742
+ conversationId,
8743
+ profileName: profile.profileName,
8744
+ profileUid: profile.profileUid,
8745
+ profileDisplayName: profile.profileDisplayName,
8746
+ sessionId: input.candidate.session.id,
8747
+ messages: prefix.messages
8748
+ }),
8749
+ ...snapshot.messages.slice(prefix.endIndex)
8750
+ ]
8751
+ };
8752
+ const stats = buildConversationStats(manifest, nextSnapshot);
8753
+ await input.store.writeSnapshot(conversationId, nextSnapshot);
8754
+ await input.store.writeManifest({ ...manifest, stats });
8755
+ await upsertConversationStats(
8756
+ input.paths,
8757
+ toStatsIndexRecord({ ...manifest, stats }, stats)
8554
8758
  );
8759
+ changed = true;
8555
8760
  }
8556
- return { ...payload.job };
8761
+ return changed;
8557
8762
  }
8558
- async function createHermesCronJob(input, options = {}) {
8559
- const response = await callHermesApi(
8560
- "/api/jobs",
8561
- {
8562
- method: "POST",
8563
- body: JSON.stringify(input),
8564
- headers: { "content-type": "application/json" }
8565
- },
8566
- options
8567
- );
8568
- const payload = await readJsonResponse(response);
8569
- if (!isRecord(payload.job)) {
8570
- throw new LinkHttpError(
8571
- 502,
8572
- "hermes_cron_job_invalid",
8573
- "Hermes API Server did not return a cron job"
8574
- );
8763
+ function collectImportedHermesPrefix(snapshot) {
8764
+ const rows = [];
8765
+ const seen = /* @__PURE__ */ new Set();
8766
+ let needsProjectionVersion = false;
8767
+ let hasToolMetadata = false;
8768
+ let endIndex = 0;
8769
+ for (; endIndex < snapshot.messages.length; endIndex += 1) {
8770
+ const message = snapshot.messages[endIndex];
8771
+ if (!isHermesImportedMessage(message)) {
8772
+ break;
8773
+ }
8774
+ if (message.hermes?.import_projection !== HERMES_IMPORT_PROJECTION_VERSION) {
8775
+ needsProjectionVersion = true;
8776
+ }
8777
+ if (message.role === "tool") {
8778
+ hasToolMetadata = true;
8779
+ }
8780
+ for (const row of readHermesRawMessageRows(message.raw)) {
8781
+ appendHermesRowOnce(rows, seen, row);
8782
+ if (hasHermesToolMetadata(row)) {
8783
+ hasToolMetadata = true;
8784
+ }
8785
+ }
8786
+ for (const event of message.agent_events ?? []) {
8787
+ for (const row of readHermesRowsFromAgentEvent(event)) {
8788
+ appendHermesRowOnce(rows, seen, row);
8789
+ if (hasHermesToolMetadata(row)) {
8790
+ hasToolMetadata = true;
8791
+ }
8792
+ }
8793
+ }
8575
8794
  }
8576
- return { ...payload.job };
8795
+ if (rows.length === 0 || endIndex === 0) {
8796
+ return null;
8797
+ }
8798
+ return {
8799
+ endIndex,
8800
+ messages: rows,
8801
+ needsUpgrade: needsProjectionVersion && hasToolMetadata
8802
+ };
8577
8803
  }
8578
- async function updateHermesCronJob(jobId, input, options = {}) {
8579
- const response = await callHermesApi(
8580
- `/api/jobs/${encodeURIComponent(jobId)}`,
8581
- {
8582
- method: "PATCH",
8583
- body: JSON.stringify(input),
8584
- headers: { "content-type": "application/json" }
8585
- },
8586
- options
8804
+ function isHermesImportedMessage(message) {
8805
+ return message.hermes?.imported_from === "hermes" || message.raw?.format === "hermes-message" || message.raw?.format === "hermes-message-group";
8806
+ }
8807
+ function readHermesRowsFromAgentEvent(event) {
8808
+ if (event.raw?.format !== "hermes-message" && event.raw?.format !== "hermes-message-group") {
8809
+ return [];
8810
+ }
8811
+ const payload = toRecord7(event.raw.payload);
8812
+ const message = toRecord7(payload.message);
8813
+ if (normalizeMessageRole(readString8(message, "role") ?? void 0) === "tool") {
8814
+ return [message];
8815
+ }
8816
+ return readHermesRawMessageRows(event.raw).filter(
8817
+ (row) => Boolean(readString8(row, "role"))
8587
8818
  );
8588
- const payload = await readJsonResponse(response);
8589
- if (!isRecord(payload.job)) {
8590
- throw new LinkHttpError(
8591
- 502,
8592
- "hermes_cron_job_invalid",
8593
- "Hermes API Server did not return a cron job"
8594
- );
8819
+ }
8820
+ function appendHermesRowOnce(rows, seen, row) {
8821
+ const key = hermesRowKey(row, rows.length);
8822
+ if (seen.has(key)) {
8823
+ return;
8595
8824
  }
8596
- return { ...payload.job };
8825
+ seen.add(key);
8826
+ rows.push(row);
8597
8827
  }
8598
- async function deleteHermesCronJob(jobId, options = {}) {
8599
- const response = await callHermesApi(
8600
- `/api/jobs/${encodeURIComponent(jobId)}`,
8601
- { method: "DELETE" },
8602
- options
8603
- );
8604
- await readJsonResponse(response);
8828
+ function hermesRowKey(row, fallbackIndex) {
8829
+ if (row.id !== void 0 && row.id !== null) {
8830
+ return `id:${row.id}`;
8831
+ }
8832
+ return `fallback:${fallbackIndex}:${row.role ?? ""}:${row.timestamp ?? ""}:${normalizeContent(row.content)}`;
8605
8833
  }
8606
- async function runHermesCronJobAction(jobId, action, options = {}) {
8607
- const response = await callHermesApi(
8608
- `/api/jobs/${encodeURIComponent(jobId)}/${action}`,
8609
- { method: "POST" },
8610
- options
8611
- );
8612
- const payload = await readJsonResponse(response);
8613
- if (!isRecord(payload.job)) {
8614
- throw new LinkHttpError(
8615
- 502,
8616
- "hermes_cron_job_invalid",
8617
- "Hermes API Server did not return a cron job"
8834
+ function hasHermesToolMetadata(row) {
8835
+ return normalizeMessageRole(row.role) === "tool" || readHermesToolCalls(row).length > 0 || Boolean(readString8(row, "tool_call_id")) || Boolean(readString8(row, "tool_name"));
8836
+ }
8837
+ function toLinkMessages(input) {
8838
+ const linkMessages = [];
8839
+ let pendingToolCalls = [];
8840
+ const toolCallsById = /* @__PURE__ */ new Map();
8841
+ let currentAssistant = null;
8842
+ const finishAssistantTurn = () => {
8843
+ if (!currentAssistant) {
8844
+ return;
8845
+ }
8846
+ pendingToolCalls.forEach((pending, index) => {
8847
+ const event = projectHermesToolCallWithoutOutputEvent({
8848
+ conversationId: input.conversationId,
8849
+ messageId: pending.message.id,
8850
+ pending,
8851
+ index
8852
+ });
8853
+ if (event) {
8854
+ attachAgentEventToMessage(pending.message, event, event.created_at);
8855
+ }
8856
+ });
8857
+ pendingToolCalls = [];
8858
+ toolCallsById.clear();
8859
+ currentAssistant = null;
8860
+ };
8861
+ input.messages.forEach((message, index) => {
8862
+ const role = normalizeMessageRole(message.role);
8863
+ if (role === "tool") {
8864
+ const pending = consumePendingToolCall({
8865
+ toolMessage: message,
8866
+ pendingToolCalls,
8867
+ toolCallsById
8868
+ });
8869
+ let target = pending?.message ?? currentAssistant;
8870
+ let createdSynthetic = false;
8871
+ if (!target) {
8872
+ target = createSyntheticToolAssistantMessage({
8873
+ ...input,
8874
+ message,
8875
+ index
8876
+ });
8877
+ linkMessages.push(target);
8878
+ currentAssistant = target;
8879
+ createdSynthetic = true;
8880
+ }
8881
+ if (!createdSynthetic) {
8882
+ appendHermesRawMessage(target, message);
8883
+ rememberHermesMessageId(target, message);
8884
+ }
8885
+ const event = projectHermesToolCompletedEvent({
8886
+ conversationId: input.conversationId,
8887
+ messageId: target.id,
8888
+ sourceMessage: message,
8889
+ pending,
8890
+ index
8891
+ });
8892
+ if (event) {
8893
+ attachAgentEventToMessage(target, event, event.created_at);
8894
+ }
8895
+ return;
8896
+ }
8897
+ if (role === "assistant") {
8898
+ if (!currentAssistant) {
8899
+ currentAssistant = toLinkMessage({
8900
+ conversationId: input.conversationId,
8901
+ profileName: input.profileName,
8902
+ profileUid: input.profileUid,
8903
+ profileDisplayName: input.profileDisplayName,
8904
+ sessionId: input.sessionId,
8905
+ message,
8906
+ index
8907
+ });
8908
+ linkMessages.push(currentAssistant);
8909
+ } else {
8910
+ appendHermesRawMessage(currentAssistant, message);
8911
+ rememberHermesMessageId(currentAssistant, message);
8912
+ appendAssistantTextToMessage({
8913
+ message: currentAssistant,
8914
+ text: normalizeContent(message.content),
8915
+ updatedAt: isoFromHermesTime(message.timestamp) ?? currentAssistant.updated_at
8916
+ });
8917
+ }
8918
+ for (const toolCall of readHermesToolCalls(message)) {
8919
+ const pending = { message: currentAssistant, toolCall };
8920
+ pendingToolCalls.push(pending);
8921
+ if (toolCall.id) {
8922
+ toolCallsById.set(toolCall.id, pending);
8923
+ }
8924
+ const event = projectHermesToolStartedEvent({
8925
+ conversationId: input.conversationId,
8926
+ messageId: currentAssistant.id,
8927
+ sourceMessage: message,
8928
+ toolCall,
8929
+ index
8930
+ });
8931
+ if (event) {
8932
+ attachAgentEventToMessage(currentAssistant, event, event.created_at);
8933
+ }
8934
+ }
8935
+ return;
8936
+ }
8937
+ finishAssistantTurn();
8938
+ const linkMessage = toLinkMessage({
8939
+ conversationId: input.conversationId,
8940
+ profileName: input.profileName,
8941
+ profileUid: input.profileUid,
8942
+ profileDisplayName: input.profileDisplayName,
8943
+ sessionId: input.sessionId,
8944
+ message,
8945
+ index
8946
+ });
8947
+ linkMessages.push(linkMessage);
8948
+ });
8949
+ finishAssistantTurn();
8950
+ return linkMessages;
8951
+ }
8952
+ function consumePendingToolCall(input) {
8953
+ const toolCallId = readString8(input.toolMessage, "tool_call_id");
8954
+ const toolName = readString8(input.toolMessage, "tool_name");
8955
+ let pending = toolCallId ? input.toolCallsById.get(toolCallId) : void 0;
8956
+ if (!pending && toolName) {
8957
+ pending = input.pendingToolCalls.find(
8958
+ (item) => item.toolCall.name === toolName
8618
8959
  );
8619
8960
  }
8620
- return { ...payload.job };
8961
+ if (!pending && !toolCallId) {
8962
+ pending = input.pendingToolCalls[0];
8963
+ }
8964
+ if (!pending) {
8965
+ return void 0;
8966
+ }
8967
+ const index = input.pendingToolCalls.indexOf(pending);
8968
+ if (index >= 0) {
8969
+ input.pendingToolCalls.splice(index, 1);
8970
+ }
8971
+ if (pending.toolCall.id) {
8972
+ input.toolCallsById.delete(pending.toolCall.id);
8973
+ }
8974
+ return pending;
8621
8975
  }
8622
- async function createHermesRun(input, options = {}) {
8623
- const response = await callHermesApi(
8624
- "/v1/runs",
8625
- {
8626
- method: "POST",
8627
- body: JSON.stringify(input),
8628
- headers: { "content-type": "application/json" }
8976
+ function createSyntheticToolAssistantMessage(input) {
8977
+ const message = toLinkMessage({
8978
+ conversationId: input.conversationId,
8979
+ profileName: input.profileName,
8980
+ profileUid: input.profileUid,
8981
+ profileDisplayName: input.profileDisplayName,
8982
+ sessionId: input.sessionId,
8983
+ message: {
8984
+ ...input.message,
8985
+ role: "assistant",
8986
+ content: null
8629
8987
  },
8630
- options
8988
+ index: input.index
8989
+ });
8990
+ message.raw = {
8991
+ format: "hermes-message",
8992
+ payload: input.message
8993
+ };
8994
+ return message;
8995
+ }
8996
+ function readHermesToolCalls(message) {
8997
+ const decoded = parseJsonValue(message.tool_calls) ?? message.tool_calls;
8998
+ const values = Array.isArray(decoded) ? decoded : decoded ? [decoded] : [];
8999
+ return values.map((value) => normalizeHermesToolCall(value)).filter(
9000
+ (toolCall) => Boolean(toolCall)
8631
9001
  );
8632
- if (response.status === 404 || response.status === 503) {
8633
- assertHermesRunsApiSupported(
8634
- await readHermesVersion().catch(() => null),
8635
- response.status
8636
- );
8637
- throw new LinkHttpError(
8638
- 503,
8639
- "hermes_api_server_unavailable",
8640
- "Hermes API Server is unavailable"
8641
- );
9002
+ }
9003
+ function normalizeHermesToolCall(value) {
9004
+ const record = toRecord7(value);
9005
+ if (Object.keys(record).length === 0) {
9006
+ return null;
9007
+ }
9008
+ const fn = toRecord7(record.function);
9009
+ const id = readString8(record, "id") ?? readString8(record, "call_id") ?? readString8(record, "tool_call_id") ?? readString8(fn, "id") ?? void 0;
9010
+ const name = readString8(fn, "name") ?? readString8(record, "name") ?? readString8(record, "tool_name") ?? readString8(record, "tool") ?? "tool";
9011
+ const rawArguments = fn.arguments ?? record.arguments ?? record.args ?? record.input;
9012
+ return {
9013
+ ...id ? { id } : {},
9014
+ name,
9015
+ ...rawArguments === void 0 ? {} : { arguments: parseJsonValue(rawArguments) ?? rawArguments },
9016
+ raw: value
9017
+ };
9018
+ }
9019
+ function projectHermesToolStartedEvent(input) {
9020
+ const createdAt = isoFromHermesTime(input.sourceMessage.timestamp) ?? new Date(Date.now() + input.index).toISOString();
9021
+ return projectHermesAgentEvent({
9022
+ conversationId: input.conversationId,
9023
+ messageId: input.messageId,
9024
+ type: "tool.started",
9025
+ createdAt,
9026
+ seq: input.index + 1,
9027
+ payload: {
9028
+ type: "tool.started",
9029
+ tool: input.toolCall.name,
9030
+ tool_name: input.toolCall.name,
9031
+ name: input.toolCall.name,
9032
+ ...input.toolCall.id ? { tool_call_id: input.toolCall.id, id: input.toolCall.id } : {},
9033
+ ...input.toolCall.arguments === void 0 ? {} : { arguments: input.toolCall.arguments },
9034
+ tool_call: input.toolCall.raw
9035
+ },
9036
+ raw: {
9037
+ format: "hermes-message",
9038
+ payload: {
9039
+ message: input.sourceMessage,
9040
+ tool_call: input.toolCall.raw
9041
+ }
9042
+ }
9043
+ });
9044
+ }
9045
+ function projectHermesToolCompletedEvent(input) {
9046
+ const createdAt = isoFromHermesTime(input.sourceMessage.timestamp) ?? new Date(Date.now() + input.index).toISOString();
9047
+ const output = normalizeContent(input.sourceMessage.content);
9048
+ const parsedOutput = parseJsonValue(output);
9049
+ const toolCallId = readString8(input.sourceMessage, "tool_call_id") ?? input.pending?.toolCall.id;
9050
+ const toolName = readString8(input.sourceMessage, "tool_name") ?? input.pending?.toolCall.name ?? "tool";
9051
+ return projectHermesAgentEvent({
9052
+ conversationId: input.conversationId,
9053
+ messageId: input.messageId,
9054
+ type: "tool.completed",
9055
+ createdAt,
9056
+ seq: input.index + 1,
9057
+ payload: {
9058
+ type: "tool.completed",
9059
+ tool: toolName,
9060
+ tool_name: toolName,
9061
+ name: toolName,
9062
+ ...toolCallId ? { tool_call_id: toolCallId, id: toolCallId } : {},
9063
+ ...input.pending?.toolCall.arguments === void 0 ? {} : { arguments: input.pending.toolCall.arguments },
9064
+ output,
9065
+ content: output,
9066
+ result: parsedOutput ?? output,
9067
+ ...input.pending ? { tool_call: input.pending.toolCall.raw } : {}
9068
+ },
9069
+ raw: {
9070
+ format: "hermes-message",
9071
+ payload: {
9072
+ message: input.sourceMessage,
9073
+ ...input.pending ? { tool_call: input.pending.toolCall.raw } : {}
9074
+ }
9075
+ }
9076
+ });
9077
+ }
9078
+ function projectHermesToolCallWithoutOutputEvent(input) {
9079
+ return projectHermesAgentEvent({
9080
+ conversationId: input.conversationId,
9081
+ messageId: input.messageId,
9082
+ type: "tool.completed",
9083
+ createdAt: input.pending.message.updated_at,
9084
+ seq: input.index + 1,
9085
+ payload: {
9086
+ type: "tool.completed",
9087
+ tool: input.pending.toolCall.name,
9088
+ tool_name: input.pending.toolCall.name,
9089
+ name: input.pending.toolCall.name,
9090
+ ...input.pending.toolCall.id ? {
9091
+ tool_call_id: input.pending.toolCall.id,
9092
+ id: input.pending.toolCall.id
9093
+ } : {},
9094
+ ...input.pending.toolCall.arguments === void 0 ? {} : { arguments: input.pending.toolCall.arguments },
9095
+ tool_call: input.pending.toolCall.raw
9096
+ },
9097
+ raw: {
9098
+ format: "hermes-tool-call",
9099
+ payload: {
9100
+ tool_call: input.pending.toolCall.raw,
9101
+ note: "tool_output_not_found"
9102
+ }
9103
+ }
9104
+ });
9105
+ }
9106
+ function projectHermesAgentEvent(input) {
9107
+ const event = {
9108
+ seq: input.seq,
9109
+ type: input.type,
9110
+ conversation_id: input.conversationId,
9111
+ message_id: input.messageId,
9112
+ created_at: input.createdAt,
9113
+ payload: input.payload,
9114
+ raw: input.raw
9115
+ };
9116
+ return projectConversationAgentEvent(event);
9117
+ }
9118
+ function attachAgentEventToMessage(message, event, updatedAt) {
9119
+ ensureTextBlockForMessage(message);
9120
+ message.agent_events = upsertAgentEventProjection(
9121
+ message.agent_events ?? [],
9122
+ event
9123
+ );
9124
+ appendAgentEventBlock(message, event, updatedAt);
9125
+ message.updated_at = latestTimestamp(message.updated_at, updatedAt);
9126
+ }
9127
+ function appendAssistantTextToMessage(input) {
9128
+ const text = input.text;
9129
+ if (!text) {
9130
+ return;
9131
+ }
9132
+ appendTextPart(input.message, text);
9133
+ if (input.message.blocks?.length) {
9134
+ appendTextBlock(input.message, text, input.updatedAt);
9135
+ }
9136
+ input.message.updated_at = latestTimestamp(
9137
+ input.message.updated_at,
9138
+ input.updatedAt
9139
+ );
9140
+ }
9141
+ function appendTextPart(message, text) {
9142
+ const existing = message.parts.find((part) => part.type === "text");
9143
+ if (existing) {
9144
+ existing.text = joinImportedText(existing.text ?? "", text);
9145
+ return;
9146
+ }
9147
+ message.parts.push({ type: "text", text });
9148
+ }
9149
+ function appendTextBlock(message, text, updatedAt) {
9150
+ const blocks = [...message.blocks ?? []];
9151
+ const last = blocks.at(-1);
9152
+ if (last?.type === "text") {
9153
+ blocks[blocks.length - 1] = {
9154
+ ...last,
9155
+ text: joinImportedText(last.text, text),
9156
+ updated_at: updatedAt
9157
+ };
9158
+ } else {
9159
+ blocks.push({
9160
+ id: `text_${blocks.length + 1}`,
9161
+ type: "text",
9162
+ text,
9163
+ created_at: updatedAt,
9164
+ updated_at: updatedAt
9165
+ });
9166
+ }
9167
+ message.blocks = blocks;
9168
+ }
9169
+ function ensureTextBlockForMessage(message) {
9170
+ if (message.blocks?.length) {
9171
+ return;
9172
+ }
9173
+ const text = message.parts.filter((part) => part.type === "text" && part.text).map((part) => part.text).join("");
9174
+ if (!text) {
9175
+ return;
9176
+ }
9177
+ message.blocks = [
9178
+ {
9179
+ id: "text_1",
9180
+ type: "text",
9181
+ text,
9182
+ created_at: message.created_at,
9183
+ updated_at: message.updated_at
9184
+ }
9185
+ ];
9186
+ }
9187
+ function appendAgentEventBlock(message, event, updatedAt) {
9188
+ const blocks = [...message.blocks ?? []];
9189
+ const matchingIndex = blocks.findIndex((block) => {
9190
+ if (block.type !== "agent_events") {
9191
+ return false;
9192
+ }
9193
+ return upsertAgentEventProjection(block.events, event).length === block.events.length;
9194
+ });
9195
+ const targetIndex = matchingIndex >= 0 ? matchingIndex : blocks.at(-1)?.type === "agent_events" ? blocks.length - 1 : -1;
9196
+ if (targetIndex >= 0) {
9197
+ const block = blocks[targetIndex];
9198
+ if (block.type === "agent_events") {
9199
+ blocks[targetIndex] = {
9200
+ ...block,
9201
+ events: upsertAgentEventProjection(block.events, event),
9202
+ updated_at: updatedAt
9203
+ };
9204
+ }
9205
+ } else {
9206
+ blocks.push({
9207
+ id: `tools_${blocks.length + 1}`,
9208
+ type: "agent_events",
9209
+ events: [event],
9210
+ created_at: updatedAt,
9211
+ updated_at: updatedAt
9212
+ });
9213
+ }
9214
+ message.blocks = blocks;
9215
+ }
9216
+ function latestTimestamp(left, right) {
9217
+ const leftTime = Date.parse(left);
9218
+ const rightTime = Date.parse(right);
9219
+ if (Number.isNaN(leftTime)) {
9220
+ return right;
9221
+ }
9222
+ if (Number.isNaN(rightTime)) {
9223
+ return left;
9224
+ }
9225
+ return leftTime >= rightTime ? left : right;
9226
+ }
9227
+ async function readKnownHermesSessions(store) {
9228
+ const ids = /* @__PURE__ */ new Set();
9229
+ const conversationIdsBySessionId = /* @__PURE__ */ new Map();
9230
+ for (const conversationId of await store.listConversationIds()) {
9231
+ const manifest = await store.readManifest(conversationId).catch(() => null);
9232
+ if (!manifest) {
9233
+ continue;
9234
+ }
9235
+ if (manifest.hermes_session_id) {
9236
+ ids.add(manifest.hermes_session_id);
9237
+ rememberKnownHermesConversation(
9238
+ conversationIdsBySessionId,
9239
+ manifest.hermes_session_id,
9240
+ conversationId
9241
+ );
9242
+ }
9243
+ for (const sessionId of manifest.hermes_session_ids ?? []) {
9244
+ ids.add(sessionId);
9245
+ rememberKnownHermesConversation(
9246
+ conversationIdsBySessionId,
9247
+ sessionId,
9248
+ conversationId
9249
+ );
9250
+ }
9251
+ }
9252
+ return { ids, conversationIdsBySessionId };
9253
+ }
9254
+ function rememberKnownHermesConversation(map, sessionId, conversationId) {
9255
+ const current = map.get(sessionId) ?? [];
9256
+ if (!current.includes(conversationId)) {
9257
+ map.set(sessionId, [...current, conversationId]);
9258
+ }
9259
+ }
9260
+ async function discoverHermesProfileNames() {
9261
+ const names = /* @__PURE__ */ new Set([DEFAULT_PROFILE_NAME]);
9262
+ const profilesDir = path13.join(os4.homedir(), ".hermes", "profiles");
9263
+ const entries = await readdir5(profilesDir, { withFileTypes: true }).catch(
9264
+ (error) => {
9265
+ if (isNodeError9(error, "ENOENT")) {
9266
+ return [];
9267
+ }
9268
+ throw error;
9269
+ }
9270
+ );
9271
+ for (const entry of entries) {
9272
+ if (entry.isDirectory() && PROFILE_NAME_PATTERN3.test(entry.name)) {
9273
+ names.add(entry.name);
9274
+ }
9275
+ }
9276
+ return [...names].sort((left, right) => {
9277
+ if (left === DEFAULT_PROFILE_NAME) {
9278
+ return -1;
9279
+ }
9280
+ if (right === DEFAULT_PROFILE_NAME) {
9281
+ return 1;
9282
+ }
9283
+ return left.localeCompare(right);
9284
+ });
9285
+ }
9286
+ async function listProfileSessions(dbPath) {
9287
+ if (!await isFile(dbPath)) {
9288
+ return [];
9289
+ }
9290
+ let db = null;
9291
+ try {
9292
+ const { DatabaseSync } = nodeRequire3(
9293
+ "node:sqlite"
9294
+ );
9295
+ db = new DatabaseSync(dbPath, {
9296
+ readOnly: true,
9297
+ timeout: 1e3
9298
+ });
9299
+ const sessionColumns = readTableColumns(db, "sessions");
9300
+ if (!sessionColumns.has("id")) {
9301
+ return [];
9302
+ }
9303
+ const messageColumns = readTableColumns(db, "messages");
9304
+ const selectColumns = [...sessionColumns].map((column) => `s.${quoteIdentifier(column)}`).join(", ");
9305
+ const lastActiveSql = messageColumns.has("timestamp") ? `COALESCE(
9306
+ (SELECT MAX(m.timestamp) FROM messages m WHERE m.session_id = s.id),
9307
+ s.started_at,
9308
+ 0
9309
+ ) AS last_active` : "COALESCE(s.started_at, 0) AS last_active";
9310
+ const rows = db.prepare(
9311
+ `
9312
+ SELECT ${selectColumns}, ${lastActiveSql}
9313
+ FROM sessions s
9314
+ ORDER BY last_active DESC
9315
+ `
9316
+ ).all();
9317
+ return projectCompressionTips(rows);
9318
+ } finally {
9319
+ db?.close();
9320
+ }
9321
+ }
9322
+ function appendHermesRawMessage(message, row) {
9323
+ const rows = readHermesRawMessageRows(message.raw);
9324
+ message.raw = rows.length === 0 ? {
9325
+ format: "hermes-message",
9326
+ payload: row
9327
+ } : {
9328
+ format: "hermes-message-group",
9329
+ payload: { messages: [...rows, row] }
9330
+ };
9331
+ }
9332
+ function readHermesRawMessageRows(raw) {
9333
+ if (!raw) {
9334
+ return [];
9335
+ }
9336
+ if (raw.format === "hermes-message-group") {
9337
+ const payload = toRecord7(raw.payload);
9338
+ return Array.isArray(payload.messages) ? payload.messages.filter(
9339
+ (item) => typeof item === "object" && item !== null
9340
+ ) : [];
9341
+ }
9342
+ if (raw.format === "hermes-message") {
9343
+ return typeof raw.payload === "object" && raw.payload !== null ? [raw.payload] : [];
9344
+ }
9345
+ return [];
9346
+ }
9347
+ function rememberHermesMessageId(message, row) {
9348
+ if (row.id === void 0 || row.id === null) {
9349
+ return;
9350
+ }
9351
+ const existing = Array.isArray(message.hermes?.message_ids) ? message.hermes.message_ids : message.hermes?.message_id === void 0 ? [] : [message.hermes.message_id];
9352
+ const id = row.id;
9353
+ message.hermes = {
9354
+ ...message.hermes ?? {},
9355
+ message_ids: existing.includes(id) ? existing : [...existing, id]
9356
+ };
9357
+ }
9358
+ function joinImportedText(left, right) {
9359
+ if (!left) {
9360
+ return right;
9361
+ }
9362
+ if (!right) {
9363
+ return left;
9364
+ }
9365
+ if (/\s$/u.test(left) || /^\s/u.test(right)) {
9366
+ return `${left}${right}`;
9367
+ }
9368
+ return `${left}
9369
+
9370
+ ${right}`;
9371
+ }
9372
+ function projectCompressionTips(rows) {
9373
+ const byId = /* @__PURE__ */ new Map();
9374
+ const childrenByParent = /* @__PURE__ */ new Map();
9375
+ for (const row of rows) {
9376
+ const id = readString8(row, "id");
9377
+ if (!id) {
9378
+ continue;
9379
+ }
9380
+ byId.set(id, row);
9381
+ const parentId = readString8(row, "parent_session_id");
9382
+ if (parentId) {
9383
+ const children = childrenByParent.get(parentId) ?? [];
9384
+ children.push(row);
9385
+ childrenByParent.set(parentId, children);
9386
+ }
9387
+ }
9388
+ const projected = [];
9389
+ for (const row of rows) {
9390
+ const id = readString8(row, "id");
9391
+ if (!id || readString8(row, "parent_session_id")) {
9392
+ continue;
9393
+ }
9394
+ let tip = row;
9395
+ const visited = /* @__PURE__ */ new Set([id]);
9396
+ while (readString8(tip, "end_reason") === "compression") {
9397
+ const tipId2 = readString8(tip, "id");
9398
+ if (!tipId2) {
9399
+ break;
9400
+ }
9401
+ const next = (childrenByParent.get(tipId2) ?? []).filter((child) => readString8(child, "id")).sort(
9402
+ (left, right) => (readNumber2(right.last_active) ?? 0) - (readNumber2(left.last_active) ?? 0)
9403
+ )[0];
9404
+ const nextId = next ? readString8(next, "id") : null;
9405
+ if (!next || !nextId || visited.has(nextId)) {
9406
+ break;
9407
+ }
9408
+ tip = next;
9409
+ visited.add(nextId);
9410
+ }
9411
+ const tipId = readString8(tip, "id");
9412
+ if (tipId) {
9413
+ projected.push({
9414
+ ...tip,
9415
+ id: tipId,
9416
+ _lineage_root_id: id,
9417
+ started_at: readNumber2(row.started_at) ?? readNumber2(tip.started_at)
9418
+ });
9419
+ }
9420
+ }
9421
+ return projected;
9422
+ }
9423
+ async function readHermesSessionMessages(candidate) {
9424
+ const [dbMessages, jsonlMessages] = await Promise.all([
9425
+ readStateDbMessages(candidate.dbPath, candidate.session.id),
9426
+ readJsonlMessages(candidate.profileName, candidate.session.id)
9427
+ ]);
9428
+ return jsonlMessages.length > dbMessages.length ? jsonlMessages : dbMessages;
9429
+ }
9430
+ async function readStateDbMessages(dbPath, sessionId) {
9431
+ if (!await isFile(dbPath)) {
9432
+ return [];
9433
+ }
9434
+ let db = null;
9435
+ try {
9436
+ const { DatabaseSync } = nodeRequire3(
9437
+ "node:sqlite"
9438
+ );
9439
+ db = new DatabaseSync(dbPath, {
9440
+ readOnly: true,
9441
+ timeout: 1e3
9442
+ });
9443
+ const columns = readTableColumns(db, "messages");
9444
+ if (!columns.has("session_id") || !columns.has("role")) {
9445
+ return [];
9446
+ }
9447
+ const selectColumns = MESSAGE_COLUMNS.map(
9448
+ (column) => columns.has(column) ? quoteIdentifier(column) : `NULL AS ${column}`
9449
+ ).join(", ");
9450
+ return db.prepare(
9451
+ `
9452
+ SELECT ${selectColumns}
9453
+ FROM messages
9454
+ WHERE session_id = ?
9455
+ ORDER BY timestamp, id
9456
+ `
9457
+ ).all(sessionId);
9458
+ } catch {
9459
+ return [];
9460
+ } finally {
9461
+ db?.close();
9462
+ }
9463
+ }
9464
+ async function readJsonlMessages(profileName, sessionId) {
9465
+ if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(sessionId)) {
9466
+ return [];
9467
+ }
9468
+ const profileDir = resolveHermesProfileDir(profileName);
9469
+ const sessionsDir = await readHermesSessionsDir(profileName).then((value) => value.sessionsDir).catch(() => path13.join(profileDir, "sessions"));
9470
+ const transcriptPath = path13.join(sessionsDir, `${sessionId}.jsonl`);
9471
+ const raw = await readFile8(transcriptPath, "utf8").catch((error) => {
9472
+ if (isNodeError9(error, "ENOENT")) {
9473
+ return "";
9474
+ }
9475
+ throw error;
9476
+ });
9477
+ if (!raw.trim()) {
9478
+ return [];
9479
+ }
9480
+ const rows = [];
9481
+ for (const line of raw.split(/\r?\n/u)) {
9482
+ if (!line.trim()) {
9483
+ continue;
9484
+ }
9485
+ try {
9486
+ const parsed = JSON.parse(line);
9487
+ const normalized = normalizeJsonlMessage(parsed);
9488
+ if (normalized) {
9489
+ rows.push(normalized);
9490
+ }
9491
+ } catch {
9492
+ continue;
9493
+ }
9494
+ }
9495
+ return rows;
9496
+ }
9497
+ function normalizeJsonlMessage(row) {
9498
+ const role = readString8(row, "role");
9499
+ if (!role) {
9500
+ return null;
9501
+ }
9502
+ const content = normalizeContent(row.content);
9503
+ const timestamp = readNumber2(row.timestamp) ?? readNumber2(row.created_at) ?? readNumber2(row.createdAt);
9504
+ return {
9505
+ ...row,
9506
+ role,
9507
+ content,
9508
+ timestamp: timestamp ?? void 0
9509
+ };
9510
+ }
9511
+ function toLinkMessage(input) {
9512
+ const role = normalizeMessageRole(input.message.role);
9513
+ const text = normalizeContent(input.message.content);
9514
+ const createdAt = isoFromHermesTime(input.message.timestamp) ?? new Date(Date.now() + input.index).toISOString();
9515
+ return {
9516
+ id: `msg_${randomUUID6().replaceAll("-", "")}`,
9517
+ schema_version: 1,
9518
+ conversation_id: input.conversationId,
9519
+ role,
9520
+ status: "completed",
9521
+ created_at: createdAt,
9522
+ updated_at: createdAt,
9523
+ sender: senderForRole({
9524
+ role,
9525
+ profileName: input.profileName,
9526
+ profileUid: input.profileUid,
9527
+ profileDisplayName: input.profileDisplayName
9528
+ }),
9529
+ parts: text ? [{ type: "text", text }] : [],
9530
+ attachments: [],
9531
+ hermes: {
9532
+ session_id: input.sessionId,
9533
+ message_id: input.message.id,
9534
+ imported_from: "hermes",
9535
+ import_projection: HERMES_IMPORT_PROJECTION_VERSION
9536
+ },
9537
+ raw: {
9538
+ format: "hermes-message",
9539
+ payload: input.message
9540
+ }
9541
+ };
9542
+ }
9543
+ function senderForRole(input) {
9544
+ switch (input.role) {
9545
+ case "user":
9546
+ return { id: "hermes_user", type: "human", display_name: "Me" };
9547
+ case "assistant":
9548
+ return {
9549
+ id: `agent_${input.profileName}`,
9550
+ type: "agent",
9551
+ display_name: input.profileDisplayName,
9552
+ profile_uid: input.profileUid,
9553
+ profile: input.profileName
9554
+ };
9555
+ case "tool":
9556
+ return { id: "hermes_tool", type: "tool", display_name: "Tool" };
9557
+ case "system":
9558
+ return { id: "hermes_system", type: "system", display_name: "System" };
9559
+ }
9560
+ }
9561
+ function firstUserText(snapshot) {
9562
+ return snapshot.messages.find((message) => message.role === "user")?.parts.find((part) => part.type === "text")?.text?.slice(0, 80);
9563
+ }
9564
+ function normalizeTitle(value) {
9565
+ const normalized = value?.replace(/\s+/gu, " ").trim();
9566
+ return normalized || DEFAULT_CONVERSATION_TITLE;
9567
+ }
9568
+ function normalizeMessageRole(value) {
9569
+ switch (value?.trim().toLowerCase()) {
9570
+ case "user":
9571
+ return "user";
9572
+ case "assistant":
9573
+ return "assistant";
9574
+ case "tool":
9575
+ return "tool";
9576
+ case "system":
9577
+ return "system";
9578
+ default:
9579
+ return "system";
9580
+ }
9581
+ }
9582
+ function normalizeContent(value) {
9583
+ if (typeof value === "string") {
9584
+ return value;
9585
+ }
9586
+ if (Array.isArray(value)) {
9587
+ return value.map((item) => {
9588
+ if (typeof item === "string") {
9589
+ return item;
9590
+ }
9591
+ if (typeof item === "object" && item !== null) {
9592
+ return readString8(item, "text") ?? "";
9593
+ }
9594
+ return "";
9595
+ }).filter(Boolean).join("");
9596
+ }
9597
+ return "";
9598
+ }
9599
+ function parseJsonValue(value) {
9600
+ if (typeof value !== "string") {
9601
+ return void 0;
9602
+ }
9603
+ const trimmed = value.trim();
9604
+ if (!trimmed) {
9605
+ return void 0;
9606
+ }
9607
+ try {
9608
+ return JSON.parse(trimmed);
9609
+ } catch {
9610
+ return void 0;
9611
+ }
9612
+ }
9613
+ function toRecord7(value) {
9614
+ return typeof value === "object" && value !== null ? value : {};
9615
+ }
9616
+ function isDeletedSession(session) {
9617
+ return readBoolean(session.deleted) || readBoolean(session.is_deleted) || Boolean(readString8(session, "deleted_at")) || ["deleted", "removed"].includes(readString8(session, "status") ?? "");
9618
+ }
9619
+ function isHiddenSession(session) {
9620
+ const source = readString8(session, "source")?.toLowerCase();
9621
+ const status = readString8(session, "status")?.toLowerCase();
9622
+ const visibility = readString8(session, "visibility")?.toLowerCase();
9623
+ return Boolean(source && HIDDEN_SESSION_SOURCES.has(source)) || readBoolean(session.hidden) || readBoolean(session.archived) || Boolean(readString8(session, "archived_at")) || status === "hidden" || status === "archived" || visibility === "hidden" || visibility === "hide";
9624
+ }
9625
+ function readTableColumns(db, tableName) {
9626
+ try {
9627
+ const rows = db.prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`).all();
9628
+ return new Set(
9629
+ rows.map((row) => typeof row.name === "string" ? row.name : "").filter(Boolean)
9630
+ );
9631
+ } catch {
9632
+ return /* @__PURE__ */ new Set();
9633
+ }
9634
+ }
9635
+ function quoteIdentifier(value) {
9636
+ return `"${value.replaceAll('"', '""')}"`;
9637
+ }
9638
+ async function isFile(filePath) {
9639
+ return stat7(filePath).then((value) => value.isFile()).catch((error) => {
9640
+ if (isNodeError9(error, "ENOENT")) {
9641
+ return false;
9642
+ }
9643
+ throw error;
9644
+ });
9645
+ }
9646
+ function createConversationId() {
9647
+ return `conv_${randomUUID6().replaceAll("-", "")}`;
9648
+ }
9649
+ function isoFromHermesTime(value) {
9650
+ const numeric = readNumber2(value);
9651
+ if (!numeric || numeric <= 0) {
9652
+ return void 0;
9653
+ }
9654
+ const millis = numeric > 1e10 ? numeric : numeric * 1e3;
9655
+ return new Date(millis).toISOString();
9656
+ }
9657
+ function readString8(payload, key) {
9658
+ const value = payload[key];
9659
+ return typeof value === "string" && value.trim() ? value.trim() : null;
9660
+ }
9661
+ function readNumber2(value) {
9662
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
9663
+ }
9664
+ function readBoolean(value) {
9665
+ if (value === true || value === 1) {
9666
+ return true;
9667
+ }
9668
+ if (typeof value === "string") {
9669
+ return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
9670
+ }
9671
+ return false;
9672
+ }
9673
+ function isNodeError9(error, code) {
9674
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
9675
+ }
9676
+
9677
+ // src/conversations/delivery-import.ts
9678
+ import { lstat, readFile as readFile9, readdir as readdir6, stat as stat8 } from "fs/promises";
9679
+ import path14 from "path";
9680
+ var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
9681
+ var MAX_MEDIA_IMPORT_FAILURES = 20;
9682
+ var MAX_DELIVERY_FILES = 50;
9683
+ var DELIVERY_STAGING_SEGMENT = "delivery-staging";
9684
+ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
9685
+ ".png",
9686
+ ".jpg",
9687
+ ".jpeg",
9688
+ ".gif",
9689
+ ".webp",
9690
+ ".heic",
9691
+ ".pdf",
9692
+ ".txt",
9693
+ ".log",
9694
+ ".md",
9695
+ ".markdown",
9696
+ ".json",
9697
+ ".jsonl",
9698
+ ".yaml",
9699
+ ".yml",
9700
+ ".toml",
9701
+ ".ini",
9702
+ ".xml",
9703
+ ".html",
9704
+ ".css",
9705
+ ".js",
9706
+ ".ts",
9707
+ ".jsx",
9708
+ ".tsx",
9709
+ ".dart",
9710
+ ".py",
9711
+ ".java",
9712
+ ".kt",
9713
+ ".swift",
9714
+ ".go",
9715
+ ".rs",
9716
+ ".rb",
9717
+ ".php",
9718
+ ".c",
9719
+ ".cc",
9720
+ ".cpp",
9721
+ ".h",
9722
+ ".hpp",
9723
+ ".cs",
9724
+ ".sql",
9725
+ ".csv",
9726
+ ".tsv",
9727
+ ".doc",
9728
+ ".docx",
9729
+ ".xls",
9730
+ ".xlsx",
9731
+ ".ppt",
9732
+ ".pptx",
9733
+ ".zip",
9734
+ ".rar",
9735
+ ".7z",
9736
+ ".tar",
9737
+ ".gz",
9738
+ ".mp4",
9739
+ ".mov",
9740
+ ".avi",
9741
+ ".mkv",
9742
+ ".webm",
9743
+ ".ogg",
9744
+ ".opus",
9745
+ ".mp3",
9746
+ ".wav",
9747
+ ".m4a"
9748
+ ]);
9749
+ function resolveDeliveryStagingTarget(paths, stagingDir) {
9750
+ const resolvedDir = path14.resolve(stagingDir);
9751
+ const relative = path14.relative(path14.resolve(paths.conversationsDir), resolvedDir);
9752
+ if (!relative || relative.startsWith("..") || path14.isAbsolute(relative)) {
9753
+ throw new LinkHttpError(
9754
+ 400,
9755
+ "delivery_staging_invalid",
9756
+ "delivery staging directory must be inside Hermes Link conversations"
9757
+ );
9758
+ }
9759
+ const segments = relative.split(path14.sep);
9760
+ if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
9761
+ throw new LinkHttpError(
9762
+ 400,
9763
+ "delivery_staging_invalid",
9764
+ "delivery staging directory is invalid"
9765
+ );
9766
+ }
9767
+ return {
9768
+ conversationId: segments[0],
9769
+ runId: segments[2],
9770
+ stagingDir: resolvedDir
9771
+ };
9772
+ }
9773
+ async function collectStagedDeliveryReferences(stagingDir) {
9774
+ const directoryStat = await lstat(stagingDir).catch((error) => {
9775
+ if (isNodeError10(error, "ENOENT")) {
9776
+ throw new LinkHttpError(
9777
+ 404,
9778
+ "delivery_staging_not_found",
9779
+ "delivery staging directory was not found"
9780
+ );
9781
+ }
9782
+ throw error;
9783
+ });
9784
+ if (!directoryStat.isDirectory()) {
9785
+ throw new LinkHttpError(
9786
+ 400,
9787
+ "delivery_staging_not_directory",
9788
+ "delivery staging path is not a directory"
9789
+ );
9790
+ }
9791
+ const entries = await readdir6(stagingDir, { withFileTypes: true });
9792
+ return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
9793
+ (left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
9794
+ ).slice(0, MAX_DELIVERY_FILES).map((entry) => {
9795
+ const sourcePath = path14.join(stagingDir, entry.name);
9796
+ const mime = inferMimeType(sourcePath);
9797
+ return {
9798
+ path: sourcePath,
9799
+ kind: mediaKindForMime(mime),
9800
+ mime
9801
+ };
9802
+ });
9803
+ }
9804
+ async function importMediaReferencesForMessage(deps, input) {
9805
+ const references = input.references.slice(0, input.maxReferences ?? MAX_DELIVERY_FILES);
9806
+ if (references.length === 0) {
9807
+ return emptyImportResult(input);
9808
+ }
9809
+ const snapshot = await deps.readSnapshot(input.conversationId);
9810
+ const assistant = snapshot.messages.find(
9811
+ (message) => message.id === input.messageId
9812
+ );
9813
+ if (!assistant) {
9814
+ return emptyImportResult(input);
9815
+ }
9816
+ const importedSourceKeys = readImportedMediaSourceKeys(assistant);
9817
+ const failedSourceKeys = readFailedMediaSourceKeys(assistant);
9818
+ const failureRecordsByKey = new Map(
9819
+ readMediaImportFailures(assistant).map((failure) => [failure.key, failure])
9820
+ );
9821
+ const importedParts = [];
9822
+ const newFailures = [];
9823
+ let skippedCount = 0;
9824
+ for (const reference of references) {
9825
+ let sourceKey;
9826
+ try {
9827
+ sourceKey = mediaSourceKey(reference.path);
9828
+ if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
9829
+ skippedCount += 1;
9830
+ continue;
9831
+ }
9832
+ const blob = await writeBlobFromFile(deps, input.conversationId, reference);
9833
+ const part = {
9834
+ type: reference.kind ?? mediaKindForMime(blob.mime),
9835
+ blob: blob.id,
9836
+ mime: blob.mime,
9837
+ size: blob.size,
9838
+ filename: blob.filename,
9839
+ url: `/api/v1/conversations/${encodeURIComponent(input.conversationId)}/blobs/${encodeURIComponent(blob.id)}`
9840
+ };
9841
+ assistant.parts.push(part);
9842
+ assistant.attachments.push({
9843
+ blob_id: blob.id,
9844
+ mime: blob.mime,
9845
+ size: blob.size,
9846
+ filename: blob.filename,
9847
+ source: "hermes_output"
9848
+ });
9849
+ importedSourceKeys.add(sourceKey);
9850
+ importedParts.push(part);
9851
+ } catch (error) {
9852
+ if (sourceKey && !failedSourceKeys.has(sourceKey)) {
9853
+ const failure = describeMediaImportFailure(reference, sourceKey, error);
9854
+ failedSourceKeys.add(sourceKey);
9855
+ failureRecordsByKey.set(sourceKey, failure);
9856
+ newFailures.push(failure);
9857
+ }
9858
+ void deps.logger.warn("conversation_media_import_failed", {
9859
+ conversation_id: input.conversationId,
9860
+ run_id: input.runId,
9861
+ message_id: input.messageId,
9862
+ error: error instanceof Error ? error.message : String(error)
9863
+ });
9864
+ }
9865
+ }
9866
+ if (importedParts.length === 0 && newFailures.length === 0) {
9867
+ return {
9868
+ ...emptyImportResult(input),
9869
+ discovered_count: references.length,
9870
+ skipped_count: skippedCount
9871
+ };
9872
+ }
9873
+ assistant.hermes = {
9874
+ ...toRecord8(assistant.hermes),
9875
+ imported_media_source_keys: [...importedSourceKeys],
9876
+ media_import_failed_source_keys: [...failedSourceKeys],
9877
+ media_import_failures: [...failureRecordsByKey.values()].slice(
9878
+ -MAX_MEDIA_IMPORT_FAILURES
9879
+ )
9880
+ };
9881
+ assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
9882
+ await deps.writeSnapshot(input.conversationId, snapshot);
9883
+ let lastEventSeq;
9884
+ if (importedParts.length > 0) {
9885
+ const event = await deps.appendEvent(input.conversationId, {
9886
+ type: "message.parts.created",
9887
+ message_id: input.messageId,
9888
+ run_id: input.runId,
9889
+ payload: { parts: importedParts }
9890
+ });
9891
+ lastEventSeq = event.seq;
9892
+ }
9893
+ return {
9894
+ conversation_id: input.conversationId,
9895
+ run_id: input.runId,
9896
+ message_id: input.messageId,
9897
+ discovered_count: references.length,
9898
+ imported_count: importedParts.length,
9899
+ skipped_count: skippedCount,
9900
+ failed_count: newFailures.length,
9901
+ parts: importedParts,
9902
+ ...lastEventSeq ? { last_event_seq: lastEventSeq } : {}
9903
+ };
9904
+ }
9905
+ function readMediaImportFailures(message) {
9906
+ const hermes = toRecord8(message.hermes);
9907
+ const failures = hermes.media_import_failures;
9908
+ if (!Array.isArray(failures)) {
9909
+ return [];
9910
+ }
9911
+ return failures.flatMap((item) => {
9912
+ const record = toRecord8(item);
9913
+ const key = readString9(record, "key");
9914
+ const filename = readString9(record, "filename");
9915
+ const reason = readString9(record, "reason");
9916
+ if (!key || !filename || !reason) {
9917
+ return [];
9918
+ }
9919
+ return [
9920
+ {
9921
+ key,
9922
+ filename,
9923
+ reason,
9924
+ ...readString9(record, "code") ? { code: readString9(record, "code") } : {}
9925
+ }
9926
+ ];
9927
+ });
9928
+ }
9929
+ function readFailedMediaSourceKeys(message) {
9930
+ const hermes = toRecord8(message.hermes);
9931
+ const keys = hermes.media_import_failed_source_keys;
9932
+ if (!Array.isArray(keys)) {
9933
+ return /* @__PURE__ */ new Set();
9934
+ }
9935
+ return new Set(
9936
+ keys.filter(
9937
+ (key) => typeof key === "string" && key.length > 0
9938
+ )
9939
+ );
9940
+ }
9941
+ function emptyImportResult(input) {
9942
+ return {
9943
+ conversation_id: input.conversationId,
9944
+ run_id: input.runId,
9945
+ message_id: input.messageId,
9946
+ discovered_count: 0,
9947
+ imported_count: 0,
9948
+ skipped_count: 0,
9949
+ failed_count: 0,
9950
+ parts: []
9951
+ };
9952
+ }
9953
+ async function writeBlobFromFile(deps, conversationId, source) {
9954
+ const sourcePath = resolveMediaSourcePath(source.path);
9955
+ const fileStat = await stat8(sourcePath).catch((error) => {
9956
+ if (isNodeError10(error, "ENOENT")) {
9957
+ throw new LinkHttpError(
9958
+ 404,
9959
+ "media_source_not_found",
9960
+ "Hermes output file was not found"
9961
+ );
9962
+ }
9963
+ throw error;
9964
+ });
9965
+ if (!fileStat.isFile()) {
9966
+ throw new LinkHttpError(
9967
+ 400,
9968
+ "media_source_not_file",
9969
+ "Hermes output media source is not a file"
9970
+ );
9971
+ }
9972
+ if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
9973
+ throw new LinkHttpError(
9974
+ 413,
9975
+ "media_source_too_large",
9976
+ "Hermes output media source is too large"
9977
+ );
9978
+ }
9979
+ return deps.writeBlob(conversationId, {
9980
+ bytes: await readFile9(sourcePath),
9981
+ filename: path14.basename(sourcePath),
9982
+ mime: source.mime ?? inferMimeType(sourcePath)
9983
+ });
9984
+ }
9985
+ function describeMediaImportFailure(reference, sourceKey, error) {
9986
+ return {
9987
+ key: sourceKey,
9988
+ filename: sanitizeFilename(reference.path, "attachment"),
9989
+ reason: error instanceof Error ? error.message : String(error),
9990
+ ...isNodeError10(error) && error.code ? { code: error.code } : {}
9991
+ };
9992
+ }
9993
+ function isSupportedDeliveryFilename(filename) {
9994
+ return SUPPORTED_DELIVERY_EXTENSIONS.has(path14.extname(filename).toLowerCase());
9995
+ }
9996
+ function readString9(payload, key) {
9997
+ const value = payload[key];
9998
+ return typeof value === "string" && value.trim() ? value.trim() : null;
9999
+ }
10000
+ function toRecord8(value) {
10001
+ return typeof value === "object" && value !== null ? value : {};
10002
+ }
10003
+ function isNodeError10(error, code) {
10004
+ return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
10005
+ }
10006
+
10007
+ // src/conversations/run-lifecycle.ts
10008
+ import { readdir as readdir7 } from "fs/promises";
10009
+
10010
+ // src/hermes/api-server.ts
10011
+ async function listHermesModels(options = {}) {
10012
+ const response = await callHermesApi("/v1/models", { method: "GET" }, options);
10013
+ if (response.status === 404) {
10014
+ return { models: [] };
10015
+ }
10016
+ return await readJsonResponse(response);
10017
+ }
10018
+ async function listHermesCronJobs(options = {}) {
10019
+ const query = options.includeDisabled ? "?include_disabled=true" : "";
10020
+ const response = await callHermesApi(
10021
+ `/api/jobs${query}`,
10022
+ { method: "GET" },
10023
+ options
10024
+ );
10025
+ const payload = await readJsonResponse(response);
10026
+ const jobs = payload.jobs;
10027
+ return Array.isArray(jobs) ? jobs.filter(isRecord).map((job) => ({ ...job })) : [];
10028
+ }
10029
+ async function getHermesCronJob(jobId, options = {}) {
10030
+ const response = await callHermesApi(
10031
+ `/api/jobs/${encodeURIComponent(jobId)}`,
10032
+ { method: "GET" },
10033
+ options
10034
+ );
10035
+ const payload = await readJsonResponse(response);
10036
+ if (!isRecord(payload.job)) {
10037
+ throw new LinkHttpError(
10038
+ 502,
10039
+ "hermes_cron_job_invalid",
10040
+ "Hermes API Server did not return a cron job"
10041
+ );
10042
+ }
10043
+ return { ...payload.job };
10044
+ }
10045
+ async function createHermesCronJob(input, options = {}) {
10046
+ const response = await callHermesApi(
10047
+ "/api/jobs",
10048
+ {
10049
+ method: "POST",
10050
+ body: JSON.stringify(input),
10051
+ headers: { "content-type": "application/json" }
10052
+ },
10053
+ options
10054
+ );
10055
+ const payload = await readJsonResponse(response);
10056
+ if (!isRecord(payload.job)) {
10057
+ throw new LinkHttpError(
10058
+ 502,
10059
+ "hermes_cron_job_invalid",
10060
+ "Hermes API Server did not return a cron job"
10061
+ );
10062
+ }
10063
+ return { ...payload.job };
10064
+ }
10065
+ async function updateHermesCronJob(jobId, input, options = {}) {
10066
+ const response = await callHermesApi(
10067
+ `/api/jobs/${encodeURIComponent(jobId)}`,
10068
+ {
10069
+ method: "PATCH",
10070
+ body: JSON.stringify(input),
10071
+ headers: { "content-type": "application/json" }
10072
+ },
10073
+ options
10074
+ );
10075
+ const payload = await readJsonResponse(response);
10076
+ if (!isRecord(payload.job)) {
10077
+ throw new LinkHttpError(
10078
+ 502,
10079
+ "hermes_cron_job_invalid",
10080
+ "Hermes API Server did not return a cron job"
10081
+ );
10082
+ }
10083
+ return { ...payload.job };
10084
+ }
10085
+ async function deleteHermesCronJob(jobId, options = {}) {
10086
+ const response = await callHermesApi(
10087
+ `/api/jobs/${encodeURIComponent(jobId)}`,
10088
+ { method: "DELETE" },
10089
+ options
10090
+ );
10091
+ await readJsonResponse(response);
10092
+ }
10093
+ async function runHermesCronJobAction(jobId, action, options = {}) {
10094
+ const response = await callHermesApi(
10095
+ `/api/jobs/${encodeURIComponent(jobId)}/${action}`,
10096
+ { method: "POST" },
10097
+ options
10098
+ );
10099
+ const payload = await readJsonResponse(response);
10100
+ if (!isRecord(payload.job)) {
10101
+ throw new LinkHttpError(
10102
+ 502,
10103
+ "hermes_cron_job_invalid",
10104
+ "Hermes API Server did not return a cron job"
10105
+ );
10106
+ }
10107
+ return { ...payload.job };
10108
+ }
10109
+ async function createHermesRun(input, options = {}) {
10110
+ const response = await callHermesApi(
10111
+ "/v1/runs",
10112
+ {
10113
+ method: "POST",
10114
+ body: JSON.stringify(input),
10115
+ headers: { "content-type": "application/json" }
10116
+ },
10117
+ options
10118
+ );
10119
+ if (response.status === 404 || response.status === 503) {
10120
+ assertHermesRunsApiSupported(
10121
+ await readHermesVersion().catch(() => null),
10122
+ response.status
10123
+ );
10124
+ throw new LinkHttpError(
10125
+ 503,
10126
+ "hermes_api_server_unavailable",
10127
+ "Hermes API Server is unavailable"
10128
+ );
8642
10129
  }
8643
10130
  const payload = await readJsonResponse(response);
8644
- const runId = readString8(payload, "run_id") ?? readString8(payload, "runId") ?? readString8(payload, "id");
10131
+ const runId = readString10(payload, "run_id") ?? readString10(payload, "runId") ?? readString10(payload, "id");
8645
10132
  if (!runId) {
8646
10133
  throw new LinkHttpError(
8647
10134
  502,
@@ -8754,10 +10241,10 @@ async function cancelHermesRun(runId, options = {}) {
8754
10241
  );
8755
10242
  }
8756
10243
  }
8757
- async function callHermesApi(path24, init, options) {
10244
+ async function callHermesApi(path25, init, options) {
8758
10245
  const method = init.method ?? "GET";
8759
10246
  const startedAt = Date.now();
8760
- void options.logger?.debug("hermes_api_request_started", { method, path: path24 });
10247
+ void options.logger?.debug("hermes_api_request_started", { method, path: path25 });
8761
10248
  const availability = await ensureHermesApiServerAvailable({
8762
10249
  fetchImpl: options.fetchImpl,
8763
10250
  logger: options.logger,
@@ -8765,21 +10252,21 @@ async function callHermesApi(path24, init, options) {
8765
10252
  });
8766
10253
  let config = availability.configResult.apiServer;
8767
10254
  const fetcher = options.fetchImpl ?? fetch;
8768
- const request = () => fetchHermesApi(fetcher, config, path24, init, options);
10255
+ const request = () => fetchHermesApi(fetcher, config, path25, init, options);
8769
10256
  let response;
8770
10257
  try {
8771
10258
  response = await request();
8772
10259
  } catch (error) {
8773
- logHermesApiError(options.logger, method, path24, startedAt, error);
10260
+ logHermesApiError(options.logger, method, path25, startedAt, error);
8774
10261
  throw error;
8775
10262
  }
8776
10263
  if (response.status !== 401) {
8777
- logHermesApiResponse(options.logger, method, path24, startedAt, response);
10264
+ logHermesApiResponse(options.logger, method, path25, startedAt, response);
8778
10265
  return response;
8779
10266
  }
8780
10267
  void options.logger?.warn("hermes_api_request_retrying_after_401", {
8781
10268
  method,
8782
- path: path24,
10269
+ path: path25,
8783
10270
  duration_ms: Date.now() - startedAt
8784
10271
  });
8785
10272
  const refreshedAvailability = await ensureHermesApiServerAvailable({
@@ -8792,20 +10279,20 @@ async function callHermesApi(path24, init, options) {
8792
10279
  try {
8793
10280
  response = await request();
8794
10281
  } catch (error) {
8795
- logHermesApiError(options.logger, method, path24, startedAt, error);
10282
+ logHermesApiError(options.logger, method, path25, startedAt, error);
8796
10283
  throw error;
8797
10284
  }
8798
- logHermesApiResponse(options.logger, method, path24, startedAt, response);
10285
+ logHermesApiResponse(options.logger, method, path25, startedAt, response);
8799
10286
  return response;
8800
10287
  }
8801
- async function fetchHermesApi(fetcher, config, path24, init, options) {
10288
+ async function fetchHermesApi(fetcher, config, path25, init, options) {
8802
10289
  const headers = new Headers(init.headers);
8803
10290
  headers.set("accept", headers.get("accept") ?? "application/json");
8804
10291
  if (config.key) {
8805
10292
  headers.set("x-api-key", config.key);
8806
10293
  headers.set("authorization", `Bearer ${config.key}`);
8807
10294
  }
8808
- return await fetcher(`http://127.0.0.1:${config.port}${path24}`, {
10295
+ return await fetcher(`http://127.0.0.1:${config.port}${path25}`, {
8809
10296
  ...init,
8810
10297
  headers
8811
10298
  }).catch((error) => {
@@ -8813,7 +10300,7 @@ async function fetchHermesApi(fetcher, config, path24, init, options) {
8813
10300
  throw error;
8814
10301
  }
8815
10302
  void options.logger?.warn("hermes_api_server_connect_failed", {
8816
- path: path24,
10303
+ path: path25,
8817
10304
  port: config.port ?? null,
8818
10305
  error: error instanceof Error ? error.message : String(error)
8819
10306
  });
@@ -8824,10 +10311,10 @@ async function fetchHermesApi(fetcher, config, path24, init, options) {
8824
10311
  );
8825
10312
  });
8826
10313
  }
8827
- function logHermesApiResponse(logger, method, path24, startedAt, response) {
10314
+ function logHermesApiResponse(logger, method, path25, startedAt, response) {
8828
10315
  const fields = {
8829
10316
  method,
8830
- path: path24,
10317
+ path: path25,
8831
10318
  status: response.status,
8832
10319
  duration_ms: Date.now() - startedAt
8833
10320
  };
@@ -8847,10 +10334,10 @@ async function logHermesApiFailureResponse(logger, fields, response) {
8847
10334
  ...upstreamError ? { upstream_error: upstreamError } : {}
8848
10335
  });
8849
10336
  }
8850
- function logHermesApiError(logger, method, path24, startedAt, error) {
10337
+ function logHermesApiError(logger, method, path25, startedAt, error) {
8851
10338
  void logger?.warn("hermes_api_request_failed", {
8852
10339
  method,
8853
- path: path24,
10340
+ path: path25,
8854
10341
  duration_ms: Date.now() - startedAt,
8855
10342
  ...error instanceof LinkHttpError ? { status: error.status, code: error.code } : {},
8856
10343
  error: error instanceof Error ? error.message : String(error)
@@ -8899,23 +10386,23 @@ function isRecord(value) {
8899
10386
  }
8900
10387
  function readUpstreamMessage(payload, raw) {
8901
10388
  const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
8902
- const message = readString8(error ?? {}, "message") ?? readString8(payload ?? {}, "message");
10389
+ const message = readString10(error ?? {}, "message") ?? readString10(payload ?? {}, "message");
8903
10390
  if (message) {
8904
10391
  return message;
8905
10392
  }
8906
10393
  const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
8907
10394
  return body || "empty response body";
8908
10395
  }
8909
- function readString8(payload, key) {
10396
+ function readString10(payload, key) {
8910
10397
  const value = payload[key];
8911
10398
  return typeof value === "string" && value.trim() ? value.trim() : null;
8912
10399
  }
8913
10400
 
8914
10401
  // src/conversations/history-builder.ts
8915
- import { readFile as readFile8, stat as stat7 } from "fs/promises";
8916
- import { createRequire as createRequire3 } from "module";
8917
- import path13 from "path";
8918
- var nodeRequire3 = createRequire3(import.meta.url);
10402
+ import { readFile as readFile10, stat as stat9 } from "fs/promises";
10403
+ import { createRequire as createRequire4 } from "module";
10404
+ import path15 from "path";
10405
+ var nodeRequire4 = createRequire4(import.meta.url);
8919
10406
  var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
8920
10407
  var HERMES_HISTORY_COLUMNS = [
8921
10408
  "role",
@@ -8976,13 +10463,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
8976
10463
  }
8977
10464
  const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
8978
10465
  const profileDir = resolveHermesProfileDir(normalizedProfileName);
8979
- const dbPath = path13.join(profileDir, "state.db");
10466
+ const dbPath = path15.join(profileDir, "state.db");
8980
10467
  const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
8981
10468
  sessionsDir: value.sessionsDir,
8982
10469
  configured: value.configured,
8983
10470
  configError: false
8984
10471
  })).catch(() => ({
8985
- sessionsDir: path13.join(profileDir, "sessions"),
10472
+ sessionsDir: path15.join(profileDir, "sessions"),
8986
10473
  configured: false,
8987
10474
  configError: true
8988
10475
  }));
@@ -9022,8 +10509,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
9022
10509
  };
9023
10510
  }
9024
10511
  async function readHermesStateDbHistory(dbPath, sessionId) {
9025
- const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
9026
- if (isNodeError9(error, "ENOENT")) {
10512
+ const exists = await stat9(dbPath).then((value) => value.isFile()).catch((error) => {
10513
+ if (isNodeError11(error, "ENOENT")) {
9027
10514
  return false;
9028
10515
  }
9029
10516
  throw error;
@@ -9040,9 +10527,9 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
9040
10527
  if (!isValidSessionFileStem(sessionId)) {
9041
10528
  return empty;
9042
10529
  }
9043
- const transcriptPath = path13.join(sessionsDir, `${sessionId}.jsonl`);
9044
- const raw = await readFile8(transcriptPath, "utf8").catch((error) => {
9045
- if (isNodeError9(error, "ENOENT")) {
10530
+ const transcriptPath = path15.join(sessionsDir, `${sessionId}.jsonl`);
10531
+ const raw = await readFile10(transcriptPath, "utf8").catch((error) => {
10532
+ if (isNodeError11(error, "ENOENT")) {
9046
10533
  return "";
9047
10534
  }
9048
10535
  throw error;
@@ -9077,14 +10564,14 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
9077
10564
  function readHistoryRows(dbPath, sessionId) {
9078
10565
  let db = null;
9079
10566
  try {
9080
- const { DatabaseSync } = nodeRequire3(
10567
+ const { DatabaseSync } = nodeRequire4(
9081
10568
  "node:sqlite"
9082
10569
  );
9083
10570
  db = new DatabaseSync(dbPath, {
9084
10571
  readOnly: true,
9085
10572
  timeout: 1e3
9086
10573
  });
9087
- const columns = readTableColumns(db, "messages");
10574
+ const columns = readTableColumns2(db, "messages");
9088
10575
  if (!columns.has("role") || !columns.has("content")) {
9089
10576
  return [];
9090
10577
  }
@@ -9189,7 +10676,7 @@ function normalizeHistoryMessage(role, content) {
9189
10676
  if (role !== "user" && role !== "assistant") {
9190
10677
  return null;
9191
10678
  }
9192
- const text = normalizeContent(content).trim();
10679
+ const text = normalizeContent2(content).trim();
9193
10680
  if (!text) {
9194
10681
  return null;
9195
10682
  }
@@ -9200,7 +10687,7 @@ function normalizeHistoryRecord(record) {
9200
10687
  if (!role) {
9201
10688
  return null;
9202
10689
  }
9203
- const content = normalizeContent(record.content);
10690
+ const content = normalizeContent2(record.content);
9204
10691
  const message = { role, content };
9205
10692
  assignString(message, "tool_call_id", record.tool_call_id);
9206
10693
  assignString(message, "tool_name", record.tool_name);
@@ -9220,7 +10707,7 @@ function normalizeHistoryRecord(record) {
9220
10707
  }
9221
10708
  return message;
9222
10709
  }
9223
- function normalizeContent(value) {
10710
+ function normalizeContent2(value) {
9224
10711
  if (typeof value === "string") {
9225
10712
  return value;
9226
10713
  }
@@ -9262,12 +10749,12 @@ function hasReplayMetadata(message) {
9262
10749
  message.tool_call_id || message.tool_name || message.tool_calls || message.reasoning || message.reasoning_content || message.reasoning_details || message.codex_reasoning_items
9263
10750
  );
9264
10751
  }
9265
- function readTableColumns(db, table) {
10752
+ function readTableColumns2(db, table) {
9266
10753
  return new Set(
9267
10754
  db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
9268
10755
  );
9269
10756
  }
9270
- function isNodeError9(error, code) {
10757
+ function isNodeError11(error, code) {
9271
10758
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
9272
10759
  }
9273
10760
  function isValidProfileName2(value) {
@@ -9285,8 +10772,8 @@ function normalizeProfileForCompare(value) {
9285
10772
 
9286
10773
  // src/hermes/stt.ts
9287
10774
  import { execFile as execFile3 } from "child_process";
9288
- import { access as access2, readFile as readFile9, stat as stat8 } from "fs/promises";
9289
- import path14 from "path";
10775
+ import { access as access2, readFile as readFile11, stat as stat10 } from "fs/promises";
10776
+ import path16 from "path";
9290
10777
  import { promisify as promisify3 } from "util";
9291
10778
  var execFileAsync3 = promisify3(execFile3);
9292
10779
  var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
@@ -9381,7 +10868,7 @@ async function buildHermesSttEnv(profileName) {
9381
10868
  };
9382
10869
  const devSource = await findDevHermesAgentSource();
9383
10870
  if (devSource) {
9384
- env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path14.delimiter);
10871
+ env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path16.delimiter);
9385
10872
  }
9386
10873
  return env;
9387
10874
  }
@@ -9428,14 +10915,14 @@ async function resolveHermesPythonCommand() {
9428
10915
  };
9429
10916
  }
9430
10917
  async function resolveExecutablePath(command) {
9431
- if (path14.isAbsolute(command)) {
10918
+ if (path16.isAbsolute(command)) {
9432
10919
  return await isExecutableFile(command) ? command : null;
9433
10920
  }
9434
10921
  const pathEnv = process.env.PATH ?? "";
9435
10922
  const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
9436
- for (const dir of pathEnv.split(path14.delimiter)) {
10923
+ for (const dir of pathEnv.split(path16.delimiter)) {
9437
10924
  for (const extension of extensions) {
9438
- const candidate = path14.join(dir, `${command}${extension}`);
10925
+ const candidate = path16.join(dir, `${command}${extension}`);
9439
10926
  if (await isExecutableFile(candidate)) {
9440
10927
  return candidate;
9441
10928
  }
@@ -9445,7 +10932,7 @@ async function resolveExecutablePath(command) {
9445
10932
  }
9446
10933
  async function isExecutableFile(filePath) {
9447
10934
  try {
9448
- const info = await stat8(filePath);
10935
+ const info = await stat10(filePath);
9449
10936
  if (!info.isFile()) {
9450
10937
  return false;
9451
10938
  }
@@ -9456,7 +10943,7 @@ async function isExecutableFile(filePath) {
9456
10943
  }
9457
10944
  }
9458
10945
  async function readShebang(filePath) {
9459
- const raw = await readFile9(filePath, "utf8").catch(() => "");
10946
+ const raw = await readFile11(filePath, "utf8").catch(() => "");
9460
10947
  const firstLine = raw.split(/\r?\n/u)[0]?.trim() ?? "";
9461
10948
  return firstLine.startsWith("#!") ? firstLine.slice(2).trim() : null;
9462
10949
  }
@@ -9475,8 +10962,8 @@ function shebangToPythonCommand(shebang) {
9475
10962
  }
9476
10963
  async function findDevHermesAgentSource() {
9477
10964
  const candidates = [
9478
- path14.resolve(process.cwd(), "reference/hermes-agent"),
9479
- path14.resolve(process.cwd(), "../../reference/hermes-agent")
10965
+ path16.resolve(process.cwd(), "reference/hermes-agent"),
10966
+ path16.resolve(process.cwd(), "../../reference/hermes-agent")
9480
10967
  ];
9481
10968
  for (const candidate of candidates) {
9482
10969
  if (await isDirectory(candidate)) {
@@ -9486,7 +10973,7 @@ async function findDevHermesAgentSource() {
9486
10973
  return null;
9487
10974
  }
9488
10975
  async function isDirectory(candidate) {
9489
- return stat8(candidate).then((info) => info.isDirectory()).catch(() => false);
10976
+ return stat10(candidate).then((info) => info.isDirectory()).catch(() => false);
9490
10977
  }
9491
10978
  function compactProcessOutput(value) {
9492
10979
  const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
@@ -9538,8 +11025,8 @@ function parseSseBlock(block) {
9538
11025
  if (decoded === null) {
9539
11026
  return null;
9540
11027
  }
9541
- const payload = toRecord7(decoded);
9542
- const payloadType = (readString9(payload, "type") ?? readString9(payload, "event") ?? readString9(payload, "object") ?? eventName) || "message";
11028
+ const payload = toRecord9(decoded);
11029
+ const payloadType = (readString11(payload, "type") ?? readString11(payload, "event") ?? readString11(payload, "object") ?? eventName) || "message";
9543
11030
  return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
9544
11031
  }
9545
11032
  function decodeJson(value) {
@@ -9555,11 +11042,11 @@ function decodeJson(value) {
9555
11042
  return { type: "message.delta", delta: value };
9556
11043
  }
9557
11044
  }
9558
- function readString9(body, key) {
11045
+ function readString11(body, key) {
9559
11046
  const value = body[key];
9560
11047
  return typeof value === "string" && value.trim() ? value.trim() : null;
9561
11048
  }
9562
- function toRecord7(value) {
11049
+ function toRecord9(value) {
9563
11050
  return typeof value === "object" && value !== null ? value : {};
9564
11051
  }
9565
11052
 
@@ -9583,8 +11070,8 @@ function normalizeHermesStreamEvent(event) {
9583
11070
  };
9584
11071
  }
9585
11072
  if (event.eventName === "hermes.tool.progress") {
9586
- const toolName = readString10(event.payload, "tool") ?? readString10(event.payload, "name") ?? "tool";
9587
- const preview = readString10(event.payload, "label") ?? readString10(event.payload, "preview") ?? toolName;
11073
+ const toolName = readString12(event.payload, "tool") ?? readString12(event.payload, "name") ?? "tool";
11074
+ const preview = readString12(event.payload, "label") ?? readString12(event.payload, "preview") ?? toolName;
9588
11075
  return {
9589
11076
  ...event,
9590
11077
  payloadType: "tool.started",
@@ -9652,12 +11139,12 @@ function normalizeHermesResponseEvent(event) {
9652
11139
  }
9653
11140
  }
9654
11141
  function normalizeResponseOutputItemAdded(event) {
9655
- const item = toRecord8(event.payload.item);
9656
- if (readString10(item, "type") !== "function_call") {
11142
+ const item = toRecord10(event.payload.item);
11143
+ if (readString12(item, "type") !== "function_call") {
9657
11144
  return null;
9658
11145
  }
9659
- const toolName = readString10(item, "name") ?? "tool";
9660
- const argumentsValue = parseJsonValue(item.arguments) ?? item.arguments;
11146
+ const toolName = readString12(item, "name") ?? "tool";
11147
+ const argumentsValue = parseJsonValue2(item.arguments) ?? item.arguments;
9661
11148
  return {
9662
11149
  ...event,
9663
11150
  payloadType: "tool.started",
@@ -9666,60 +11153,60 @@ function normalizeResponseOutputItemAdded(event) {
9666
11153
  tool: toolName,
9667
11154
  tool_name: toolName,
9668
11155
  name: toolName,
9669
- tool_call_id: readString10(item, "call_id") ?? readString10(item, "id"),
11156
+ tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
9670
11157
  arguments: argumentsValue,
9671
11158
  preview: toolName,
9672
- response_item_id: readString10(item, "id") ?? void 0
11159
+ response_item_id: readString12(item, "id") ?? void 0
9673
11160
  }
9674
11161
  };
9675
11162
  }
9676
11163
  function normalizeResponseOutputItemDone(event) {
9677
- const item = toRecord8(event.payload.item);
9678
- if (readString10(item, "type") !== "function_call_output") {
11164
+ const item = toRecord10(event.payload.item);
11165
+ if (readString12(item, "type") !== "function_call_output") {
9679
11166
  return null;
9680
11167
  }
9681
11168
  const output = readResponseItemOutput(item.output);
9682
- const parsedOutput = parseJsonValue(output);
11169
+ const parsedOutput = parseJsonValue2(output);
9683
11170
  return {
9684
11171
  ...event,
9685
11172
  payloadType: "tool.completed",
9686
11173
  payload: {
9687
11174
  type: "tool.completed",
9688
- tool_call_id: readString10(item, "call_id") ?? readString10(item, "id"),
9689
- status: readString10(item, "status") ?? "completed",
11175
+ tool_call_id: readString12(item, "call_id") ?? readString12(item, "id"),
11176
+ status: readString12(item, "status") ?? "completed",
9690
11177
  output,
9691
11178
  content: output,
9692
11179
  result: parsedOutput ?? output,
9693
- response_item_id: readString10(item, "id") ?? void 0
11180
+ response_item_id: readString12(item, "id") ?? void 0
9694
11181
  }
9695
11182
  };
9696
11183
  }
9697
11184
  function normalizeResponseCompleted(event) {
9698
- const response = toRecord8(event.payload.response);
11185
+ const response = toRecord10(event.payload.response);
9699
11186
  return {
9700
11187
  ...event,
9701
11188
  payloadType: "run.completed",
9702
11189
  payload: {
9703
11190
  type: "run.completed",
9704
- response_id: readString10(response, "id") ?? readString10(event.payload, "id"),
9705
- usage: toRecord8(response.usage),
11191
+ response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
11192
+ usage: toRecord10(response.usage),
9706
11193
  response
9707
11194
  }
9708
11195
  };
9709
11196
  }
9710
11197
  function normalizeResponseFailed(event) {
9711
- const response = toRecord8(event.payload.response);
9712
- const error = toRecord8(response.error);
11198
+ const response = toRecord10(event.payload.response);
11199
+ const error = toRecord10(response.error);
9713
11200
  return {
9714
11201
  ...event,
9715
11202
  payloadType: "run.failed",
9716
11203
  payload: {
9717
11204
  type: "run.failed",
9718
- response_id: readString10(response, "id") ?? readString10(event.payload, "id"),
11205
+ response_id: readString12(response, "id") ?? readString12(event.payload, "id"),
9719
11206
  error: {
9720
- message: readString10(error, "message") ?? readString10(event.payload, "message") ?? "Hermes run failed"
11207
+ message: readString12(error, "message") ?? readString12(event.payload, "message") ?? "Hermes run failed"
9721
11208
  },
9722
- usage: toRecord8(response.usage),
11209
+ usage: toRecord10(response.usage),
9723
11210
  response
9724
11211
  }
9725
11212
  };
@@ -9743,8 +11230,8 @@ function readErrorMessage2(payload) {
9743
11230
  if (typeof payload.error === "string" && payload.error.trim()) {
9744
11231
  return payload.error.trim();
9745
11232
  }
9746
- const error = toRecord8(payload.error);
9747
- return readString10(error, "message") ?? readString10(payload, "message");
11233
+ const error = toRecord10(payload.error);
11234
+ return readString12(error, "message") ?? readString12(payload, "message");
9748
11235
  }
9749
11236
  function readDelta(payload) {
9750
11237
  return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
@@ -9779,15 +11266,15 @@ function isTopLevelErrorEvent(event) {
9779
11266
  }
9780
11267
  function readChatCompletionDelta(payload) {
9781
11268
  const choice = readFirstChoice(payload);
9782
- const delta = toRecord8(choice.delta);
11269
+ const delta = toRecord10(choice.delta);
9783
11270
  return readText2(delta, "content");
9784
11271
  }
9785
11272
  function readChatCompletionFinishReason(payload) {
9786
11273
  const choice = readFirstChoice(payload);
9787
- return readString10(choice, "finish_reason") ?? readString10(choice, "finishReason");
11274
+ return readString12(choice, "finish_reason") ?? readString12(choice, "finishReason");
9788
11275
  }
9789
11276
  function readChatCompletionUsage(payload) {
9790
- const usage = toRecord8(payload.usage);
11277
+ const usage = toRecord10(payload.usage);
9791
11278
  const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
9792
11279
  const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
9793
11280
  const total = readInteger2(usage, "total_tokens");
@@ -9815,7 +11302,7 @@ function readFirstChoice(payload) {
9815
11302
  if (!Array.isArray(choices)) {
9816
11303
  return {};
9817
11304
  }
9818
- return toRecord8(choices[0]);
11305
+ return toRecord10(choices[0]);
9819
11306
  }
9820
11307
  function readInteger2(payload, key) {
9821
11308
  const value = payload[key];
@@ -9828,7 +11315,7 @@ function readInteger2(payload, key) {
9828
11315
  }
9829
11316
  return void 0;
9830
11317
  }
9831
- function readString10(payload, key) {
11318
+ function readString12(payload, key) {
9832
11319
  const value = payload[key];
9833
11320
  return typeof value === "string" && value.trim() ? value.trim() : null;
9834
11321
  }
@@ -9843,10 +11330,10 @@ function readResponseItemOutput(value) {
9843
11330
  if (!Array.isArray(value)) {
9844
11331
  return stringifyJsonValue(value);
9845
11332
  }
9846
- const text = value.map(toRecord8).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
11333
+ const text = value.map(toRecord10).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
9847
11334
  return text || stringifyJsonValue(value);
9848
11335
  }
9849
- function parseJsonValue(value) {
11336
+ function parseJsonValue2(value) {
9850
11337
  if (typeof value !== "string" || !value.trim()) {
9851
11338
  return null;
9852
11339
  }
@@ -9869,13 +11356,11 @@ function stringifyJsonValue(value) {
9869
11356
  return String(value);
9870
11357
  }
9871
11358
  }
9872
- function toRecord8(value) {
11359
+ function toRecord10(value) {
9873
11360
  return typeof value === "object" && value !== null ? value : {};
9874
11361
  }
9875
11362
 
9876
11363
  // src/conversations/run-lifecycle.ts
9877
- var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
9878
- var MAX_MEDIA_IMPORT_FAILURES = 20;
9879
11364
  var ConversationRunLifecycle = class {
9880
11365
  constructor(deps) {
9881
11366
  this.deps = deps;
@@ -10288,7 +11773,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10288
11773
  assistant.agent_events ?? [],
10289
11774
  agentEvent
10290
11775
  );
10291
- appendAgentEventBlock(assistant, agentEvent, event.created_at);
11776
+ appendAgentEventBlock2(assistant, agentEvent, event.created_at);
10292
11777
  assistant.updated_at = event.created_at;
10293
11778
  await this.deps.writeSnapshot(conversationId, snapshot);
10294
11779
  }
@@ -10357,8 +11842,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10357
11842
  }
10358
11843
  const textPart = assistant.parts.find((part) => part.type === "text");
10359
11844
  const currentText = textPart?.text ?? "";
10360
- const pendingDeliveryText = readString11(
10361
- toRecord9(assistant.hermes),
11845
+ const pendingDeliveryText = readString13(
11846
+ toRecord11(assistant.hermes),
10362
11847
  "pending_media_delivery_text"
10363
11848
  );
10364
11849
  const normalizedDelta = normalizeStreamingTextDelta(
@@ -10373,7 +11858,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10373
11858
  pendingDeliveryText ?? ""
10374
11859
  );
10375
11860
  const nextHermes = {
10376
- ...toRecord9(assistant.hermes),
11861
+ ...toRecord11(assistant.hermes),
10377
11862
  ...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
10378
11863
  };
10379
11864
  if (!extracted.pendingText) {
@@ -10389,7 +11874,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10389
11874
  assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
10390
11875
  assistant.raw = { format: "hermes-run-event", payload: event.rawPayload };
10391
11876
  if (extracted.visibleText) {
10392
- appendTextBlock(assistant, extracted.visibleText, assistant.updated_at);
11877
+ appendTextBlock2(assistant, extracted.visibleText, assistant.updated_at);
10393
11878
  }
10394
11879
  await this.deps.writeSnapshot(conversationId, snapshot);
10395
11880
  if (extracted.visibleText) {
@@ -10626,119 +12111,11 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10626
12111
  return snapshot?.runs.find((item) => item.id === runId)?.assistant_message_id;
10627
12112
  }
10628
12113
  async importMediaReferences(conversationId, runId, messageId, references) {
10629
- if (references.length === 0) {
10630
- return;
10631
- }
10632
- const snapshot = await this.deps.readSnapshot(conversationId);
10633
- const assistant = snapshot.messages.find(
10634
- (message) => message.id === messageId
10635
- );
10636
- if (!assistant) {
10637
- return;
10638
- }
10639
- const importedSourceKeys = readImportedMediaSourceKeys(assistant);
10640
- const failedSourceKeys = readFailedMediaSourceKeys(assistant);
10641
- const failureRecordsByKey = new Map(
10642
- readMediaImportFailures(assistant).map((failure) => [
10643
- failure.key,
10644
- failure
10645
- ])
10646
- );
10647
- const importedParts = [];
10648
- const newFailures = [];
10649
- for (const reference of references.slice(0, 10)) {
10650
- let sourceKey;
10651
- try {
10652
- sourceKey = mediaSourceKey(reference.path);
10653
- if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
10654
- continue;
10655
- }
10656
- const blob = await this.writeBlobFromFile(conversationId, reference);
10657
- const part = {
10658
- type: reference.kind ?? mediaKindForMime(blob.mime),
10659
- blob: blob.id,
10660
- mime: blob.mime,
10661
- size: blob.size,
10662
- filename: blob.filename,
10663
- url: `/api/v1/conversations/${encodeURIComponent(conversationId)}/blobs/${encodeURIComponent(blob.id)}`
10664
- };
10665
- assistant.parts.push(part);
10666
- assistant.attachments.push({
10667
- blob_id: blob.id,
10668
- mime: blob.mime,
10669
- size: blob.size,
10670
- filename: blob.filename,
10671
- source: "hermes_output"
10672
- });
10673
- importedSourceKeys.add(sourceKey);
10674
- importedParts.push(part);
10675
- } catch (error) {
10676
- if (sourceKey && !failedSourceKeys.has(sourceKey)) {
10677
- const failure = describeMediaImportFailure(reference, sourceKey, error);
10678
- failedSourceKeys.add(sourceKey);
10679
- failureRecordsByKey.set(sourceKey, failure);
10680
- newFailures.push(failure);
10681
- }
10682
- void this.deps.logger.warn("conversation_media_import_failed", {
10683
- conversation_id: conversationId,
10684
- run_id: runId,
10685
- message_id: messageId,
10686
- error: error instanceof Error ? error.message : String(error)
10687
- });
10688
- }
10689
- }
10690
- if (importedParts.length === 0 && newFailures.length === 0) {
10691
- return;
10692
- }
10693
- assistant.hermes = {
10694
- ...toRecord9(assistant.hermes),
10695
- imported_media_source_keys: [...importedSourceKeys],
10696
- media_import_failed_source_keys: [...failedSourceKeys],
10697
- media_import_failures: [...failureRecordsByKey.values()].slice(
10698
- -MAX_MEDIA_IMPORT_FAILURES
10699
- )
10700
- };
10701
- assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
10702
- await this.deps.writeSnapshot(conversationId, snapshot);
10703
- if (importedParts.length > 0) {
10704
- await this.deps.appendEvent(conversationId, {
10705
- type: "message.parts.created",
10706
- message_id: messageId,
10707
- run_id: runId,
10708
- payload: { parts: importedParts }
10709
- });
10710
- }
10711
- }
10712
- async writeBlobFromFile(conversationId, source) {
10713
- const sourcePath = resolveMediaSourcePath(source.path);
10714
- const fileStat = await stat9(sourcePath).catch((error) => {
10715
- if (isNodeError10(error, "ENOENT")) {
10716
- throw new LinkHttpError(
10717
- 404,
10718
- "media_source_not_found",
10719
- "Hermes output file was not found"
10720
- );
10721
- }
10722
- throw error;
10723
- });
10724
- if (!fileStat.isFile()) {
10725
- throw new LinkHttpError(
10726
- 400,
10727
- "media_source_not_file",
10728
- "Hermes output media source is not a file"
10729
- );
10730
- }
10731
- if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
10732
- throw new LinkHttpError(
10733
- 413,
10734
- "media_source_too_large",
10735
- "Hermes output media source is too large"
10736
- );
10737
- }
10738
- return this.deps.writeBlob(conversationId, {
10739
- bytes: await readFile10(sourcePath),
10740
- filename: path15.basename(sourcePath),
10741
- mime: source.mime ?? inferMimeType(sourcePath)
12114
+ await importMediaReferencesForMessage(this.deps, {
12115
+ conversationId,
12116
+ runId,
12117
+ messageId,
12118
+ references
10742
12119
  });
10743
12120
  }
10744
12121
  async readHermesCronJobIds(profileName) {
@@ -10748,7 +12125,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10748
12125
  includeDisabled: true
10749
12126
  });
10750
12127
  return new Set(
10751
- jobs.map((job) => readString11(job, "id") ?? readString11(job, "job_id")).filter((id) => Boolean(id))
12128
+ jobs.map((job) => readString13(job, "id") ?? readString13(job, "job_id")).filter((id) => Boolean(id))
10752
12129
  );
10753
12130
  }
10754
12131
  async bindNewCronJobsCreatedByRun(input) {
@@ -10773,7 +12150,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
10773
12150
  "",
10774
12151
  "Delivery staging directory for this run:",
10775
12152
  `- ${deliveryStagingDir}`,
10776
- "Before final delivery markers, copy each deliverable file into this directory and reference the staged copy path in <hermes_link_delivery>."
12153
+ "Copy every deliverable file into this directory, then run:",
12154
+ `hermeslink deliver ${quoteShellArg(deliveryStagingDir)}`
10777
12155
  ] : [],
10778
12156
  "",
10779
12157
  "Current runtime selected by Hermes Link:",
@@ -10782,52 +12160,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
10782
12160
  "If the user asks what model or provider you are currently using, answer from this runtime selection instead of inferring from earlier conversation history."
10783
12161
  ].join("\n");
10784
12162
  }
10785
- function readFailedMediaSourceKeys(message) {
10786
- const hermes = toRecord9(message.hermes);
10787
- const keys = hermes.media_import_failed_source_keys;
10788
- if (!Array.isArray(keys)) {
10789
- return /* @__PURE__ */ new Set();
10790
- }
10791
- return new Set(
10792
- keys.filter(
10793
- (key) => typeof key === "string" && key.length > 0
10794
- )
10795
- );
10796
- }
10797
- function readMediaImportFailures(message) {
10798
- const hermes = toRecord9(message.hermes);
10799
- const failures = hermes.media_import_failures;
10800
- if (!Array.isArray(failures)) {
10801
- return [];
10802
- }
10803
- return failures.flatMap((item) => {
10804
- const record = toRecord9(item);
10805
- const key = readString11(record, "key");
10806
- const filename = readString11(record, "filename");
10807
- const reason = readString11(record, "reason");
10808
- if (!key || !filename || !reason) {
10809
- return [];
10810
- }
10811
- return [
10812
- {
10813
- key,
10814
- filename,
10815
- reason,
10816
- ...readString11(record, "code") ? { code: readString11(record, "code") } : {}
10817
- }
10818
- ];
10819
- });
10820
- }
10821
- function describeMediaImportFailure(reference, sourceKey, error) {
10822
- return {
10823
- key: sourceKey,
10824
- filename: sanitizeFilename(reference.path, "attachment"),
10825
- reason: error instanceof Error ? error.message : String(error),
10826
- ...isNodeError10(error) && error.code ? { code: error.code } : {}
10827
- };
10828
- }
10829
12163
  function appendMediaImportFailureNotice(message) {
10830
- const hermes = toRecord9(message.hermes);
12164
+ const hermes = toRecord11(message.hermes);
10831
12165
  if (hermes.media_import_failure_notice_appended === true) {
10832
12166
  return;
10833
12167
  }
@@ -10849,6 +12183,9 @@ function appendMediaImportFailureNotice(message) {
10849
12183
  media_import_failure_notice_appended: true
10850
12184
  };
10851
12185
  }
12186
+ function quoteShellArg(value) {
12187
+ return `'${value.replaceAll("'", "'\\''")}'`;
12188
+ }
10852
12189
  function formatMediaImportFailureNotice(failures) {
10853
12190
  const filenames = failures.map((failure) => failure.filename);
10854
12191
  const target = filenames.length === 1 ? `\u6587\u4EF6\u201C${filenames[0]}\u201D` : `${filenames.length} \u4E2A\u6587\u4EF6\uFF08${formatFilenameList(filenames)}\uFF09`;
@@ -10867,18 +12204,18 @@ function formatFilenameList(filenames) {
10867
12204
  return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
10868
12205
  }
10869
12206
  async function readdirWithDirs(directory) {
10870
- return readdir5(directory, { withFileTypes: true }).catch((error) => {
10871
- if (isNodeError10(error, "ENOENT")) {
12207
+ return readdir7(directory, { withFileTypes: true }).catch((error) => {
12208
+ if (isNodeError12(error, "ENOENT")) {
10872
12209
  return [];
10873
12210
  }
10874
12211
  throw error;
10875
12212
  });
10876
12213
  }
10877
- function readString11(payload, key) {
12214
+ function readString13(payload, key) {
10878
12215
  const value = payload[key];
10879
12216
  return typeof value === "string" && value.trim() ? value.trim() : null;
10880
12217
  }
10881
- function toRecord9(value) {
12218
+ function toRecord11(value) {
10882
12219
  return typeof value === "object" && value !== null ? value : {};
10883
12220
  }
10884
12221
  function formatFailureMessage(message, detail) {
@@ -10894,17 +12231,17 @@ function isFileSearchCompletion(payloadType, payload) {
10894
12231
  if (payloadType !== "tool.completed") {
10895
12232
  return false;
10896
12233
  }
10897
- const tool = toRecord9(payload.tool);
10898
- const toolCall = toRecord9(payload.tool_call ?? payload.toolCall);
10899
- const fn = toRecord9(toolCall.function ?? payload.function);
12234
+ const tool = toRecord11(payload.tool);
12235
+ const toolCall = toRecord11(payload.tool_call ?? payload.toolCall);
12236
+ const fn = toRecord11(toolCall.function ?? payload.function);
10900
12237
  const candidates = [
10901
- readString11(payload, "tool_name"),
10902
- readString11(payload, "toolName"),
10903
- readString11(payload, "name"),
10904
- readString11(payload, "tool"),
10905
- readString11(tool, "name"),
10906
- readString11(toolCall, "name"),
10907
- readString11(fn, "name")
12238
+ readString13(payload, "tool_name"),
12239
+ readString13(payload, "toolName"),
12240
+ readString13(payload, "name"),
12241
+ readString13(payload, "tool"),
12242
+ readString13(tool, "name"),
12243
+ readString13(toolCall, "name"),
12244
+ readString13(fn, "name")
10908
12245
  ].filter((value) => Boolean(value)).map(normalizeToolName);
10909
12246
  return candidates.some(
10910
12247
  (name) => [
@@ -10947,7 +12284,7 @@ function isVoicePart(part) {
10947
12284
  function normalizeToolName(value) {
10948
12285
  return value.trim().toLowerCase().replace(/[\s-]+/gu, "_");
10949
12286
  }
10950
- function appendTextBlock(message, delta, updatedAt) {
12287
+ function appendTextBlock2(message, delta, updatedAt) {
10951
12288
  if (!delta) {
10952
12289
  return;
10953
12290
  }
@@ -10970,7 +12307,7 @@ function appendTextBlock(message, delta, updatedAt) {
10970
12307
  }
10971
12308
  message.blocks = blocks;
10972
12309
  }
10973
- function appendAgentEventBlock(message, event, updatedAt) {
12310
+ function appendAgentEventBlock2(message, event, updatedAt) {
10974
12311
  const blocks = [...message.blocks ?? []];
10975
12312
  const matchingIndex = blocks.findIndex((block) => {
10976
12313
  if (block.type !== "agent_events") {
@@ -11059,10 +12396,10 @@ function readResponseId(payload) {
11059
12396
  if (!payload) {
11060
12397
  return null;
11061
12398
  }
11062
- const response = toRecord9(payload.response);
11063
- return readString11(payload, "response_id") ?? readString11(response, "id");
12399
+ const response = toRecord11(payload.response);
12400
+ return readString13(payload, "response_id") ?? readString13(response, "id");
11064
12401
  }
11065
- function isNodeError10(error, code) {
12402
+ function isNodeError12(error, code) {
11066
12403
  if (typeof error !== "object" || error === null || !("code" in error)) {
11067
12404
  return false;
11068
12405
  }
@@ -11151,6 +12488,7 @@ var ConversationService = class {
11151
12488
  orchestration;
11152
12489
  queries;
11153
12490
  runLifecycle;
12491
+ hermesSessionSyncPromise = null;
11154
12492
  async withConversationLock(conversationId, task) {
11155
12493
  const previous = this.conversationLocks.get(conversationId) ?? Promise.resolve();
11156
12494
  let release;
@@ -11211,7 +12549,7 @@ var ConversationService = class {
11211
12549
  async createConversation(input = {}) {
11212
12550
  await this.store.ensureConversationsDir();
11213
12551
  const now = (/* @__PURE__ */ new Date()).toISOString();
11214
- const id = `conv_${randomUUID6().replaceAll("-", "")}`;
12552
+ const id = `conv_${randomUUID7().replaceAll("-", "")}`;
11215
12553
  const title = input.title?.trim() || DEFAULT_CONVERSATION_TITLE;
11216
12554
  const profile = await resolveConversationProfileTarget(
11217
12555
  this.paths,
@@ -11298,7 +12636,7 @@ var ConversationService = class {
11298
12636
  manifest.profile_name_snapshot ?? manifest.profile ?? input.profileName
11299
12637
  );
11300
12638
  const message = {
11301
- id: `msg_${randomUUID6().replaceAll("-", "")}`,
12639
+ id: `msg_${randomUUID7().replaceAll("-", "")}`,
11302
12640
  schema_version: 1,
11303
12641
  conversation_id: manifest.id,
11304
12642
  role: "assistant",
@@ -11342,6 +12680,65 @@ var ConversationService = class {
11342
12680
  async syncCronDeliveries() {
11343
12681
  await syncHermesLinkCronDeliveries(this.paths, this, this.logger);
11344
12682
  }
12683
+ async syncHermesSessions() {
12684
+ if (this.hermesSessionSyncPromise) {
12685
+ return this.hermesSessionSyncPromise;
12686
+ }
12687
+ const task = (async () => {
12688
+ const result = await syncHermesSessionsIntoConversations(
12689
+ this.paths,
12690
+ this.logger
12691
+ );
12692
+ if (result.imported_count > 0 || result.reprojected_count > 0) {
12693
+ await this.rebuildStatisticsIndex();
12694
+ }
12695
+ return result;
12696
+ })();
12697
+ this.hermesSessionSyncPromise = task;
12698
+ try {
12699
+ return await task;
12700
+ } finally {
12701
+ if (this.hermesSessionSyncPromise === task) {
12702
+ this.hermesSessionSyncPromise = null;
12703
+ }
12704
+ }
12705
+ }
12706
+ async deliverStagedFiles(stagingDir) {
12707
+ const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
12708
+ return this.withConversationLock(target.conversationId, async () => {
12709
+ await this.store.readActiveManifest(target.conversationId);
12710
+ const snapshot = await this.store.readSnapshot(target.conversationId);
12711
+ const run = snapshot.runs.find((item) => item.id === target.runId);
12712
+ if (!run) {
12713
+ throw new LinkHttpError(404, "run_not_found", "Run was not found");
12714
+ }
12715
+ const assistant = snapshot.messages.find(
12716
+ (message) => message.id === run.assistant_message_id
12717
+ );
12718
+ if (!assistant) {
12719
+ throw new LinkHttpError(
12720
+ 404,
12721
+ "assistant_message_not_found",
12722
+ "Assistant message was not found"
12723
+ );
12724
+ }
12725
+ return importMediaReferencesForMessage(
12726
+ {
12727
+ logger: this.logger,
12728
+ readSnapshot: (conversationId) => this.store.readSnapshot(conversationId),
12729
+ writeSnapshot: (conversationId, nextSnapshot) => this.store.writeSnapshot(conversationId, nextSnapshot),
12730
+ appendEvent: (conversationId, input) => this.appendEvent(conversationId, input),
12731
+ writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input)
12732
+ },
12733
+ {
12734
+ conversationId: target.conversationId,
12735
+ runId: target.runId,
12736
+ messageId: assistant.id,
12737
+ references: await collectStagedDeliveryReferences(target.stagingDir)
12738
+ }
12739
+ );
12740
+ });
12741
+ }
11345
12742
  async getMessages(conversationId, options = {}) {
11346
12743
  return this.queries.getMessages(conversationId, options);
11347
12744
  }
@@ -11831,7 +13228,7 @@ function findApproval(snapshot, approvalId) {
11831
13228
  }
11832
13229
 
11833
13230
  // src/identity/identity.ts
11834
- import { generateKeyPairSync, randomUUID as randomUUID7, sign } from "crypto";
13231
+ import { generateKeyPairSync, randomUUID as randomUUID8, sign } from "crypto";
11835
13232
  import { mkdir as mkdir9, chmod } from "fs/promises";
11836
13233
  import { z } from "zod";
11837
13234
  var linkIdentitySchema = z.object({
@@ -11859,7 +13256,7 @@ async function ensureIdentity(paths = resolveRuntimePaths()) {
11859
13256
  const { publicKey, privateKey } = generateKeyPairSync("ed25519");
11860
13257
  const now = (/* @__PURE__ */ new Date()).toISOString();
11861
13258
  const identity = {
11862
- install_id: `install_${randomUUID7().replaceAll("-", "")}`,
13259
+ install_id: `install_${randomUUID8().replaceAll("-", "")}`,
11863
13260
  link_id: null,
11864
13261
  public_key_pem: publicKey.export({ type: "spki", format: "pem" }).toString(),
11865
13262
  private_key_pem: privateKey.export({ type: "pkcs8", format: "pem" }).toString(),
@@ -11896,7 +13293,7 @@ function getIdentityStatus(identity) {
11896
13293
  }
11897
13294
 
11898
13295
  // src/security/devices.ts
11899
- import { randomBytes as randomBytes2, randomUUID as randomUUID8, timingSafeEqual, createHash as createHash4 } from "crypto";
13296
+ import { randomBytes as randomBytes2, randomUUID as randomUUID9, timingSafeEqual, createHash as createHash4 } from "crypto";
11900
13297
  var ACCESS_TOKEN_TTL_MS = 15 * 60 * 1e3;
11901
13298
  var REFRESH_TOKEN_TTL_MS = 90 * 24 * 60 * 60 * 1e3;
11902
13299
  var DEVICE_SEEN_WRITE_INTERVAL_MS = 60 * 60 * 1e3;
@@ -11914,7 +13311,7 @@ async function createDeviceSession(input, paths = resolveRuntimePaths()) {
11914
13311
  }
11915
13312
  }
11916
13313
  const device = {
11917
- id: `dev_${randomUUID8().replaceAll("-", "")}`,
13314
+ id: `dev_${randomUUID9().replaceAll("-", "")}`,
11918
13315
  label: normalizeDeviceLabel(input.label),
11919
13316
  platform: normalizeDevicePlatform(input.platform),
11920
13317
  model: normalizeDeviceModel(input.model),
@@ -12363,12 +13760,12 @@ async function readRawBody(request, maxBytes) {
12363
13760
  }
12364
13761
  return Buffer.concat(chunks);
12365
13762
  }
12366
- function readString12(body, key) {
13763
+ function readString14(body, key) {
12367
13764
  const value = body[key];
12368
13765
  return typeof value === "string" && value.trim() ? value.trim() : null;
12369
13766
  }
12370
13767
  function readOptionalProfileName(body) {
12371
- return readString12(body, "profile") ?? readString12(body, "profile_name") ?? readString12(body, "profileName") ?? void 0;
13768
+ return readString14(body, "profile") ?? readString14(body, "profile_name") ?? readString14(body, "profileName") ?? void 0;
12372
13769
  }
12373
13770
  function readStringArray(body, ...keys) {
12374
13771
  for (const key of keys) {
@@ -12406,7 +13803,7 @@ function readPositiveInteger2(value) {
12406
13803
  }
12407
13804
  return void 0;
12408
13805
  }
12409
- function readBoolean(value) {
13806
+ function readBoolean2(value) {
12410
13807
  if (typeof value === "boolean") {
12411
13808
  return value;
12412
13809
  }
@@ -12509,7 +13906,7 @@ function readMessageAttachments(value) {
12509
13906
  }
12510
13907
  const kind = readAttachmentString(record.kind);
12511
13908
  const type = readAttachmentString(record.type);
12512
- const isVoiceNote = readBoolean(record.is_voice_note) ?? readBoolean(record.isVoiceNote);
13909
+ const isVoiceNote = readBoolean2(record.is_voice_note) ?? readBoolean2(record.isVoiceNote);
12513
13910
  const durationMs = readPositiveInteger2(record.duration_ms) ?? readPositiveInteger2(record.durationMs);
12514
13911
  const waveform = readAttachmentWaveform2(
12515
13912
  record.waveform ?? record.waveform_samples ?? record.waveformSamples
@@ -12601,7 +13998,7 @@ function registerConversationRoutes(router, options) {
12601
13998
  ctx.body = {
12602
13999
  ok: true,
12603
14000
  conversation: await conversations.createConversation({
12604
- title: readString12(body, "title") ?? void 0,
14001
+ title: readString14(body, "title") ?? void 0,
12605
14002
  profileName: readOptionalProfileName(body)
12606
14003
  })
12607
14004
  };
@@ -12672,7 +14069,7 @@ function registerConversationRoutes(router, options) {
12672
14069
  router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
12673
14070
  await authenticateRequest(ctx, paths);
12674
14071
  const body = await readJsonBody(ctx.req);
12675
- const content = readString12(body, "content") ?? readString12(body, "text") ?? readString12(body, "input") ?? "";
14072
+ const content = readString14(body, "content") ?? readString14(body, "text") ?? readString14(body, "input") ?? "";
12676
14073
  const attachments = readMessageAttachments(body.attachments ?? body.blobs);
12677
14074
  if (!content && attachments.length === 0) {
12678
14075
  throw new LinkHttpError(
@@ -12688,7 +14085,7 @@ function registerConversationRoutes(router, options) {
12688
14085
  conversationId: ctx.params.conversationId,
12689
14086
  content,
12690
14087
  attachments,
12691
- clientMessageId: readString12(body, "client_message_id") ?? readString12(body, "clientMessageId") ?? void 0,
14088
+ clientMessageId: readString14(body, "client_message_id") ?? readString14(body, "clientMessageId") ?? void 0,
12692
14089
  idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
12693
14090
  profileName: readOptionalProfileName(body)
12694
14091
  })
@@ -12697,7 +14094,7 @@ function registerConversationRoutes(router, options) {
12697
14094
  router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
12698
14095
  await authenticateRequest(ctx, paths);
12699
14096
  const body = await readJsonBody(ctx.req);
12700
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId") ?? readString12(body, "model");
14097
+ const modelId = readString14(body, "model_id") ?? readString14(body, "modelId") ?? readString14(body, "model");
12701
14098
  if (!modelId) {
12702
14099
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
12703
14100
  }
@@ -12727,7 +14124,7 @@ function registerConversationRoutes(router, options) {
12727
14124
  router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
12728
14125
  await authenticateRequest(ctx, paths);
12729
14126
  const body = await readJsonBody(ctx.req);
12730
- const title = readString12(body, "title") ?? readString12(body, "name") ?? readString12(body, "display_name");
14127
+ const title = readString14(body, "title") ?? readString14(body, "name") ?? readString14(body, "display_name");
12731
14128
  if (!title) {
12732
14129
  throw new LinkHttpError(400, "title_required", "title is required");
12733
14130
  }
@@ -12791,7 +14188,7 @@ function registerConversationRoutes(router, options) {
12791
14188
  async (ctx) => {
12792
14189
  await authenticateRequest(ctx, paths);
12793
14190
  const body = await readJsonBody(ctx.req);
12794
- const scope = readString12(body, "scope") ?? "always";
14191
+ const scope = readString14(body, "scope") ?? "always";
12795
14192
  ctx.body = {
12796
14193
  ok: true,
12797
14194
  ...await conversations.resolveApproval({
@@ -12951,26 +14348,26 @@ function createHttpErrorMiddleware(logger) {
12951
14348
  }
12952
14349
 
12953
14350
  // src/hermes/profiles.ts
12954
- import { mkdir as mkdir10, readdir as readdir6, readFile as readFile11, rename as rename4, rm as rm6, stat as stat10 } from "fs/promises";
12955
- import os4 from "os";
12956
- import path16 from "path";
14351
+ import { mkdir as mkdir10, readdir as readdir8, readFile as readFile12, rename as rename4, rm as rm6, stat as stat11 } from "fs/promises";
14352
+ import os5 from "os";
14353
+ import path17 from "path";
12957
14354
  import YAML2 from "yaml";
12958
14355
  var DEFAULT_PROFILE = "default";
12959
- var PROFILE_NAME_PATTERN3 = /^[a-zA-Z0-9._-]{1,64}$/;
14356
+ var PROFILE_NAME_PATTERN4 = /^[a-zA-Z0-9._-]{1,64}$/;
12960
14357
  async function listHermesProfiles(paths = resolveRuntimePaths()) {
12961
14358
  const profiles = /* @__PURE__ */ new Map();
12962
14359
  profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
12963
- const profilesDir = path16.join(os4.homedir(), ".hermes", "profiles");
12964
- const entries = await readdir6(profilesDir, { withFileTypes: true }).catch(
14360
+ const profilesDir = path17.join(os5.homedir(), ".hermes", "profiles");
14361
+ const entries = await readdir8(profilesDir, { withFileTypes: true }).catch(
12965
14362
  (error) => {
12966
- if (isNodeError11(error, "ENOENT")) {
14363
+ if (isNodeError13(error, "ENOENT")) {
12967
14364
  return [];
12968
14365
  }
12969
14366
  throw error;
12970
14367
  }
12971
14368
  );
12972
14369
  for (const entry of entries) {
12973
- if (entry.isDirectory() && PROFILE_NAME_PATTERN3.test(entry.name)) {
14370
+ if (entry.isDirectory() && PROFILE_NAME_PATTERN4.test(entry.name)) {
12974
14371
  profiles.set(entry.name, await profileInfo(entry.name, paths));
12975
14372
  }
12976
14373
  }
@@ -12987,8 +14384,8 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
12987
14384
  async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
12988
14385
  assertProfileName(name);
12989
14386
  const profile = await profileInfo(name, paths);
12990
- const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
12991
- if (isNodeError11(error, "ENOENT")) {
14387
+ const exists = await stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
14388
+ if (isNodeError13(error, "ENOENT")) {
12992
14389
  return false;
12993
14390
  }
12994
14391
  throw error;
@@ -13028,8 +14425,8 @@ async function updateHermesProfileMetadata(name, metadata, paths = resolveRuntim
13028
14425
  async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
13029
14426
  assertMutableProfile(name);
13030
14427
  const profile = await profileInfo(name, paths);
13031
- const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
13032
- if (isNodeError11(error, "ENOENT")) {
14428
+ const exists = await stat11(profile.path).then((value) => value.isDirectory()).catch((error) => {
14429
+ if (isNodeError13(error, "ENOENT")) {
13033
14430
  return false;
13034
14431
  }
13035
14432
  throw error;
@@ -13055,7 +14452,7 @@ async function readHermesProfileCapabilities(name) {
13055
14452
  return {
13056
14453
  defaultModel: listedModels?.defaultModel ?? null,
13057
14454
  modelCount: listedModels?.models.length ?? 0,
13058
- skillCount: await countSkills(path16.join(profileDir, "skills")).catch(
14455
+ skillCount: await countSkills(path17.join(profileDir, "skills")).catch(
13059
14456
  () => 0
13060
14457
  ),
13061
14458
  toolCount: await countConfiguredTools(name).catch(() => 0)
@@ -13093,17 +14490,17 @@ function assertMutableProfile(name) {
13093
14490
  }
13094
14491
  }
13095
14492
  function assertProfileName(name) {
13096
- if (!PROFILE_NAME_PATTERN3.test(name)) {
14493
+ if (!PROFILE_NAME_PATTERN4.test(name)) {
13097
14494
  throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
13098
14495
  }
13099
14496
  }
13100
- function isNodeError11(error, code) {
14497
+ function isNodeError13(error, code) {
13101
14498
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
13102
14499
  }
13103
14500
  async function countSkills(root) {
13104
- const entries = await readdir6(root, { withFileTypes: true }).catch(
14501
+ const entries = await readdir8(root, { withFileTypes: true }).catch(
13105
14502
  (error) => {
13106
- if (isNodeError11(error, "ENOENT")) {
14503
+ if (isNodeError13(error, "ENOENT")) {
13107
14504
  return [];
13108
14505
  }
13109
14506
  throw error;
@@ -13111,7 +14508,7 @@ async function countSkills(root) {
13111
14508
  );
13112
14509
  let count = 0;
13113
14510
  for (const entry of entries) {
13114
- const entryPath = path16.join(root, entry.name);
14511
+ const entryPath = path17.join(root, entry.name);
13115
14512
  if (entry.name === ".git" || entry.name === ".hub") {
13116
14513
  continue;
13117
14514
  }
@@ -13126,11 +14523,11 @@ async function countSkills(root) {
13126
14523
  return count;
13127
14524
  }
13128
14525
  async function countConfiguredTools(profileName) {
13129
- const raw = await readFile11(
14526
+ const raw = await readFile12(
13130
14527
  resolveHermesConfigPath(profileName),
13131
14528
  "utf8"
13132
14529
  ).catch((error) => {
13133
- if (isNodeError11(error, "ENOENT")) {
14530
+ if (isNodeError13(error, "ENOENT")) {
13134
14531
  return "";
13135
14532
  }
13136
14533
  throw error;
@@ -13138,14 +14535,14 @@ async function countConfiguredTools(profileName) {
13138
14535
  if (!raw.trim()) {
13139
14536
  return 0;
13140
14537
  }
13141
- const config = toRecord10(YAML2.parse(raw));
14538
+ const config = toRecord12(YAML2.parse(raw));
13142
14539
  const toolsets = /* @__PURE__ */ new Set();
13143
14540
  collectToolsetValues(config.toolsets, toolsets);
13144
- const platformToolsets = toRecord10(config.platform_toolsets);
14541
+ const platformToolsets = toRecord12(config.platform_toolsets);
13145
14542
  for (const value of Object.values(platformToolsets)) {
13146
14543
  collectToolsetValues(value, toolsets);
13147
14544
  }
13148
- const mcpServers = Object.keys(toRecord10(config.mcp_servers)).length;
14545
+ const mcpServers = Object.keys(toRecord12(config.mcp_servers)).length;
13149
14546
  return toolsets.size + mcpServers;
13150
14547
  }
13151
14548
  function collectToolsetValues(value, target) {
@@ -13159,7 +14556,7 @@ function collectToolsetValues(value, target) {
13159
14556
  target.add(value.trim());
13160
14557
  }
13161
14558
  }
13162
- function toRecord10(value) {
14559
+ function toRecord12(value) {
13163
14560
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
13164
14561
  }
13165
14562
 
@@ -13356,7 +14753,7 @@ function toHermesCronJobInput(input) {
13356
14753
  };
13357
14754
  }
13358
14755
  async function bindAndDecorateCronJobForHermesLink(input) {
13359
- const jobId = readString12(input.job, "id") ?? readString12(input.job, "job_id");
14756
+ const jobId = readString14(input.job, "id") ?? readString14(input.job, "job_id");
13360
14757
  if (!jobId) {
13361
14758
  return input.job;
13362
14759
  }
@@ -13373,9 +14770,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
13373
14770
  }
13374
14771
  function readCronJobCreateInput(body) {
13375
14772
  const input = {};
13376
- const name = readString12(body, "name") ?? readString12(body, "title");
13377
- const prompt = readString12(body, "prompt") ?? readString12(body, "description") ?? readString12(body, "task");
13378
- const schedule = readString12(body, "schedule");
14773
+ const name = readString14(body, "name") ?? readString14(body, "title");
14774
+ const prompt = readString14(body, "prompt") ?? readString14(body, "description") ?? readString14(body, "task");
14775
+ const schedule = readString14(body, "schedule");
13379
14776
  if (!name) {
13380
14777
  throw new LinkHttpError(400, "cron_job_name_required", "name is required");
13381
14778
  }
@@ -13396,7 +14793,7 @@ function readCronJobCreateInput(body) {
13396
14793
  input.name = name;
13397
14794
  input.prompt = prompt;
13398
14795
  input.schedule = schedule;
13399
- input.deliver = readString12(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
14796
+ input.deliver = readString14(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
13400
14797
  const skills = readOptionalCronSkills(body);
13401
14798
  if (skills) {
13402
14799
  input.skills = skills;
@@ -13454,7 +14851,7 @@ function readCronJobUpdateInput(body) {
13454
14851
  if (repeat !== void 0) {
13455
14852
  input.repeat = repeat;
13456
14853
  }
13457
- const enabled = readBoolean(body.enabled);
14854
+ const enabled = readBoolean2(body.enabled);
13458
14855
  if (enabled !== void 0) {
13459
14856
  input.enabled = enabled;
13460
14857
  }
@@ -13624,7 +15021,7 @@ function registerModelConfigRoutes(router, options) {
13624
15021
  router.delete("/api/v1/model-configs", async (ctx) => {
13625
15022
  await authenticateRequest(ctx, paths);
13626
15023
  const body = await readJsonBody(ctx.req);
13627
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId");
15024
+ const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
13628
15025
  if (!modelId) {
13629
15026
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
13630
15027
  }
@@ -13679,7 +15076,7 @@ function registerModelConfigRoutes(router, options) {
13679
15076
  await authenticateRequest(ctx, paths);
13680
15077
  await getHermesProfileStatus(ctx.params.name, paths);
13681
15078
  const body = await readJsonBody(ctx.req);
13682
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId");
15079
+ const modelId = readString14(body, "model_id") ?? readString14(body, "modelId");
13683
15080
  if (!modelId) {
13684
15081
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
13685
15082
  }
@@ -13696,9 +15093,9 @@ function registerModelConfigRoutes(router, options) {
13696
15093
  });
13697
15094
  }
13698
15095
  function readModelConfigInput(body) {
13699
- const id = readString12(body, "id") ?? readString12(body, "model_id") ?? readString12(body, "modelId");
13700
- const provider = readString12(body, "provider") ?? readString12(body, "provider_key") ?? readString12(body, "providerKey");
13701
- const baseUrl = readString12(body, "base_url") ?? readString12(body, "baseUrl");
15096
+ const id = readString14(body, "id") ?? readString14(body, "model_id") ?? readString14(body, "modelId");
15097
+ const provider = readString14(body, "provider") ?? readString14(body, "provider_key") ?? readString14(body, "providerKey");
15098
+ const baseUrl = readString14(body, "base_url") ?? readString14(body, "baseUrl");
13702
15099
  if (!id || !provider || !baseUrl) {
13703
15100
  throw new LinkHttpError(
13704
15101
  400,
@@ -13708,28 +15105,28 @@ function readModelConfigInput(body) {
13708
15105
  }
13709
15106
  return {
13710
15107
  id,
13711
- originalModelId: readString12(body, "original_model_id") ?? readString12(body, "originalModelId") ?? readString12(body, "original_id") ?? void 0,
15108
+ originalModelId: readString14(body, "original_model_id") ?? readString14(body, "originalModelId") ?? readString14(body, "original_id") ?? void 0,
13712
15109
  provider,
13713
- providerName: readString12(body, "provider_name") ?? readString12(body, "providerName") ?? void 0,
15110
+ providerName: readString14(body, "provider_name") ?? readString14(body, "providerName") ?? void 0,
13714
15111
  baseUrl,
13715
- apiKey: readString12(body, "api_key") ?? readString12(body, "apiKey") ?? void 0,
13716
- apiMode: readString12(body, "api_mode") ?? readString12(body, "apiMode") ?? void 0,
15112
+ apiKey: readString14(body, "api_key") ?? readString14(body, "apiKey") ?? void 0,
15113
+ apiMode: readString14(body, "api_mode") ?? readString14(body, "apiMode") ?? void 0,
13717
15114
  contextLength: readPositiveInteger2(
13718
15115
  body.context_length ?? body.contextLength
13719
15116
  ),
13720
- keyEnv: readString12(body, "key_env") ?? readString12(body, "keyEnv") ?? void 0,
13721
- setDefault: readBoolean(body.set_default ?? body.setDefault),
13722
- reasoningEffort: readString12(body, "reasoning_effort") ?? readString12(body, "reasoningEffort") ?? void 0
15117
+ keyEnv: readString14(body, "key_env") ?? readString14(body, "keyEnv") ?? void 0,
15118
+ setDefault: readBoolean2(body.set_default ?? body.setDefault),
15119
+ reasoningEffort: readString14(body, "reasoning_effort") ?? readString14(body, "reasoningEffort") ?? void 0
13723
15120
  };
13724
15121
  }
13725
15122
  function readModelDefaultsInput(body) {
13726
15123
  return {
13727
- taskModelId: readString12(body, "task_model_id") ?? readString12(body, "taskModelId") ?? readString12(body, "default_model_id") ?? readString12(body, "defaultModelId") ?? void 0,
13728
- compressionModelId: readString12(body, "compression_model_id") ?? readString12(body, "compressionModelId") ?? void 0
15124
+ taskModelId: readString14(body, "task_model_id") ?? readString14(body, "taskModelId") ?? readString14(body, "default_model_id") ?? readString14(body, "defaultModelId") ?? void 0,
15125
+ compressionModelId: readString14(body, "compression_model_id") ?? readString14(body, "compressionModelId") ?? void 0
13729
15126
  };
13730
15127
  }
13731
15128
  function shouldReloadGatewayAfterModelConfigChange(body) {
13732
- const explicit = readBoolean(body.reload_gateway ?? body.reloadGateway) ?? (readBoolean(body.skip_gateway_reload ?? body.skipGatewayReload) === true ? false : void 0);
15129
+ const explicit = readBoolean2(body.reload_gateway ?? body.reloadGateway) ?? (readBoolean2(body.skip_gateway_reload ?? body.skipGatewayReload) === true ? false : void 0);
13733
15130
  return explicit ?? true;
13734
15131
  }
13735
15132
  function markModelConfigAppliedWithoutGatewayReload(result) {
@@ -13810,19 +15207,19 @@ import {
13810
15207
  copyFile as copyFile2,
13811
15208
  cp,
13812
15209
  mkdir as mkdir11,
13813
- readFile as readFile12,
15210
+ readFile as readFile13,
13814
15211
  rm as rm7,
13815
- stat as stat11,
15212
+ stat as stat12,
13816
15213
  writeFile as writeFile4,
13817
15214
  rename as rename5
13818
15215
  } from "fs/promises";
13819
- import path17 from "path";
15216
+ import path18 from "path";
13820
15217
  import YAML3 from "yaml";
13821
15218
  var PROFILE_CREATE_LOG_FILE = "profile-create.log";
13822
15219
  var PROFILE_CREATE_LOG_MAX_FILES = 3;
13823
15220
  var MAX_PROFILE_CREATE_LOG_LINES = 260;
13824
15221
  var MAX_OUTPUT_LINE_LENGTH = 1200;
13825
- var PROFILE_NAME_PATTERN4 = /^[a-z0-9][a-z0-9_-]{0,63}$/u;
15222
+ var PROFILE_NAME_PATTERN5 = /^[a-z0-9][a-z0-9_-]{0,63}$/u;
13826
15223
  var ALL_COPY_SCOPES = [
13827
15224
  "models",
13828
15225
  "skills",
@@ -14074,7 +15471,7 @@ function normalizeOptionalProfileName(value) {
14074
15471
  if (!trimmed) {
14075
15472
  return null;
14076
15473
  }
14077
- if (!PROFILE_NAME_PATTERN4.test(trimmed)) {
15474
+ if (!PROFILE_NAME_PATTERN5.test(trimmed)) {
14078
15475
  throw new LinkHttpError(
14079
15476
  400,
14080
15477
  "invalid_profile_name",
@@ -14206,7 +15603,7 @@ function copyModelConfig(source, target) {
14206
15603
  copied[key] = cloneJson(source[key]);
14207
15604
  }
14208
15605
  }
14209
- const sourceAuxiliary = toRecord11(source.auxiliary);
15606
+ const sourceAuxiliary = toRecord13(source.auxiliary);
14210
15607
  if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
14211
15608
  const targetAuxiliary = ensureRecord2(target, "auxiliary");
14212
15609
  targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
@@ -14215,12 +15612,12 @@ function copyModelConfig(source, target) {
14215
15612
  return copied;
14216
15613
  }
14217
15614
  function copyToolPermissionsConfig(source, target) {
14218
- const sourcePlatformToolsets = toRecord11(source.platform_toolsets);
15615
+ const sourcePlatformToolsets = toRecord13(source.platform_toolsets);
14219
15616
  if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
14220
15617
  const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
14221
15618
  targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
14222
15619
  }
14223
- const sourceStt = toRecord11(source.stt);
15620
+ const sourceStt = toRecord13(source.stt);
14224
15621
  if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
14225
15622
  const targetStt = ensureRecord2(target, "stt");
14226
15623
  targetStt.enabled = cloneJson(sourceStt.enabled);
@@ -14267,9 +15664,9 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
14267
15664
  return keys;
14268
15665
  }
14269
15666
  async function writeEnvValues(profileName, values) {
14270
- const envPath = path17.join(resolveHermesProfileDir(profileName), ".env");
14271
- const existingRaw = await readFile12(envPath, "utf8").catch((error) => {
14272
- if (isNodeError12(error, "ENOENT")) {
15667
+ const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
15668
+ const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
15669
+ if (isNodeError14(error, "ENOENT")) {
14273
15670
  return "";
14274
15671
  }
14275
15672
  throw error;
@@ -14294,7 +15691,7 @@ async function writeEnvValues(profileName, values) {
14294
15691
  nextLines.push(`${key}=${formatEnvValue2(value)}`);
14295
15692
  }
14296
15693
  const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
14297
- await mkdir11(path17.dirname(envPath), { recursive: true, mode: 448 });
15694
+ await mkdir11(path18.dirname(envPath), { recursive: true, mode: 448 });
14298
15695
  if (existingRaw) {
14299
15696
  await copyFile2(envPath, `${envPath}.bak.${Date.now()}`);
14300
15697
  }
@@ -14303,8 +15700,8 @@ async function writeEnvValues(profileName, values) {
14303
15700
  await rename5(tempPath, envPath);
14304
15701
  }
14305
15702
  async function copySkills(sourceProfile, targetProfile) {
14306
- const sourceSkills = path17.join(resolveHermesProfileDir(sourceProfile), "skills");
14307
- const targetSkills = path17.join(resolveHermesProfileDir(targetProfile), "skills");
15703
+ const sourceSkills = path18.join(resolveHermesProfileDir(sourceProfile), "skills");
15704
+ const targetSkills = path18.join(resolveHermesProfileDir(targetProfile), "skills");
14308
15705
  if (!await pathExists(sourceSkills)) {
14309
15706
  return;
14310
15707
  }
@@ -14323,21 +15720,21 @@ function copyProperty(source, target, key) {
14323
15720
  }
14324
15721
  }
14325
15722
  async function readYamlConfig(configPath) {
14326
- const existingRaw = await readFile12(configPath, "utf8").catch(
15723
+ const existingRaw = await readFile13(configPath, "utf8").catch(
14327
15724
  (error) => {
14328
- if (isNodeError12(error, "ENOENT")) {
15725
+ if (isNodeError14(error, "ENOENT")) {
14329
15726
  return null;
14330
15727
  }
14331
15728
  throw error;
14332
15729
  }
14333
15730
  );
14334
15731
  return {
14335
- config: toRecord11(existingRaw ? YAML3.parse(existingRaw) : {}),
15732
+ config: toRecord13(existingRaw ? YAML3.parse(existingRaw) : {}),
14336
15733
  existingRaw
14337
15734
  };
14338
15735
  }
14339
15736
  async function writeYamlConfig(configPath, input) {
14340
- await mkdir11(path17.dirname(configPath), { recursive: true, mode: 448 });
15737
+ await mkdir11(path18.dirname(configPath), { recursive: true, mode: 448 });
14341
15738
  if (input.existingRaw) {
14342
15739
  await copyFile2(configPath, `${configPath}.bak.${Date.now()}`);
14343
15740
  }
@@ -14385,7 +15782,7 @@ async function writeProfileCreationState(paths, state) {
14385
15782
  await writeJsonFile(profileCreationStatePath(paths), state);
14386
15783
  }
14387
15784
  async function readProfileCreationLogLines(paths) {
14388
- const raw = await readFile12(profileCreationLogPath(paths), "utf8").catch(() => "");
15785
+ const raw = await readFile13(profileCreationLogPath(paths), "utf8").catch(() => "");
14389
15786
  if (!raw.trim()) {
14390
15787
  return [];
14391
15788
  }
@@ -14394,10 +15791,10 @@ async function readProfileCreationLogLines(paths) {
14394
15791
  );
14395
15792
  }
14396
15793
  function profileCreationStatePath(paths) {
14397
- return path17.join(paths.runDir, "profile-create-state.json");
15794
+ return path18.join(paths.runDir, "profile-create-state.json");
14398
15795
  }
14399
15796
  function profileCreationLogPath(paths) {
14400
- return path17.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
15797
+ return path18.join(paths.logsDir, PROFILE_CREATE_LOG_FILE);
14401
15798
  }
14402
15799
  async function clearProfileCreationLogFiles(paths) {
14403
15800
  const primary = profileCreationLogPath(paths);
@@ -14410,8 +15807,8 @@ async function clearProfileCreationLogFiles(paths) {
14410
15807
  ]);
14411
15808
  }
14412
15809
  async function pathExists(targetPath) {
14413
- return await stat11(targetPath).then(() => true).catch((error) => {
14414
- if (isNodeError12(error, "ENOENT")) {
15810
+ return await stat12(targetPath).then(() => true).catch((error) => {
15811
+ if (isNodeError14(error, "ENOENT")) {
14415
15812
  return false;
14416
15813
  }
14417
15814
  throw error;
@@ -14443,7 +15840,7 @@ function ensureRecord2(target, key) {
14443
15840
  target[key] = next;
14444
15841
  return next;
14445
15842
  }
14446
- function toRecord11(value) {
15843
+ function toRecord13(value) {
14447
15844
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
14448
15845
  }
14449
15846
  function cloneJson(value) {
@@ -14458,7 +15855,7 @@ function formatEnvValue2(value) {
14458
15855
  function escapeRegExp2(value) {
14459
15856
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
14460
15857
  }
14461
- function isNodeError12(error, code) {
15858
+ function isNodeError14(error, code) {
14462
15859
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
14463
15860
  }
14464
15861
 
@@ -14543,16 +15940,16 @@ function readProfilePermissionsInput(body) {
14543
15940
  const approvals = readOptionalObject(body, "approvals");
14544
15941
  if (approvals) {
14545
15942
  input.approvals = {
14546
- mode: readString12(approvals, "mode") ?? readString12(approvals, "approval_mode") ?? readString12(approvals, "approvalMode") ?? void 0,
15943
+ mode: readString14(approvals, "mode") ?? readString14(approvals, "approval_mode") ?? readString14(approvals, "approvalMode") ?? void 0,
14547
15944
  timeout: readPositiveInteger2(approvals.timeout),
14548
- cronMode: readString12(approvals, "cron_mode") ?? readString12(approvals, "cronMode") ?? void 0
15945
+ cronMode: readString14(approvals, "cron_mode") ?? readString14(approvals, "cronMode") ?? void 0
14549
15946
  };
14550
15947
  }
14551
15948
  const terminal = readOptionalObject(body, "terminal");
14552
15949
  if (terminal) {
14553
15950
  input.terminal = {
14554
- backend: readString12(terminal, "backend") ?? void 0,
14555
- cwd: readString12(terminal, "cwd") ?? void 0,
15951
+ backend: readString14(terminal, "backend") ?? void 0,
15952
+ cwd: readString14(terminal, "cwd") ?? void 0,
14556
15953
  containerCpu: readPositiveInteger2(
14557
15954
  terminal.container_cpu ?? terminal.containerCpu
14558
15955
  ),
@@ -14562,7 +15959,7 @@ function readProfilePermissionsInput(body) {
14562
15959
  containerDisk: readPositiveInteger2(
14563
15960
  terminal.container_disk ?? terminal.containerDisk
14564
15961
  ),
14565
- containerPersistent: readBoolean(
15962
+ containerPersistent: readBoolean2(
14566
15963
  terminal.container_persistent ?? terminal.containerPersistent
14567
15964
  )
14568
15965
  };
@@ -14574,7 +15971,7 @@ function readProfilePermissionsInput(body) {
14574
15971
  toolsets.enabled_toolsets ?? toolsets.enabledToolsets ?? toolsets.enabled,
14575
15972
  "toolsets.enabled"
14576
15973
  ) ?? void 0,
14577
- mcpEnabled: readBoolean(toolsets.mcp_enabled ?? toolsets.mcpEnabled)
15974
+ mcpEnabled: readBoolean2(toolsets.mcp_enabled ?? toolsets.mcpEnabled)
14578
15975
  };
14579
15976
  }
14580
15977
  if (Object.keys(input).length === 0) {
@@ -14669,13 +16066,13 @@ import {
14669
16066
  access as access3,
14670
16067
  copyFile as copyFile3,
14671
16068
  mkdir as mkdir12,
14672
- readdir as readdir7,
14673
- readFile as readFile13,
16069
+ readdir as readdir9,
16070
+ readFile as readFile14,
14674
16071
  rename as rename6,
14675
- stat as stat12,
16072
+ stat as stat13,
14676
16073
  writeFile as writeFile5
14677
16074
  } from "fs/promises";
14678
- import path18 from "path";
16075
+ import path19 from "path";
14679
16076
  import YAML4 from "yaml";
14680
16077
  var ENTRY_DELIMITER = "\n\xA7\n";
14681
16078
  var DEFAULT_MEMORY_LIMIT = 2200;
@@ -14892,7 +16289,7 @@ async function saveProviderSettings(profileName, provider, patch) {
14892
16289
  if (provider === "hindsight") {
14893
16290
  await patchJsonProviderConfig(
14894
16291
  profileName,
14895
- path18.join("hindsight", "config.json"),
16292
+ path19.join("hindsight", "config.json"),
14896
16293
  {
14897
16294
  mode: patch.mode,
14898
16295
  api_url: patch.apiUrl,
@@ -14945,7 +16342,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
14945
16342
  "\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
14946
16343
  );
14947
16344
  }
14948
- const config = toRecord12(parsed);
16345
+ const config = toRecord14(parsed);
14949
16346
  if (Object.keys(config).length === 0 && parsed !== null) {
14950
16347
  throw new HermesMemoryError(
14951
16348
  "memory_provider_config_invalid",
@@ -14986,7 +16383,7 @@ function isSensitiveConfigKey(key) {
14986
16383
  }
14987
16384
  async function writeCustomProviderConfig(profileName, provider, config) {
14988
16385
  const configPath = customProviderConfigPath(profileName, provider);
14989
- await mkdir12(path18.dirname(configPath), { recursive: true, mode: 448 });
16386
+ await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
14990
16387
  await writeFile5(configPath, `${JSON.stringify(config, null, 2)}
14991
16388
  `, {
14992
16389
  encoding: "utf8",
@@ -15037,21 +16434,21 @@ function normalizeCustomProviderId(provider) {
15037
16434
  }
15038
16435
  async function patchHermesMemoryProvider(profileName, provider) {
15039
16436
  const configPath = resolveHermesConfigPath(profileName);
15040
- const existingRaw = await readFile13(configPath, "utf8").catch(
16437
+ const existingRaw = await readFile14(configPath, "utf8").catch(
15041
16438
  (error) => {
15042
- if (isNodeError13(error, "ENOENT")) {
16439
+ if (isNodeError15(error, "ENOENT")) {
15043
16440
  return null;
15044
16441
  }
15045
16442
  throw error;
15046
16443
  }
15047
16444
  );
15048
16445
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
15049
- const config = toRecord12(document.toJSON());
15050
- const memory = toRecord12(config.memory);
16446
+ const config = toRecord14(document.toJSON());
16447
+ const memory = toRecord14(config.memory);
15051
16448
  memory.provider = provider === "built-in" ? "" : provider;
15052
16449
  config.memory = memory;
15053
16450
  const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
15054
- await mkdir12(path18.dirname(configPath), { recursive: true, mode: 448 });
16451
+ await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
15055
16452
  if (backupPath) {
15056
16453
  await copyFile3(configPath, backupPath);
15057
16454
  }
@@ -15064,13 +16461,13 @@ async function patchHermesMemoryProvider(profileName, provider) {
15064
16461
  await rename6(tempPath, configPath);
15065
16462
  }
15066
16463
  function resolveMemoryDir(profileName) {
15067
- return path18.join(resolveHermesProfileDir(profileName), "memories");
16464
+ return path19.join(resolveHermesProfileDir(profileName), "memories");
15068
16465
  }
15069
16466
  async function readMemoryStore(profileName, target, limits) {
15070
16467
  const filePath = memoryFilePath(profileName, target);
15071
16468
  const entries = await readMemoryEntries(filePath);
15072
- const fileStat = await stat12(filePath).catch((error) => {
15073
- if (isNodeError13(error, "ENOENT")) {
16469
+ const fileStat = await stat13(filePath).catch((error) => {
16470
+ if (isNodeError15(error, "ENOENT")) {
15074
16471
  return null;
15075
16472
  }
15076
16473
  throw error;
@@ -15098,8 +16495,8 @@ async function readMemoryStore(profileName, target, limits) {
15098
16495
  };
15099
16496
  }
15100
16497
  async function readMemoryEntries(filePath) {
15101
- const raw = await readFile13(filePath, "utf8").catch((error) => {
15102
- if (isNodeError13(error, "ENOENT")) {
16498
+ const raw = await readFile14(filePath, "utf8").catch((error) => {
16499
+ if (isNodeError15(error, "ENOENT")) {
15103
16500
  return "";
15104
16501
  }
15105
16502
  throw error;
@@ -15119,9 +16516,9 @@ async function mutateMemoryEntries(profileName, target, mutate) {
15119
16516
  async function writeMemoryEntries(profileName, target, entries) {
15120
16517
  assertWithinLimit(target, entries, await readMemoryLimits(profileName));
15121
16518
  const filePath = memoryFilePath(profileName, target);
15122
- const dir = path18.dirname(filePath);
16519
+ const dir = path19.dirname(filePath);
15123
16520
  await mkdir12(dir, { recursive: true, mode: 448 });
15124
- const tempPath = path18.join(
16521
+ const tempPath = path19.join(
15125
16522
  dir,
15126
16523
  `.mem_${process.pid}_${Date.now()}_${target}.tmp`
15127
16524
  );
@@ -15132,7 +16529,7 @@ async function writeMemoryEntries(profileName, target, entries) {
15132
16529
  await rename6(tempPath, filePath);
15133
16530
  }
15134
16531
  function memoryFilePath(profileName, target) {
15135
- return path18.join(
16532
+ return path19.join(
15136
16533
  resolveMemoryDir(profileName),
15137
16534
  target === "user" ? "USER.md" : "MEMORY.md"
15138
16535
  );
@@ -15192,7 +16589,7 @@ async function readCustomProviderSetupSummary(profileName) {
15192
16589
  configurable: true,
15193
16590
  configured: true,
15194
16591
  configurationIssue: null,
15195
- providerConfigPath: path18.join(
16592
+ providerConfigPath: path19.join(
15196
16593
  resolveHermesProfileDir(profileName),
15197
16594
  "<provider>.json"
15198
16595
  ),
@@ -15270,7 +16667,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15270
16667
  const config2 = await readJsonObject(
15271
16668
  memoryProviderConfigPath(profileName, "honcho") ?? ""
15272
16669
  );
15273
- return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString13(config2.apiKey)) || isConfiguredEnvValue(readString13(config2.api_key)) || isConfiguredEnvValue(readString13(config2.baseUrl)) ? { configured: true, issue: null } : {
16670
+ return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString15(config2.apiKey)) || isConfiguredEnvValue(readString15(config2.api_key)) || isConfiguredEnvValue(readString15(config2.baseUrl)) ? { configured: true, issue: null } : {
15274
16671
  configured: false,
15275
16672
  issue: "Honcho \u9700\u8981\u5148\u914D\u7F6E HONCHO_API_KEY\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
15276
16673
  };
@@ -15279,7 +16676,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15279
16676
  const config2 = await readJsonObject(
15280
16677
  memoryProviderConfigPath(profileName, "mem0") ?? ""
15281
16678
  );
15282
- return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString13(config2.api_key)) ? { configured: true, issue: null } : {
16679
+ return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString15(config2.api_key)) ? { configured: true, issue: null } : {
15283
16680
  configured: false,
15284
16681
  issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u673A Hermes .env \u914D\u7F6E MEM0_API_KEY\u3002"
15285
16682
  };
@@ -15321,7 +16718,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15321
16718
  memoryProviderConfigPath(profileName, provider) ?? ""
15322
16719
  );
15323
16720
  const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
15324
- const apiKey = readString13(config.apiKey) ?? readString13(config.api_key) ?? env.HINDSIGHT_API_KEY;
16721
+ const apiKey = readString15(config.apiKey) ?? readString15(config.api_key) ?? env.HINDSIGHT_API_KEY;
15325
16722
  if (mode === "cloud") {
15326
16723
  return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
15327
16724
  configured: false,
@@ -15329,15 +16726,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
15329
16726
  };
15330
16727
  }
15331
16728
  if (mode === "local_external") {
15332
- const apiUrl = readString13(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
16729
+ const apiUrl = readString15(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
15333
16730
  return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
15334
16731
  configured: false,
15335
16732
  issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
15336
16733
  };
15337
16734
  }
15338
16735
  if (mode === "local_embedded") {
15339
- const llmProvider = readString13(config.llm_provider) ?? "openai";
15340
- const llmModel = readString13(config.llm_model);
16736
+ const llmProvider = readString15(config.llm_provider) ?? "openai";
16737
+ const llmModel = readString15(config.llm_model);
15341
16738
  if (!llmModel) {
15342
16739
  return {
15343
16740
  configured: false,
@@ -15345,7 +16742,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15345
16742
  };
15346
16743
  }
15347
16744
  if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
15348
- readString13(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
16745
+ readString15(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
15349
16746
  )) {
15350
16747
  return {
15351
16748
  configured: false,
@@ -15353,7 +16750,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15353
16750
  };
15354
16751
  }
15355
16752
  if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
15356
- readString13(config.llmApiKey) ?? readString13(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
16753
+ readString15(config.llmApiKey) ?? readString15(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
15357
16754
  )) {
15358
16755
  return {
15359
16756
  configured: false,
@@ -15493,8 +16890,8 @@ async function readProviderSettings(profileName, provider) {
15493
16890
  const config = await readJsonObject(
15494
16891
  memoryProviderConfigPath(profileName, provider) ?? ""
15495
16892
  );
15496
- const banks = toRecord12(config.banks);
15497
- const hermesBank = toRecord12(banks.hermes);
16893
+ const banks = toRecord14(config.banks);
16894
+ const hermesBank = toRecord14(banks.hermes);
15498
16895
  const mode = normalizeHindsightMode(config.mode);
15499
16896
  return [
15500
16897
  selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
@@ -15546,7 +16943,7 @@ async function readProviderSettings(profileName, provider) {
15546
16943
  stringSetting(
15547
16944
  "dbPath",
15548
16945
  "SQLite \u6570\u636E\u5E93\u8DEF\u5F84",
15549
- config.db_path ?? path18.join(resolveHermesProfileDir(profileName), "memory_store.db")
16946
+ config.db_path ?? path19.join(resolveHermesProfileDir(profileName), "memory_store.db")
15550
16947
  ),
15551
16948
  booleanSetting("autoExtract", "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u62BD\u53D6", config.auto_extract ?? false),
15552
16949
  numberSetting("defaultTrust", "\u9ED8\u8BA4\u4FE1\u4EFB\u5206", config.default_trust ?? 0.5),
@@ -15569,7 +16966,7 @@ async function readProviderSettings(profileName, provider) {
15569
16966
  stringSetting(
15570
16967
  "workingDirectory",
15571
16968
  "\u5DE5\u4F5C\u76EE\u5F55",
15572
- path18.join(resolveHermesProfileDir(profileName), "byterover"),
16969
+ path19.join(resolveHermesProfileDir(profileName), "byterover"),
15573
16970
  false
15574
16971
  )
15575
16972
  ];
@@ -15578,16 +16975,16 @@ async function readProviderSettings(profileName, provider) {
15578
16975
  }
15579
16976
  function memoryProviderConfigPath(profileName, provider) {
15580
16977
  if (provider === "honcho") {
15581
- return path18.join(resolveHermesProfileDir(profileName), "honcho.json");
16978
+ return path19.join(resolveHermesProfileDir(profileName), "honcho.json");
15582
16979
  }
15583
16980
  if (provider === "mem0") {
15584
- return path18.join(resolveHermesProfileDir(profileName), "mem0.json");
16981
+ return path19.join(resolveHermesProfileDir(profileName), "mem0.json");
15585
16982
  }
15586
16983
  if (provider === "supermemory") {
15587
- return path18.join(resolveHermesProfileDir(profileName), "supermemory.json");
16984
+ return path19.join(resolveHermesProfileDir(profileName), "supermemory.json");
15588
16985
  }
15589
16986
  if (provider === "hindsight") {
15590
- return path18.join(
16987
+ return path19.join(
15591
16988
  resolveHermesProfileDir(profileName),
15592
16989
  "hindsight",
15593
16990
  "config.json"
@@ -15596,21 +16993,21 @@ function memoryProviderConfigPath(profileName, provider) {
15596
16993
  return null;
15597
16994
  }
15598
16995
  function customProviderConfigPath(profileName, provider) {
15599
- return path18.join(
16996
+ return path19.join(
15600
16997
  resolveHermesProfileDir(profileName),
15601
16998
  `${normalizeCustomProviderId(provider)}.json`
15602
16999
  );
15603
17000
  }
15604
17001
  function customProviderRegistryPath(profileName) {
15605
- return path18.join(
17002
+ return path19.join(
15606
17003
  resolveHermesProfileDir(profileName),
15607
17004
  CUSTOM_PROVIDER_REGISTRY_FILE
15608
17005
  );
15609
17006
  }
15610
17007
  async function readCustomProviderRegistry(profileName) {
15611
- const raw = await readFile13(customProviderRegistryPath(profileName), "utf8").catch(
17008
+ const raw = await readFile14(customProviderRegistryPath(profileName), "utf8").catch(
15612
17009
  (error) => {
15613
- if (isNodeError13(error, "ENOENT")) {
17010
+ if (isNodeError15(error, "ENOENT")) {
15614
17011
  return "";
15615
17012
  }
15616
17013
  throw error;
@@ -15621,18 +17018,18 @@ async function readCustomProviderRegistry(profileName) {
15621
17018
  }
15622
17019
  try {
15623
17020
  const parsed = JSON.parse(raw);
15624
- const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord12(parsed).providers) ? toRecord12(parsed).providers : [];
17021
+ const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord14(parsed).providers) ? toRecord14(parsed).providers : [];
15625
17022
  return providers.map((item) => {
15626
17023
  if (typeof item === "string") {
15627
17024
  const id2 = normalizeCustomProviderId(item);
15628
17025
  return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
15629
17026
  }
15630
- const record = toRecord12(item);
15631
- const id = normalizeCustomProviderId(readString13(record.id) ?? "");
17027
+ const record = toRecord14(item);
17028
+ const id = normalizeCustomProviderId(readString15(record.id) ?? "");
15632
17029
  return {
15633
17030
  id,
15634
- label: readString13(record.label) ?? id,
15635
- description: readString13(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
17031
+ label: readString15(record.label) ?? id,
17032
+ description: readString15(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15636
17033
  };
15637
17034
  }).filter((item) => item.id);
15638
17035
  } catch {
@@ -15647,7 +17044,7 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
15647
17044
  { id: providerId, label: providerId, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" }
15648
17045
  ].sort((left, right) => left.id.localeCompare(right.id));
15649
17046
  const registryPath2 = customProviderRegistryPath(profileName);
15650
- await mkdir12(path18.dirname(registryPath2), { recursive: true, mode: 448 });
17047
+ await mkdir12(path19.dirname(registryPath2), { recursive: true, mode: 448 });
15651
17048
  await writeFile5(
15652
17049
  registryPath2,
15653
17050
  `${JSON.stringify({ providers }, null, 2)}
@@ -15656,10 +17053,10 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
15656
17053
  );
15657
17054
  }
15658
17055
  async function discoverUserMemoryProviderDescriptors(profileName) {
15659
- const pluginsDir = path18.join(resolveHermesProfileDir(profileName), "plugins");
15660
- const entries = await readdir7(pluginsDir, { withFileTypes: true }).catch(
17056
+ const pluginsDir = path19.join(resolveHermesProfileDir(profileName), "plugins");
17057
+ const entries = await readdir9(pluginsDir, { withFileTypes: true }).catch(
15661
17058
  (error) => {
15662
- if (isNodeError13(error, "ENOENT")) {
17059
+ if (isNodeError15(error, "ENOENT")) {
15663
17060
  return [];
15664
17061
  }
15665
17062
  throw error;
@@ -15676,21 +17073,21 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
15676
17073
  } catch {
15677
17074
  continue;
15678
17075
  }
15679
- const providerDir = path18.join(pluginsDir, entry.name);
17076
+ const providerDir = path19.join(pluginsDir, entry.name);
15680
17077
  if (!await isMemoryProviderPluginDir(providerDir)) {
15681
17078
  continue;
15682
17079
  }
15683
17080
  const meta = await readPluginMetadata(providerDir);
15684
17081
  descriptors.push({
15685
17082
  id: providerId,
15686
- label: readString13(meta.name) ?? providerId,
15687
- description: readString13(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
17083
+ label: readString15(meta.name) ?? providerId,
17084
+ description: readString15(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15688
17085
  });
15689
17086
  }
15690
17087
  return descriptors;
15691
17088
  }
15692
17089
  async function isUserMemoryProviderInstalled(profileName, provider) {
15693
- const providerDir = path18.join(
17090
+ const providerDir = path19.join(
15694
17091
  resolveHermesProfileDir(profileName),
15695
17092
  "plugins",
15696
17093
  normalizeCustomProviderId(provider)
@@ -15698,9 +17095,9 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
15698
17095
  return isMemoryProviderPluginDir(providerDir);
15699
17096
  }
15700
17097
  async function isMemoryProviderPluginDir(providerDir) {
15701
- const source = await readFile13(path18.join(providerDir, "__init__.py"), "utf8").catch(
17098
+ const source = await readFile14(path19.join(providerDir, "__init__.py"), "utf8").catch(
15702
17099
  (error) => {
15703
- if (isNodeError13(error, "ENOENT")) {
17100
+ if (isNodeError15(error, "ENOENT")) {
15704
17101
  return "";
15705
17102
  }
15706
17103
  throw error;
@@ -15710,22 +17107,22 @@ async function isMemoryProviderPluginDir(providerDir) {
15710
17107
  return sample.includes("register_memory_provider") || sample.includes("MemoryProvider");
15711
17108
  }
15712
17109
  async function readPluginMetadata(providerDir) {
15713
- const raw = await readFile13(path18.join(providerDir, "plugin.yaml"), "utf8").catch(
17110
+ const raw = await readFile14(path19.join(providerDir, "plugin.yaml"), "utf8").catch(
15714
17111
  (error) => {
15715
- if (isNodeError13(error, "ENOENT")) {
17112
+ if (isNodeError15(error, "ENOENT")) {
15716
17113
  return "";
15717
17114
  }
15718
17115
  throw error;
15719
17116
  }
15720
17117
  );
15721
- return raw ? toRecord12(YAML4.parse(raw)) : {};
17118
+ return raw ? toRecord14(YAML4.parse(raw)) : {};
15722
17119
  }
15723
17120
  async function resolveByteRoverCli() {
15724
17121
  const candidates = [
15725
- ...(process.env.PATH ?? "").split(path18.delimiter).filter(Boolean).map((dir) => path18.join(dir, "brv")),
15726
- path18.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
17122
+ ...(process.env.PATH ?? "").split(path19.delimiter).filter(Boolean).map((dir) => path19.join(dir, "brv")),
17123
+ path19.join(process.env.HOME ?? "", ".brv-cli", "bin", "brv"),
15727
17124
  "/usr/local/bin/brv",
15728
- path18.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
17125
+ path19.join(process.env.HOME ?? "", ".npm-global", "bin", "brv")
15729
17126
  ].filter(Boolean);
15730
17127
  for (const candidate of candidates) {
15731
17128
  const found = await access3(candidate).then(() => true).catch(() => false);
@@ -15736,32 +17133,32 @@ async function resolveByteRoverCli() {
15736
17133
  return null;
15737
17134
  }
15738
17135
  async function readHolographicProviderConfig(profileName) {
15739
- const raw = await readFile13(resolveHermesConfigPath(profileName), "utf8").catch(
17136
+ const raw = await readFile14(resolveHermesConfigPath(profileName), "utf8").catch(
15740
17137
  (error) => {
15741
- if (isNodeError13(error, "ENOENT")) {
17138
+ if (isNodeError15(error, "ENOENT")) {
15742
17139
  return "";
15743
17140
  }
15744
17141
  throw error;
15745
17142
  }
15746
17143
  );
15747
- const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15748
- const plugins = toRecord12(config.plugins);
15749
- return toRecord12(plugins["hermes-memory-store"]);
17144
+ const config = raw ? toRecord14(YAML4.parse(raw)) : {};
17145
+ const plugins = toRecord14(config.plugins);
17146
+ return toRecord14(plugins["hermes-memory-store"]);
15750
17147
  }
15751
17148
  async function patchHolographicProviderConfig(profileName, patch) {
15752
17149
  const configPath = resolveHermesConfigPath(profileName);
15753
- const existingRaw = await readFile13(configPath, "utf8").catch(
17150
+ const existingRaw = await readFile14(configPath, "utf8").catch(
15754
17151
  (error) => {
15755
- if (isNodeError13(error, "ENOENT")) {
17152
+ if (isNodeError15(error, "ENOENT")) {
15756
17153
  return null;
15757
17154
  }
15758
17155
  throw error;
15759
17156
  }
15760
17157
  );
15761
17158
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
15762
- const config = toRecord12(document.toJSON());
15763
- const plugins = toRecord12(config.plugins);
15764
- const memoryStore = toRecord12(plugins["hermes-memory-store"]);
17159
+ const config = toRecord14(document.toJSON());
17160
+ const plugins = toRecord14(config.plugins);
17161
+ const memoryStore = toRecord14(plugins["hermes-memory-store"]);
15765
17162
  for (const [key, value] of Object.entries(patch)) {
15766
17163
  if (value !== void 0) {
15767
17164
  memoryStore[key] = value;
@@ -15769,7 +17166,7 @@ async function patchHolographicProviderConfig(profileName, patch) {
15769
17166
  }
15770
17167
  plugins["hermes-memory-store"] = memoryStore;
15771
17168
  config.plugins = plugins;
15772
- await mkdir12(path18.dirname(configPath), { recursive: true, mode: 448 });
17169
+ await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
15773
17170
  if (existingRaw) {
15774
17171
  await copyFile3(configPath, `${configPath}.bak.${Date.now()}`);
15775
17172
  }
@@ -15788,9 +17185,9 @@ async function patchHermesMemoryEnv(profileName, patch) {
15788
17185
  if (entries.length === 0) {
15789
17186
  return;
15790
17187
  }
15791
- const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
15792
- const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
15793
- if (isNodeError13(error, "ENOENT")) {
17188
+ const envPath = path19.join(resolveHermesProfileDir(profileName), ".env");
17189
+ const existingRaw = await readFile14(envPath, "utf8").catch((error) => {
17190
+ if (isNodeError15(error, "ENOENT")) {
15794
17191
  return "";
15795
17192
  }
15796
17193
  throw error;
@@ -15820,7 +17217,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
15820
17217
  }
15821
17218
  }
15822
17219
  const nextRaw = nextLines.join("\n").replace(/\n*$/u, "\n");
15823
- await mkdir12(path18.dirname(envPath), { recursive: true, mode: 448 });
17220
+ await mkdir12(path19.dirname(envPath), { recursive: true, mode: 448 });
15824
17221
  if (existingRaw) {
15825
17222
  await copyFile3(envPath, `${envPath}.bak.${Date.now()}`);
15826
17223
  }
@@ -15839,29 +17236,29 @@ function isMemoryEnvKeyWritable(key) {
15839
17236
  ].includes(key);
15840
17237
  }
15841
17238
  function normalizeHindsightMode(value) {
15842
- const mode = readString13(value) ?? "cloud";
17239
+ const mode = readString15(value) ?? "cloud";
15843
17240
  return mode === "local" ? "local_embedded" : mode;
15844
17241
  }
15845
17242
  async function readActiveMemoryProvider(profileName) {
15846
- const raw = await readFile13(
17243
+ const raw = await readFile14(
15847
17244
  resolveHermesConfigPath(profileName),
15848
17245
  "utf8"
15849
17246
  ).catch((error) => {
15850
- if (isNodeError13(error, "ENOENT")) {
17247
+ if (isNodeError15(error, "ENOENT")) {
15851
17248
  return "";
15852
17249
  }
15853
17250
  throw error;
15854
17251
  });
15855
- const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15856
- const memory = toRecord12(config.memory);
15857
- const provider = readString13(memory.provider);
17252
+ const config = raw ? toRecord14(YAML4.parse(raw)) : {};
17253
+ const memory = toRecord14(config.memory);
17254
+ const provider = readString15(memory.provider);
15858
17255
  if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
15859
17256
  return null;
15860
17257
  }
15861
17258
  return provider;
15862
17259
  }
15863
17260
  async function patchJsonProviderConfig(profileName, relativePath, patch) {
15864
- const configPath = path18.join(
17261
+ const configPath = path19.join(
15865
17262
  resolveHermesProfileDir(profileName),
15866
17263
  relativePath
15867
17264
  );
@@ -15872,7 +17269,7 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
15872
17269
  next[key] = value;
15873
17270
  }
15874
17271
  }
15875
- await mkdir12(path18.dirname(configPath), { recursive: true, mode: 448 });
17272
+ await mkdir12(path19.dirname(configPath), { recursive: true, mode: 448 });
15876
17273
  await writeFile5(configPath, `${JSON.stringify(next, null, 2)}
15877
17274
  `, {
15878
17275
  encoding: "utf8",
@@ -15880,18 +17277,18 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
15880
17277
  });
15881
17278
  }
15882
17279
  async function readJsonObject(filePath) {
15883
- const raw = await readFile13(filePath, "utf8").catch((error) => {
15884
- if (isNodeError13(error, "ENOENT")) {
17280
+ const raw = await readFile14(filePath, "utf8").catch((error) => {
17281
+ if (isNodeError15(error, "ENOENT")) {
15885
17282
  return "{}";
15886
17283
  }
15887
17284
  throw error;
15888
17285
  });
15889
17286
  try {
15890
- return toRecord12(JSON.parse(raw || "{}"));
17287
+ return toRecord14(JSON.parse(raw || "{}"));
15891
17288
  } catch {
15892
17289
  throw new HermesMemoryError(
15893
17290
  "memory_provider_config_invalid",
15894
- `${path18.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
17291
+ `${path19.basename(filePath)} \u4E0D\u662F\u6709\u6548\u7684 JSON \u914D\u7F6E\u6587\u4EF6\u3002`
15895
17292
  );
15896
17293
  }
15897
17294
  }
@@ -15899,7 +17296,7 @@ function booleanSetting(key, label, value) {
15899
17296
  return {
15900
17297
  key,
15901
17298
  label,
15902
- value: readBoolean2(value) ?? false,
17299
+ value: readBoolean3(value) ?? false,
15903
17300
  editable: true,
15904
17301
  kind: "boolean"
15905
17302
  };
@@ -15912,7 +17309,7 @@ function stringSetting(key, label, value, editable = true) {
15912
17309
  return {
15913
17310
  key,
15914
17311
  label,
15915
- value: readString13(value) ?? "",
17312
+ value: readString15(value) ?? "",
15916
17313
  editable,
15917
17314
  kind: "string"
15918
17315
  };
@@ -15927,21 +17324,21 @@ function textSetting(key, label, value, editable = true) {
15927
17324
  };
15928
17325
  }
15929
17326
  function selectSetting(key, label, value, options, editable = true) {
15930
- const stringValue = readString13(value) ?? options[0] ?? null;
17327
+ const stringValue = readString15(value) ?? options[0] ?? null;
15931
17328
  return { key, label, value: stringValue, editable, kind: "select", options };
15932
17329
  }
15933
17330
  async function readMemoryLimits(profileName) {
15934
- const raw = await readFile13(
17331
+ const raw = await readFile14(
15935
17332
  resolveHermesConfigPath(profileName),
15936
17333
  "utf8"
15937
17334
  ).catch((error) => {
15938
- if (isNodeError13(error, "ENOENT")) {
17335
+ if (isNodeError15(error, "ENOENT")) {
15939
17336
  return "";
15940
17337
  }
15941
17338
  throw error;
15942
17339
  });
15943
- const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15944
- const memory = toRecord12(config.memory);
17340
+ const config = raw ? toRecord14(YAML4.parse(raw)) : {};
17341
+ const memory = toRecord14(config.memory);
15945
17342
  return {
15946
17343
  memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
15947
17344
  user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
@@ -15997,17 +17394,17 @@ function hashString(value) {
15997
17394
  }
15998
17395
  return hash.toString(16);
15999
17396
  }
16000
- function toRecord12(value) {
17397
+ function toRecord14(value) {
16001
17398
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
16002
17399
  }
16003
- function readString13(value) {
17400
+ function readString15(value) {
16004
17401
  return typeof value === "string" && value.trim() ? value.trim() : null;
16005
17402
  }
16006
17403
  function readPositiveInteger3(value) {
16007
17404
  const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
16008
17405
  return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
16009
17406
  }
16010
- function readBoolean2(value) {
17407
+ function readBoolean3(value) {
16011
17408
  if (typeof value === "boolean") {
16012
17409
  return value;
16013
17410
  }
@@ -16028,7 +17425,7 @@ function formatEnvValue3(value) {
16028
17425
  function escapeRegExp3(value) {
16029
17426
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
16030
17427
  }
16031
- function isNodeError13(error, code) {
17428
+ function isNodeError15(error, code) {
16032
17429
  return error instanceof Error && "code" in error && error.code === code;
16033
17430
  }
16034
17431
 
@@ -16142,7 +17539,7 @@ function registerProfileMemoryRoutes(router, options) {
16142
17539
  );
16143
17540
  }
16144
17541
  function readMemoryTarget(body) {
16145
- const raw = readString12(body, "target");
17542
+ const raw = readString14(body, "target");
16146
17543
  if (raw === "memory" || raw === "user") {
16147
17544
  return raw;
16148
17545
  }
@@ -16153,7 +17550,7 @@ function readMemoryTarget(body) {
16153
17550
  );
16154
17551
  }
16155
17552
  function readMemoryResetTarget(body) {
16156
- const raw = readString12(body, "target") ?? "all";
17553
+ const raw = readString14(body, "target") ?? "all";
16157
17554
  if (raw === "all" || raw === "memory" || raw === "user") {
16158
17555
  return raw;
16159
17556
  }
@@ -16164,7 +17561,7 @@ function readMemoryResetTarget(body) {
16164
17561
  );
16165
17562
  }
16166
17563
  function readRequiredMemoryContent(body) {
16167
- const content = readString12(body, "content") ?? readString12(body, "text");
17564
+ const content = readString14(body, "content") ?? readString14(body, "text");
16168
17565
  if (!content) {
16169
17566
  throw new LinkHttpError(
16170
17567
  400,
@@ -16175,7 +17572,7 @@ function readRequiredMemoryContent(body) {
16175
17572
  return content;
16176
17573
  }
16177
17574
  function readRequiredMemoryMatch(body) {
16178
- const oldText = readString12(body, "old_text") ?? readString12(body, "oldText") ?? readString12(body, "match");
17575
+ const oldText = readString14(body, "old_text") ?? readString14(body, "oldText") ?? readString14(body, "match");
16179
17576
  if (!oldText) {
16180
17577
  throw new LinkHttpError(
16181
17578
  400,
@@ -16186,7 +17583,7 @@ function readRequiredMemoryMatch(body) {
16186
17583
  return oldText;
16187
17584
  }
16188
17585
  function readRequiredMemoryProvider(body) {
16189
- const provider = readString12(body, "provider") ?? readString12(body, "provider_id") ?? readString12(body, "providerId");
17586
+ const provider = readString14(body, "provider") ?? readString14(body, "provider_id") ?? readString14(body, "providerId");
16190
17587
  if (!provider) {
16191
17588
  throw new LinkHttpError(
16192
17589
  400,
@@ -16198,7 +17595,7 @@ function readRequiredMemoryProvider(body) {
16198
17595
  }
16199
17596
  function readMemorySettingsPatch(body) {
16200
17597
  const input = {};
16201
- const mode = readString12(body, "mode");
17598
+ const mode = readString14(body, "mode");
16202
17599
  if (mode) {
16203
17600
  input.mode = mode;
16204
17601
  }
@@ -16210,7 +17607,7 @@ function readMemorySettingsPatch(body) {
16210
17607
  if (bankId !== void 0) {
16211
17608
  input.bankId = bankId;
16212
17609
  }
16213
- const llmProvider = readString12(body, "llm_provider") ?? readString12(body, "llmProvider");
17610
+ const llmProvider = readString14(body, "llm_provider") ?? readString14(body, "llmProvider");
16214
17611
  if (llmProvider) {
16215
17612
  input.llmProvider = llmProvider;
16216
17613
  }
@@ -16226,23 +17623,23 @@ function readMemorySettingsPatch(body) {
16226
17623
  if (containerTag !== void 0) {
16227
17624
  input.containerTag = containerTag;
16228
17625
  }
16229
- const autoRecall = readBoolean(body.auto_recall ?? body.autoRecall);
17626
+ const autoRecall = readBoolean2(body.auto_recall ?? body.autoRecall);
16230
17627
  if (autoRecall !== void 0) {
16231
17628
  input.autoRecall = autoRecall;
16232
17629
  }
16233
- const autoCapture = readBoolean(body.auto_capture ?? body.autoCapture);
17630
+ const autoCapture = readBoolean2(body.auto_capture ?? body.autoCapture);
16234
17631
  if (autoCapture !== void 0) {
16235
17632
  input.autoCapture = autoCapture;
16236
17633
  }
16237
- const autoRetain = readBoolean(body.auto_retain ?? body.autoRetain);
17634
+ const autoRetain = readBoolean2(body.auto_retain ?? body.autoRetain);
16238
17635
  if (autoRetain !== void 0) {
16239
17636
  input.autoRetain = autoRetain;
16240
17637
  }
16241
- const memoryMode = readString12(body, "memory_mode") ?? readString12(body, "memoryMode");
17638
+ const memoryMode = readString14(body, "memory_mode") ?? readString14(body, "memoryMode");
16242
17639
  if (memoryMode) {
16243
17640
  input.memoryMode = memoryMode;
16244
17641
  }
16245
- const recallBudget = readString12(body, "recall_budget") ?? readString12(body, "recallBudget");
17642
+ const recallBudget = readString14(body, "recall_budget") ?? readString14(body, "recallBudget");
16246
17643
  if (recallBudget) {
16247
17644
  input.recallBudget = recallBudget;
16248
17645
  }
@@ -16258,11 +17655,11 @@ function readMemorySettingsPatch(body) {
16258
17655
  if (profileFrequency !== void 0) {
16259
17656
  input.profileFrequency = profileFrequency;
16260
17657
  }
16261
- const captureMode = readString12(body, "capture_mode") ?? readString12(body, "captureMode");
17658
+ const captureMode = readString14(body, "capture_mode") ?? readString14(body, "captureMode");
16262
17659
  if (captureMode) {
16263
17660
  input.captureMode = captureMode;
16264
17661
  }
16265
- const searchMode = readString12(body, "search_mode") ?? readString12(body, "searchMode");
17662
+ const searchMode = readString14(body, "search_mode") ?? readString14(body, "searchMode");
16266
17663
  if (searchMode) {
16267
17664
  input.searchMode = searchMode;
16268
17665
  }
@@ -16296,19 +17693,19 @@ function readMemorySettingsPatch(body) {
16296
17693
  if (aiPeer !== void 0) {
16297
17694
  input.aiPeer = aiPeer;
16298
17695
  }
16299
- const recallMode = readString12(body, "recall_mode") ?? readString12(body, "recallMode");
17696
+ const recallMode = readString14(body, "recall_mode") ?? readString14(body, "recallMode");
16300
17697
  if (recallMode) {
16301
17698
  input.recallMode = recallMode;
16302
17699
  }
16303
- const writeFrequency = readString12(body, "write_frequency") ?? readString12(body, "writeFrequency");
17700
+ const writeFrequency = readString14(body, "write_frequency") ?? readString14(body, "writeFrequency");
16304
17701
  if (writeFrequency) {
16305
17702
  input.writeFrequency = writeFrequency;
16306
17703
  }
16307
- const saveMessages = readBoolean(body.save_messages ?? body.saveMessages);
17704
+ const saveMessages = readBoolean2(body.save_messages ?? body.saveMessages);
16308
17705
  if (saveMessages !== void 0) {
16309
17706
  input.saveMessages = saveMessages;
16310
17707
  }
16311
- const sessionStrategy = readString12(body, "session_strategy") ?? readString12(body, "sessionStrategy");
17708
+ const sessionStrategy = readString14(body, "session_strategy") ?? readString14(body, "sessionStrategy");
16312
17709
  if (sessionStrategy) {
16313
17710
  input.sessionStrategy = sessionStrategy;
16314
17711
  }
@@ -16344,7 +17741,7 @@ function readMemorySettingsPatch(body) {
16344
17741
  if (agentId !== void 0) {
16345
17742
  input.agentId = agentId;
16346
17743
  }
16347
- const rerank = readBoolean(body.rerank);
17744
+ const rerank = readBoolean2(body.rerank);
16348
17745
  if (rerank !== void 0) {
16349
17746
  input.rerank = rerank;
16350
17747
  }
@@ -16368,7 +17765,7 @@ function readMemorySettingsPatch(body) {
16368
17765
  if (dbPath !== void 0) {
16369
17766
  input.dbPath = dbPath;
16370
17767
  }
16371
- const autoExtract = readBoolean(body.auto_extract ?? body.autoExtract);
17768
+ const autoExtract = readBoolean2(body.auto_extract ?? body.autoExtract);
16372
17769
  if (autoExtract !== void 0) {
16373
17770
  input.autoExtract = autoExtract;
16374
17771
  }
@@ -16439,12 +17836,12 @@ function toMemoryHttpError(error) {
16439
17836
  import {
16440
17837
  copyFile as copyFile4,
16441
17838
  mkdir as mkdir13,
16442
- readFile as readFile14,
16443
- readdir as readdir8,
17839
+ readFile as readFile15,
17840
+ readdir as readdir10,
16444
17841
  rename as rename7,
16445
17842
  writeFile as writeFile6
16446
17843
  } from "fs/promises";
16447
- import path19 from "path";
17844
+ import path20 from "path";
16448
17845
  import YAML5 from "yaml";
16449
17846
  var HermesSkillNotFoundError = class extends Error {
16450
17847
  constructor(skillName) {
@@ -16458,7 +17855,7 @@ var EXCLUDED_SKILL_DIRS = /* @__PURE__ */ new Set([".git", ".github", ".hub"]);
16458
17855
  async function listHermesProfileSkills(profileName, paths = resolveRuntimePaths()) {
16459
17856
  const profile = await readExistingProfile(profileName, paths);
16460
17857
  const profileDir = resolveHermesProfileDir(profile.name);
16461
- const skillsRoot = path19.join(profileDir, "skills");
17858
+ const skillsRoot = path20.join(profileDir, "skills");
16462
17859
  const [skillFiles, disabled, provenance] = await Promise.all([
16463
17860
  findSkillFiles(skillsRoot),
16464
17861
  readDisabledSkillNames(resolveHermesConfigPath(profile.name)),
@@ -16535,9 +17932,9 @@ async function findSkillFiles(root) {
16535
17932
  return results.sort((left, right) => left.localeCompare(right));
16536
17933
  }
16537
17934
  async function collectSkillFiles(directory, results) {
16538
- const entries = await readdir8(directory, { withFileTypes: true }).catch(
17935
+ const entries = await readdir10(directory, { withFileTypes: true }).catch(
16539
17936
  (error) => {
16540
- if (isNodeError14(error, "ENOENT")) {
17937
+ if (isNodeError16(error, "ENOENT")) {
16541
17938
  return [];
16542
17939
  }
16543
17940
  throw error;
@@ -16549,7 +17946,7 @@ async function collectSkillFiles(directory, results) {
16549
17946
  if (EXCLUDED_SKILL_DIRS.has(entry.name)) {
16550
17947
  continue;
16551
17948
  }
16552
- const entryPath = path19.join(directory, entry.name);
17949
+ const entryPath = path20.join(directory, entry.name);
16553
17950
  if (entry.isDirectory()) {
16554
17951
  await collectSkillFiles(entryPath, results);
16555
17952
  continue;
@@ -16560,9 +17957,9 @@ async function collectSkillFiles(directory, results) {
16560
17957
  }
16561
17958
  }
16562
17959
  async function readSkillMetadata(input) {
16563
- const raw = await readFile14(input.skillFile, "utf8").catch(
17960
+ const raw = await readFile15(input.skillFile, "utf8").catch(
16564
17961
  (error) => {
16565
- if (isNodeError14(error, "ENOENT") || isNodeError14(error, "EACCES")) {
17962
+ if (isNodeError16(error, "ENOENT") || isNodeError16(error, "EACCES")) {
16566
17963
  return null;
16567
17964
  }
16568
17965
  throw error;
@@ -16571,16 +17968,16 @@ async function readSkillMetadata(input) {
16571
17968
  if (raw === null) {
16572
17969
  return null;
16573
17970
  }
16574
- const skillDir = path19.dirname(input.skillFile);
17971
+ const skillDir = path20.dirname(input.skillFile);
16575
17972
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
16576
17973
  const name = normalizeSkillName(
16577
- readString14(frontmatter.name) ?? path19.basename(skillDir)
17974
+ readString16(frontmatter.name) ?? path20.basename(skillDir)
16578
17975
  );
16579
17976
  if (!name) {
16580
17977
  return null;
16581
17978
  }
16582
17979
  const description = normalizeDescription(
16583
- readString14(frontmatter.description) ?? firstBodyDescription(body)
17980
+ readString16(frontmatter.description) ?? firstBodyDescription(body)
16584
17981
  );
16585
17982
  const provenance = input.provenance.get(name) ?? {
16586
17983
  source: "local",
@@ -16593,7 +17990,7 @@ async function readSkillMetadata(input) {
16593
17990
  enabled: !input.disabled.has(name),
16594
17991
  source: provenance.source,
16595
17992
  trust: provenance.trust,
16596
- relativePath: path19.relative(input.skillsRoot, skillDir)
17993
+ relativePath: path20.relative(input.skillsRoot, skillDir)
16597
17994
  };
16598
17995
  }
16599
17996
  function parseSkillDocument(raw) {
@@ -16606,7 +18003,7 @@ function parseSkillDocument(raw) {
16606
18003
  }
16607
18004
  try {
16608
18005
  return {
16609
- frontmatter: toRecord13(YAML5.parse(match[1] ?? "")),
18006
+ frontmatter: toRecord15(YAML5.parse(match[1] ?? "")),
16610
18007
  body: content.slice(match[0].length)
16611
18008
  };
16612
18009
  } catch {
@@ -16614,8 +18011,8 @@ function parseSkillDocument(raw) {
16614
18011
  }
16615
18012
  }
16616
18013
  function categoryFromPath(skillsRoot, skillFile) {
16617
- const relative = path19.relative(skillsRoot, skillFile);
16618
- const parts = relative.split(path19.sep).filter(Boolean);
18014
+ const relative = path20.relative(skillsRoot, skillFile);
18015
+ const parts = relative.split(path20.sep).filter(Boolean);
16619
18016
  return parts.length >= 3 ? parts[0] : null;
16620
18017
  }
16621
18018
  function firstBodyDescription(body) {
@@ -16638,8 +18035,8 @@ function normalizeDescription(value) {
16638
18035
  return `${description.slice(0, MAX_DESCRIPTION_LENGTH - 3)}...`;
16639
18036
  }
16640
18037
  async function readDisabledSkillNames(configPath) {
16641
- const raw = await readFile14(configPath, "utf8").catch((error) => {
16642
- if (isNodeError14(error, "ENOENT")) {
18038
+ const raw = await readFile15(configPath, "utf8").catch((error) => {
18039
+ if (isNodeError16(error, "ENOENT")) {
16643
18040
  return "";
16644
18041
  }
16645
18042
  throw error;
@@ -16647,8 +18044,8 @@ async function readDisabledSkillNames(configPath) {
16647
18044
  if (!raw.trim()) {
16648
18045
  return /* @__PURE__ */ new Set();
16649
18046
  }
16650
- const config = toRecord13(YAML5.parse(raw));
16651
- const skills = toRecord13(config.skills);
18047
+ const config = toRecord15(YAML5.parse(raw));
18048
+ const skills = toRecord15(config.skills);
16652
18049
  return new Set(readStringList3(skills.disabled));
16653
18050
  }
16654
18051
  async function readSkillProvenance(root) {
@@ -16662,9 +18059,9 @@ async function readSkillProvenance(root) {
16662
18059
  return provenance;
16663
18060
  }
16664
18061
  async function readBundledSkillNames(root) {
16665
- const raw = await readFile14(path19.join(root, ".bundled_manifest"), "utf8").catch(
18062
+ const raw = await readFile15(path20.join(root, ".bundled_manifest"), "utf8").catch(
16666
18063
  (error) => {
16667
- if (isNodeError14(error, "ENOENT")) {
18064
+ if (isNodeError16(error, "ENOENT")) {
16668
18065
  return "";
16669
18066
  }
16670
18067
  throw error;
@@ -16685,9 +18082,9 @@ async function readBundledSkillNames(root) {
16685
18082
  return names;
16686
18083
  }
16687
18084
  async function readHubInstalledSkills(root) {
16688
- const raw = await readFile14(path19.join(root, ".hub", "lock.json"), "utf8").catch(
18085
+ const raw = await readFile15(path20.join(root, ".hub", "lock.json"), "utf8").catch(
16689
18086
  (error) => {
16690
- if (isNodeError14(error, "ENOENT")) {
18087
+ if (isNodeError16(error, "ENOENT")) {
16691
18088
  return "";
16692
18089
  }
16693
18090
  throw error;
@@ -16698,17 +18095,17 @@ async function readHubInstalledSkills(root) {
16698
18095
  }
16699
18096
  let lock;
16700
18097
  try {
16701
- lock = toRecord13(JSON.parse(raw));
18098
+ lock = toRecord15(JSON.parse(raw));
16702
18099
  } catch {
16703
18100
  return /* @__PURE__ */ new Map();
16704
18101
  }
16705
- const installed = toRecord13(lock.installed);
18102
+ const installed = toRecord15(lock.installed);
16706
18103
  const result = /* @__PURE__ */ new Map();
16707
18104
  for (const [name, rawEntry] of Object.entries(installed)) {
16708
- const entry = toRecord13(rawEntry);
18105
+ const entry = toRecord15(rawEntry);
16709
18106
  result.set(normalizeSkillName(name), {
16710
- source: readString14(entry.source) ?? "hub",
16711
- trust: readString14(entry.trust_level) ?? null
18107
+ source: readString16(entry.source) ?? "hub",
18108
+ trust: readString16(entry.trust_level) ?? null
16712
18109
  });
16713
18110
  }
16714
18111
  return result;
@@ -16757,9 +18154,9 @@ function compareCategoryNames(left, right) {
16757
18154
  return left.localeCompare(right);
16758
18155
  }
16759
18156
  async function readHermesConfigDocument2(configPath) {
16760
- const existingRaw = await readFile14(configPath, "utf8").catch(
18157
+ const existingRaw = await readFile15(configPath, "utf8").catch(
16761
18158
  (error) => {
16762
- if (isNodeError14(error, "ENOENT")) {
18159
+ if (isNodeError16(error, "ENOENT")) {
16763
18160
  return null;
16764
18161
  }
16765
18162
  throw error;
@@ -16768,13 +18165,13 @@ async function readHermesConfigDocument2(configPath) {
16768
18165
  const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
16769
18166
  return {
16770
18167
  document,
16771
- config: toRecord13(document.toJSON()),
18168
+ config: toRecord15(document.toJSON()),
16772
18169
  existingRaw
16773
18170
  };
16774
18171
  }
16775
18172
  async function writeHermesConfigDocument2(input) {
16776
18173
  const backupPath = input.existingRaw ? `${input.configPath}.bak.${Date.now()}` : null;
16777
- await mkdir13(path19.dirname(input.configPath), { recursive: true, mode: 448 });
18174
+ await mkdir13(path20.dirname(input.configPath), { recursive: true, mode: 448 });
16778
18175
  if (backupPath) {
16779
18176
  await copyFile4(input.configPath, backupPath);
16780
18177
  }
@@ -16790,21 +18187,21 @@ function readStringList3(value) {
16790
18187
  }
16791
18188
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
16792
18189
  }
16793
- function readString14(value) {
18190
+ function readString16(value) {
16794
18191
  return typeof value === "string" && value.trim() ? value.trim() : null;
16795
18192
  }
16796
- function toRecord13(value) {
18193
+ function toRecord15(value) {
16797
18194
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
16798
18195
  }
16799
18196
  function ensureRecord3(target, key) {
16800
- const current = toRecord13(target[key]);
18197
+ const current = toRecord15(target[key]);
16801
18198
  if (current === target[key]) {
16802
18199
  return current;
16803
18200
  }
16804
18201
  target[key] = current;
16805
18202
  return current;
16806
18203
  }
16807
- function isNodeError14(error, code) {
18204
+ function isNodeError16(error, code) {
16808
18205
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
16809
18206
  }
16810
18207
 
@@ -16819,7 +18216,7 @@ function registerProfileSkillRoutes(router, options) {
16819
18216
  router.patch("/api/v1/profiles/:name/skills/:skillName", async (ctx) => {
16820
18217
  await authenticateRequest(ctx, paths);
16821
18218
  const body = await readJsonBody(ctx.req);
16822
- const enabled = readBoolean(body.enabled);
18219
+ const enabled = readBoolean2(body.enabled);
16823
18220
  if (enabled === void 0) {
16824
18221
  throw new LinkHttpError(
16825
18222
  400,
@@ -17107,7 +18504,7 @@ function registerRunRoutes(router, options) {
17107
18504
  router.post("/api/v1/runs", async (ctx) => {
17108
18505
  await authenticateRequest(ctx, paths);
17109
18506
  const body = await readJsonBody(ctx.req);
17110
- const input = readString12(body, "input");
18507
+ const input = readString14(body, "input");
17111
18508
  if (!input) {
17112
18509
  throw new LinkHttpError(400, "run_input_required", "input is required");
17113
18510
  }
@@ -17115,11 +18512,11 @@ function registerRunRoutes(router, options) {
17115
18512
  ctx.body = await createHermesRun(
17116
18513
  {
17117
18514
  input,
17118
- instructions: readString12(body, "instructions") ?? void 0,
18515
+ instructions: readString14(body, "instructions") ?? void 0,
17119
18516
  conversation_history: readConversationHistory(
17120
18517
  body.conversation_history ?? body.conversationHistory
17121
18518
  ),
17122
- session_id: readString12(body, "session_id") ?? readString12(body, "sessionId") ?? void 0
18519
+ session_id: readString14(body, "session_id") ?? readString14(body, "sessionId") ?? void 0
17123
18520
  },
17124
18521
  { logger, profileName: readOptionalProfileName(body) }
17125
18522
  );
@@ -17290,8 +18687,8 @@ function readModelList(payload) {
17290
18687
  // src/hermes/updates.ts
17291
18688
  import { EventEmitter as EventEmitter3 } from "events";
17292
18689
  import { spawn as spawn3 } from "child_process";
17293
- import { mkdir as mkdir14, readFile as readFile15, rm as rm8 } from "fs/promises";
17294
- import path20 from "path";
18690
+ import { mkdir as mkdir14, readFile as readFile16, rm as rm8 } from "fs/promises";
18691
+ import path21 from "path";
17295
18692
  var SERVER_HERMES_RELEASES_LATEST_PATH = "/api/v1/hermes-agent/releases/latest";
17296
18693
  var RELEASE_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
17297
18694
  var RELEASE_FETCH_TIMEOUT_MS = 5e3;
@@ -17512,24 +18909,24 @@ async function readRemoteRelease(options, now) {
17512
18909
  }
17513
18910
  }
17514
18911
  function normalizeServerReleaseSnapshot(payload) {
17515
- const snapshot = toRecord14(payload);
18912
+ const snapshot = toRecord16(payload);
17516
18913
  const remote = toNullableRecord(snapshot.remote);
17517
18914
  return {
17518
18915
  remote: remote ? normalizeServerRelease(remote) : null,
17519
- cacheState: readString15(snapshot, "cache_state") ?? readString15(snapshot, "cacheState"),
17520
- issue: readString15(snapshot, "issue")
18916
+ cacheState: readString17(snapshot, "cache_state") ?? readString17(snapshot, "cacheState"),
18917
+ issue: readString17(snapshot, "issue")
17521
18918
  };
17522
18919
  }
17523
18920
  function normalizeServerRelease(payload) {
17524
- const tag = readString15(payload, "tag");
17525
- const name = readString15(payload, "name");
18921
+ const tag = readString17(payload, "tag");
18922
+ const name = readString17(payload, "name");
17526
18923
  return {
17527
- version: readString15(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
18924
+ version: readString17(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
17528
18925
  tag,
17529
18926
  name,
17530
- releaseUrl: readString15(payload, "releaseUrl") ?? readString15(payload, "release_url"),
17531
- publishedAt: readString15(payload, "publishedAt") ?? readString15(payload, "published_at"),
17532
- fetchedAt: readString15(payload, "fetchedAt") ?? readString15(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
18927
+ releaseUrl: readString17(payload, "releaseUrl") ?? readString17(payload, "release_url"),
18928
+ publishedAt: readString17(payload, "publishedAt") ?? readString17(payload, "published_at"),
18929
+ fetchedAt: readString17(payload, "fetchedAt") ?? readString17(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
17533
18930
  };
17534
18931
  }
17535
18932
  async function readReleaseCache(paths) {
@@ -17548,7 +18945,7 @@ async function writeUpdateState(paths, state) {
17548
18945
  await writeJsonFile(updateStatePath(paths), state);
17549
18946
  }
17550
18947
  async function readUpdateLogLines(paths) {
17551
- const raw = await readFile15(updateLogPath(paths), "utf8").catch(() => "");
18948
+ const raw = await readFile16(updateLogPath(paths), "utf8").catch(() => "");
17552
18949
  if (!raw.trim()) {
17553
18950
  return [];
17554
18951
  }
@@ -17557,13 +18954,13 @@ async function readUpdateLogLines(paths) {
17557
18954
  );
17558
18955
  }
17559
18956
  function releaseCachePath(paths) {
17560
- return path20.join(paths.indexesDir, "hermes-release-check.json");
18957
+ return path21.join(paths.indexesDir, "hermes-release-check.json");
17561
18958
  }
17562
18959
  function updateStatePath(paths) {
17563
- return path20.join(paths.runDir, "hermes-update-state.json");
18960
+ return path21.join(paths.runDir, "hermes-update-state.json");
17564
18961
  }
17565
18962
  function updateLogPath(paths) {
17566
- return path20.join(paths.logsDir, UPDATE_LOG_FILE);
18963
+ return path21.join(paths.logsDir, UPDATE_LOG_FILE);
17567
18964
  }
17568
18965
  async function clearUpdateLogFiles(paths) {
17569
18966
  const primary = updateLogPath(paths);
@@ -17601,7 +18998,7 @@ function compareSemver2(left, right) {
17601
18998
  }
17602
18999
  return 0;
17603
19000
  }
17604
- function toRecord14(value) {
19001
+ function toRecord16(value) {
17605
19002
  return typeof value === "object" && value !== null ? value : {};
17606
19003
  }
17607
19004
  function toNullableRecord(value) {
@@ -17647,7 +19044,7 @@ function isRecentRunningState2(state) {
17647
19044
  const startedAt = Date.parse(state.started_at);
17648
19045
  return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
17649
19046
  }
17650
- function readString15(payload, key) {
19047
+ function readString17(payload, key) {
17651
19048
  const value = payload[key];
17652
19049
  return typeof value === "string" && value.trim() ? value.trim() : null;
17653
19050
  }
@@ -17655,13 +19052,13 @@ function readString15(payload, key) {
17655
19052
  // src/link/updates.ts
17656
19053
  import { spawn as spawn5 } from "child_process";
17657
19054
  import { EventEmitter as EventEmitter4 } from "events";
17658
- import { mkdir as mkdir17, readFile as readFile17, rm as rm11 } from "fs/promises";
17659
- import path22 from "path";
19055
+ import { mkdir as mkdir17, readFile as readFile18, rm as rm11 } from "fs/promises";
19056
+ import path23 from "path";
17660
19057
 
17661
19058
  // src/daemon/process.ts
17662
19059
  import { spawn as spawn4 } from "child_process";
17663
- import { mkdir as mkdir16, readFile as readFile16, rm as rm10 } from "fs/promises";
17664
- import path21 from "path";
19060
+ import { mkdir as mkdir16, readFile as readFile17, rm as rm10 } from "fs/promises";
19061
+ import path22 from "path";
17665
19062
 
17666
19063
  // src/daemon/service.ts
17667
19064
  import { createServer } from "http";
@@ -17839,7 +19236,7 @@ async function handleFrame(socket, raw, localPort, abortControllers) {
17839
19236
  // src/runtime/system-info.ts
17840
19237
  import { execFileSync } from "child_process";
17841
19238
  import { readFileSync } from "fs";
17842
- import os5 from "os";
19239
+ import os6 from "os";
17843
19240
  function readLinkSystemInfo() {
17844
19241
  const platform = process.platform;
17845
19242
  const hostname = readHostname(platform);
@@ -17878,7 +19275,7 @@ function readHostname(platform) {
17878
19275
  return computerName;
17879
19276
  }
17880
19277
  }
17881
- return normalizeText(os5.hostname());
19278
+ return normalizeText(os6.hostname());
17882
19279
  }
17883
19280
  function readOsLabel(platform) {
17884
19281
  if (platform === "darwin") {
@@ -17886,12 +19283,12 @@ function readOsLabel(platform) {
17886
19283
  return version ? `macOS ${version}` : "macOS";
17887
19284
  }
17888
19285
  if (platform === "linux") {
17889
- return readLinuxOsRelease() ?? `Linux ${os5.release()}`;
19286
+ return readLinuxOsRelease() ?? `Linux ${os6.release()}`;
17890
19287
  }
17891
19288
  if (platform === "win32") {
17892
- return `Windows ${os5.release()}`;
19289
+ return `Windows ${os6.release()}`;
17893
19290
  }
17894
- return `${os5.type()} ${os5.release()}`.trim();
19291
+ return `${os6.type()} ${os6.release()}`.trim();
17895
19292
  }
17896
19293
  function readLinuxOsRelease() {
17897
19294
  for (const file of ["/etc/os-release", "/usr/lib/os-release"]) {
@@ -17938,11 +19335,11 @@ function truncateText(value, maxLength) {
17938
19335
  }
17939
19336
 
17940
19337
  // src/topology/network.ts
17941
- import os7 from "os";
19338
+ import os8 from "os";
17942
19339
 
17943
19340
  // src/topology/environment.ts
17944
19341
  import { existsSync, readFileSync as readFileSync2 } from "fs";
17945
- import os6 from "os";
19342
+ import os7 from "os";
17946
19343
  function detectRuntimeEnvironment(env = process.env) {
17947
19344
  if (isWsl(env)) {
17948
19345
  return {
@@ -17971,7 +19368,7 @@ function isWsl(env) {
17971
19368
  if (env.WSL_DISTRO_NAME || env.WSL_INTEROP) {
17972
19369
  return true;
17973
19370
  }
17974
- const release = os6.release().toLowerCase();
19371
+ const release = os7.release().toLowerCase();
17975
19372
  return release.includes("microsoft") || release.includes("wsl");
17976
19373
  }
17977
19374
  function isContainer(env) {
@@ -18016,7 +19413,7 @@ async function discoverRouteCandidates(options) {
18016
19413
  };
18017
19414
  }
18018
19415
  function discoverLanIps() {
18019
- return discoverLanIpsFromInterfaces(os7.networkInterfaces());
19416
+ return discoverLanIpsFromInterfaces(os8.networkInterfaces());
18020
19417
  }
18021
19418
  function discoverLanIpsFromInterfaces(interfaces) {
18022
19419
  const result = /* @__PURE__ */ new Set();
@@ -18509,6 +19906,33 @@ function startCronDeliveryScheduler(options) {
18509
19906
  }
18510
19907
  };
18511
19908
  }
19909
+ function startHermesSessionSyncScheduler(options) {
19910
+ let running = false;
19911
+ const syncSessions = async () => {
19912
+ if (running) {
19913
+ return;
19914
+ }
19915
+ running = true;
19916
+ try {
19917
+ await options.conversations.syncHermesSessions();
19918
+ } catch (error) {
19919
+ void options.logger.warn("hermes_session_sync_failed", {
19920
+ error: error instanceof Error ? error.message : String(error)
19921
+ });
19922
+ } finally {
19923
+ running = false;
19924
+ }
19925
+ };
19926
+ const timer = setInterval(() => {
19927
+ void syncSessions();
19928
+ }, options.intervalMs ?? 10 * 60 * 1e3);
19929
+ timer.unref?.();
19930
+ return {
19931
+ close() {
19932
+ clearInterval(timer);
19933
+ }
19934
+ };
19935
+ }
18512
19936
 
18513
19937
  // src/daemon/service.ts
18514
19938
  async function startLinkService(options = {}) {
@@ -18529,11 +19953,21 @@ async function startLinkService(options = {}) {
18529
19953
  }
18530
19954
  const conversations = new ConversationService(paths, logger);
18531
19955
  await conversations.rebuildStatisticsIndex();
19956
+ const triggerHermesSessionSync = () => {
19957
+ void conversations.syncHermesSessions().catch((error) => {
19958
+ void logger.warn("hermes_session_sync_failed", {
19959
+ error: error instanceof Error ? error.message : String(error)
19960
+ });
19961
+ });
19962
+ };
18532
19963
  const app = await createApp({
18533
19964
  paths,
18534
19965
  logger,
18535
19966
  conversations,
18536
- onPairingClaimed: options.onPairingClaimed
19967
+ onPairingClaimed: async () => {
19968
+ triggerHermesSessionSync();
19969
+ await options.onPairingClaimed?.();
19970
+ }
18537
19971
  });
18538
19972
  const server = createServer(app.callback());
18539
19973
  try {
@@ -18553,11 +19987,16 @@ async function startLinkService(options = {}) {
18553
19987
  port: config.port,
18554
19988
  link_id: identity?.link_id ?? null
18555
19989
  });
19990
+ triggerHermesSessionSync();
18556
19991
  const scheduler = startCronDeliveryScheduler({
18557
19992
  paths,
18558
19993
  conversations,
18559
19994
  logger
18560
19995
  });
19996
+ const hermesSessionSyncScheduler = startHermesSessionSyncScheduler({
19997
+ conversations,
19998
+ logger
19999
+ });
18561
20000
  let relay = null;
18562
20001
  if (identity?.link_id) {
18563
20002
  relay = connectRelayControl({
@@ -18590,6 +20029,7 @@ async function startLinkService(options = {}) {
18590
20029
  return {
18591
20030
  async close() {
18592
20031
  scheduler.close();
20032
+ hermesSessionSyncScheduler.close();
18593
20033
  lanIpMonitor.close();
18594
20034
  relay?.close();
18595
20035
  await closeServer(server);
@@ -18702,7 +20142,7 @@ async function runDaemonSupervisor(paths = resolveRuntimePaths()) {
18702
20142
  await mkdir16(paths.logsDir, { recursive: true, mode: 448 });
18703
20143
  const log = createRotatingTextLogWriter({
18704
20144
  paths,
18705
- fileName: path21.basename(daemonLogFile(paths))
20145
+ fileName: path22.basename(daemonLogFile(paths))
18706
20146
  });
18707
20147
  const scriptPath = currentCliScriptPath();
18708
20148
  const child = spawn4(process.execPath, [scriptPath, "daemon", "--foreground"], {
@@ -18831,7 +20271,7 @@ function currentCliScriptPath() {
18831
20271
  return process.argv[1];
18832
20272
  }
18833
20273
  async function readPid(filePath) {
18834
- const raw = await readFile16(filePath, "utf8").catch(() => null);
20274
+ const raw = await readFile17(filePath, "utf8").catch(() => null);
18835
20275
  if (!raw) {
18836
20276
  return null;
18837
20277
  }
@@ -19157,21 +20597,21 @@ async function readRemoteLinkPolicy(options) {
19157
20597
  }
19158
20598
  }
19159
20599
  function normalizeServerSnapshot(payload) {
19160
- const snapshot = toRecord15(payload);
20600
+ const snapshot = toRecord17(payload);
19161
20601
  const policy = toNullableRecord2(snapshot.policy);
19162
20602
  if (!policy) {
19163
20603
  return {
19164
20604
  remote: null,
19165
- issue: readString16(snapshot, "issue")
20605
+ issue: readString18(snapshot, "issue")
19166
20606
  };
19167
20607
  }
19168
20608
  const release = toNullableRecord2(snapshot.release);
19169
- const currentVersion = readString16(policy, "current_version") ?? readString16(policy, "currentVersion");
19170
- const minSafeVersion = readString16(policy, "min_safe_version") ?? readString16(policy, "minSafeVersion");
20609
+ const currentVersion = readString18(policy, "current_version") ?? readString18(policy, "currentVersion");
20610
+ const minSafeVersion = readString18(policy, "min_safe_version") ?? readString18(policy, "minSafeVersion");
19171
20611
  if (!currentVersion) {
19172
20612
  return {
19173
20613
  remote: null,
19174
- issue: readString16(snapshot, "issue")
20614
+ issue: readString18(snapshot, "issue")
19175
20615
  };
19176
20616
  }
19177
20617
  return {
@@ -19179,10 +20619,10 @@ function normalizeServerSnapshot(payload) {
19179
20619
  current_version: currentVersion,
19180
20620
  min_safe_version: minSafeVersion,
19181
20621
  target_version: currentVersion,
19182
- release_url: release ? readString16(release, "release_url") ?? readString16(release, "releaseUrl") : null,
19183
- published_at: release ? readString16(release, "published_at") ?? readString16(release, "publishedAt") : null
20622
+ release_url: release ? readString18(release, "release_url") ?? readString18(release, "releaseUrl") : null,
20623
+ published_at: release ? readString18(release, "published_at") ?? readString18(release, "publishedAt") : null
19184
20624
  },
19185
- issue: readString16(snapshot, "issue")
20625
+ issue: readString18(snapshot, "issue")
19186
20626
  };
19187
20627
  }
19188
20628
  async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
@@ -19232,7 +20672,7 @@ async function writeUpdateState2(paths, state) {
19232
20672
  await writeJsonFile(updateStatePath2(paths), state);
19233
20673
  }
19234
20674
  async function readUpdateLogLines2(paths) {
19235
- const raw = await readFile17(updateLogPath2(paths), "utf8").catch(() => "");
20675
+ const raw = await readFile18(updateLogPath2(paths), "utf8").catch(() => "");
19236
20676
  if (!raw.trim()) {
19237
20677
  return [];
19238
20678
  }
@@ -19241,10 +20681,10 @@ async function readUpdateLogLines2(paths) {
19241
20681
  );
19242
20682
  }
19243
20683
  function updateStatePath2(paths) {
19244
- return path22.join(paths.runDir, "link-update-state.json");
20684
+ return path23.join(paths.runDir, "link-update-state.json");
19245
20685
  }
19246
20686
  function updateLogPath2(paths) {
19247
- return path22.join(paths.logsDir, UPDATE_LOG_FILE2);
20687
+ return path23.join(paths.logsDir, UPDATE_LOG_FILE2);
19248
20688
  }
19249
20689
  async function clearUpdateLogFiles2(paths) {
19250
20690
  const primary = updateLogPath2(paths);
@@ -19296,19 +20736,19 @@ function isProcessAlive4(pid) {
19296
20736
  return false;
19297
20737
  }
19298
20738
  }
19299
- function toRecord15(value) {
20739
+ function toRecord17(value) {
19300
20740
  return typeof value === "object" && value !== null ? value : {};
19301
20741
  }
19302
20742
  function toNullableRecord2(value) {
19303
20743
  return typeof value === "object" && value !== null ? value : null;
19304
20744
  }
19305
- function readString16(payload, key) {
20745
+ function readString18(payload, key) {
19306
20746
  const value = payload[key];
19307
20747
  return typeof value === "string" && value.trim() ? value.trim() : null;
19308
20748
  }
19309
20749
 
19310
20750
  // src/pairing/pairing.ts
19311
- import path23 from "path";
20751
+ import path24 from "path";
19312
20752
  import { rm as rm12 } from "fs/promises";
19313
20753
 
19314
20754
  // src/relay/bootstrap.ts
@@ -19584,8 +21024,8 @@ async function loadRequiredIdentity2(paths) {
19584
21024
  }
19585
21025
  return identity;
19586
21026
  }
19587
- async function postServerJson(serverBaseUrl, path24, body) {
19588
- const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path24}`, {
21027
+ async function postServerJson(serverBaseUrl, path25, body) {
21028
+ const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
19589
21029
  method: "POST",
19590
21030
  headers: {
19591
21031
  accept: "application/json",
@@ -19625,8 +21065,8 @@ function pairingErrorSnapshot(stage, error) {
19625
21065
  occurred_at: (/* @__PURE__ */ new Date()).toISOString()
19626
21066
  };
19627
21067
  }
19628
- async function patchServerJson(serverBaseUrl, path24, token, body) {
19629
- const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path24}`, {
21068
+ async function patchServerJson(serverBaseUrl, path25, token, body) {
21069
+ const response = await fetch(`${serverBaseUrl.replace(/\/+$/u, "")}${path25}`, {
19630
21070
  method: "PATCH",
19631
21071
  headers: {
19632
21072
  accept: "application/json",
@@ -19657,10 +21097,10 @@ function readErrorMessage5(payload) {
19657
21097
  return typeof message === "string" ? message : null;
19658
21098
  }
19659
21099
  function pairingClaimPath(sessionId, paths) {
19660
- return path23.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
21100
+ return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.claimed.json`);
19661
21101
  }
19662
21102
  function pairingSessionPath(sessionId, paths) {
19663
- return path23.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
21103
+ return path24.join(paths.pairingDir, `${Buffer.from(sessionId).toString("base64url")}.json`);
19664
21104
  }
19665
21105
  function qrPreferredUrls(routes) {
19666
21106
  return routes.preferredUrls.filter((url) => !url.includes("/api/v1/relay/links/")).slice(0, 1);
@@ -19731,8 +21171,8 @@ function registerSystemRoutes(router, options) {
19731
21171
  });
19732
21172
  router.post("/api/v1/pairing/claim", async (ctx) => {
19733
21173
  const body = await readJsonBody(ctx.req);
19734
- const sessionId = readString12(body, "session_id") ?? readString12(body, "sessionId");
19735
- const claimToken = readString12(body, "claim_token") ?? readString12(body, "claimToken");
21174
+ const sessionId = readString14(body, "session_id") ?? readString14(body, "sessionId");
21175
+ const claimToken = readString14(body, "claim_token") ?? readString14(body, "claimToken");
19736
21176
  if (!sessionId || !claimToken) {
19737
21177
  throw new LinkHttpError(
19738
21178
  400,
@@ -19743,10 +21183,10 @@ function registerSystemRoutes(router, options) {
19743
21183
  const claimed = await claimPairing({
19744
21184
  sessionId,
19745
21185
  claimToken,
19746
- deviceLabel: readString12(body, "device_label") ?? readString12(body, "deviceLabel") ?? "HermesPilot App",
19747
- devicePlatform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform") ?? "unknown",
19748
- deviceModel: readString12(body, "device_model") ?? readString12(body, "deviceModel"),
19749
- appInstanceId: readString12(body, "app_instance_id") ?? readString12(body, "appInstanceId"),
21186
+ deviceLabel: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
21187
+ devicePlatform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
21188
+ deviceModel: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
21189
+ appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
19750
21190
  paths
19751
21191
  });
19752
21192
  ctx.body = claimed;
@@ -19821,9 +21261,9 @@ function registerSystemRoutes(router, options) {
19821
21261
  const body = await readJsonBody(ctx.req);
19822
21262
  const session = await createDeviceSession(
19823
21263
  {
19824
- label: readString12(body, "device_label") ?? readString12(body, "deviceLabel") ?? "HermesPilot App",
19825
- platform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform") ?? "unknown",
19826
- model: readString12(body, "device_model") ?? readString12(body, "deviceModel"),
21264
+ label: readString14(body, "device_label") ?? readString14(body, "deviceLabel") ?? "HermesPilot App",
21265
+ platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform") ?? "unknown",
21266
+ model: readString14(body, "device_model") ?? readString14(body, "deviceModel"),
19827
21267
  appInstanceId: auth.appInstanceId
19828
21268
  },
19829
21269
  paths
@@ -19852,7 +21292,7 @@ function registerSystemRoutes(router, options) {
19852
21292
  });
19853
21293
  router.post("/api/v1/auth/refresh", async (ctx) => {
19854
21294
  const body = await readJsonBody(ctx.req);
19855
- const refreshToken = readString12(body, "refresh_token") ?? readString12(body, "refreshToken");
21295
+ const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
19856
21296
  if (!refreshToken) {
19857
21297
  throw new LinkHttpError(
19858
21298
  400,
@@ -19863,10 +21303,10 @@ function registerSystemRoutes(router, options) {
19863
21303
  const session = await refreshDeviceSession(
19864
21304
  refreshToken,
19865
21305
  {
19866
- appInstanceId: readString12(body, "app_instance_id") ?? readString12(body, "appInstanceId"),
19867
- label: readString12(body, "device_label") ?? readString12(body, "deviceLabel"),
19868
- platform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform"),
19869
- model: readString12(body, "device_model") ?? readString12(body, "deviceModel")
21306
+ appInstanceId: readString14(body, "app_instance_id") ?? readString14(body, "appInstanceId"),
21307
+ label: readString14(body, "device_label") ?? readString14(body, "deviceLabel"),
21308
+ platform: readString14(body, "device_platform") ?? readString14(body, "devicePlatform"),
21309
+ model: readString14(body, "device_model") ?? readString14(body, "deviceModel")
19870
21310
  },
19871
21311
  paths
19872
21312
  );
@@ -19885,7 +21325,7 @@ function registerSystemRoutes(router, options) {
19885
21325
  });
19886
21326
  router.post("/api/v1/auth/logout", async (ctx) => {
19887
21327
  const body = await readJsonBody(ctx.req);
19888
- const refreshToken = readString12(body, "refresh_token") ?? readString12(body, "refreshToken");
21328
+ const refreshToken = readString14(body, "refresh_token") ?? readString14(body, "refreshToken");
19889
21329
  if (refreshToken) {
19890
21330
  await revokeDeviceRefreshToken(refreshToken, paths);
19891
21331
  }
@@ -20110,7 +21550,7 @@ function registerSystemRoutes(router, options) {
20110
21550
  router.patch("/api/v1/devices/:deviceId", async (ctx) => {
20111
21551
  const auth = await authenticateRequest(ctx, paths);
20112
21552
  const body = await readJsonBody(ctx.req);
20113
- const label = readString12(body, "label") ?? readString12(body, "device_label");
21553
+ const label = readString14(body, "label") ?? readString14(body, "device_label");
20114
21554
  if (!label) {
20115
21555
  throw new LinkHttpError(
20116
21556
  400,
@@ -20150,11 +21590,11 @@ async function readActiveCronJobCount(profiles, logger) {
20150
21590
  }, 0);
20151
21591
  }
20152
21592
  function isActiveCronJob(job) {
20153
- const enabled = readBoolean(job.enabled) ?? true;
21593
+ const enabled = readBoolean2(job.enabled) ?? true;
20154
21594
  if (!enabled) {
20155
21595
  return false;
20156
21596
  }
20157
- const state = readString12(job, "state")?.toLowerCase();
21597
+ const state = readString14(job, "state")?.toLowerCase();
20158
21598
  return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
20159
21599
  }
20160
21600
  function filterLogsWithinHours(logs, hours, now = Date.now()) {
@@ -20248,7 +21688,7 @@ function registerLinkUpdateRoutes(router, options) {
20248
21688
  ctx.body = await startLinkUpdate({
20249
21689
  paths,
20250
21690
  logger,
20251
- targetVersion: readString12(body, "target_version") ?? readString12(body, "targetVersion")
21691
+ targetVersion: readString14(body, "target_version") ?? readString14(body, "targetVersion")
20252
21692
  });
20253
21693
  });
20254
21694
  router.get("/api/v1/link/update/events", async (ctx) => {
@@ -20282,7 +21722,7 @@ import QRCode from "qrcode";
20282
21722
  function registerPairingRoutes(router, options) {
20283
21723
  const { paths } = options;
20284
21724
  router.get("/pair", async (ctx) => {
20285
- const sessionId = readString12(ctx.query, "session_id");
21725
+ const sessionId = readString14(ctx.query, "session_id");
20286
21726
  if (!sessionId) {
20287
21727
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
20288
21728
  }
@@ -20307,7 +21747,7 @@ function registerPairingRoutes(router, options) {
20307
21747
  ctx.body = page;
20308
21748
  });
20309
21749
  router.get("/api/v1/pairing/session", async (ctx) => {
20310
- const sessionId = readString12(ctx.query, "session_id");
21750
+ const sessionId = readString14(ctx.query, "session_id");
20311
21751
  if (!sessionId) {
20312
21752
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
20313
21753
  }
@@ -20706,6 +22146,37 @@ function formatDate(value) {
20706
22146
  return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
20707
22147
  }
20708
22148
 
22149
+ // src/http/routes/internal.ts
22150
+ function registerInternalRoutes(router, options) {
22151
+ router.post("/internal/deliver", async (ctx) => {
22152
+ assertLoopbackRequest(ctx.req);
22153
+ const body = await readJsonBody(ctx.req);
22154
+ const stagingDir = readString14(body, "staging_dir") ?? readString14(body, "stagingDir");
22155
+ if (!stagingDir) {
22156
+ throw new LinkHttpError(
22157
+ 400,
22158
+ "delivery_staging_required",
22159
+ "delivery staging directory is required"
22160
+ );
22161
+ }
22162
+ ctx.body = {
22163
+ ok: true,
22164
+ ...await options.conversations.deliverStagedFiles(stagingDir)
22165
+ };
22166
+ });
22167
+ }
22168
+ function assertLoopbackRequest(request) {
22169
+ const address = request.socket.remoteAddress;
22170
+ if (address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1") {
22171
+ return;
22172
+ }
22173
+ throw new LinkHttpError(
22174
+ 403,
22175
+ "internal_route_forbidden",
22176
+ "internal route is only available on loopback"
22177
+ );
22178
+ }
22179
+
20709
22180
  // src/http/app.ts
20710
22181
  async function createApp(options = {}) {
20711
22182
  const paths = options.paths ?? resolveRuntimePaths();
@@ -20735,6 +22206,7 @@ async function createApp(options = {}) {
20735
22206
  logger,
20736
22207
  onPairingClaimed: options.onPairingClaimed
20737
22208
  });
22209
+ registerInternalRoutes(router, { conversations });
20738
22210
  registerPairingRoutes(router, { paths });
20739
22211
  registerHermesUpdateRoutes(router, { paths, logger });
20740
22212
  registerLinkUpdateRoutes(router, { paths, logger });
@@ -20764,11 +22236,13 @@ export {
20764
22236
  ensureHermesApiServerConfig,
20765
22237
  LinkHttpError,
20766
22238
  resolveRuntimePaths,
22239
+ createFileLogger,
20767
22240
  getLinkLogFile,
20768
22241
  ensureHermesApiServerAvailable,
20769
22242
  loadConfig,
20770
22243
  saveConfig,
20771
22244
  normalizeLanHost,
22245
+ ConversationService,
20772
22246
  loadIdentity,
20773
22247
  ensureIdentity,
20774
22248
  getIdentityStatus,