@hermespilot/link 0.3.2 → 0.3.4

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.
@@ -353,7 +353,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
353
353
  FROM run_usage_facts
354
354
  ${where.sql}
355
355
  GROUP BY COALESCE(NULLIF(model, ''), 'unknown'), provider
356
- HAVING total_tokens > 0
356
+ HAVING SUM(total_tokens) > 0
357
357
  ORDER BY total_tokens DESC, run_count DESC, model ASC
358
358
  LIMIT 12
359
359
  `).all(...where.params);
@@ -379,7 +379,7 @@ async function readLinkUsageStatistics(paths, filter = {}) {
379
379
  NULLIF(profile_uid, ''),
380
380
  'unknown'
381
381
  )
382
- HAVING total_tokens > 0
382
+ HAVING SUM(total_tokens) > 0
383
383
  ORDER BY total_tokens DESC, run_count DESC, profile ASC
384
384
  LIMIT 12
385
385
  `).all(...where.params);
@@ -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.2";
3727
+ var LINK_VERSION = "0.3.4";
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,9 +8516,338 @@ 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/delivery-import.ts
8520
+ import { lstat, readFile as readFile8, readdir as readdir5, stat as stat7 } from "fs/promises";
8521
+ import path13 from "path";
8522
+ var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
8523
+ var MAX_MEDIA_IMPORT_FAILURES = 20;
8524
+ var MAX_DELIVERY_FILES = 50;
8525
+ var DELIVERY_STAGING_SEGMENT = "delivery-staging";
8526
+ var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
8527
+ ".png",
8528
+ ".jpg",
8529
+ ".jpeg",
8530
+ ".gif",
8531
+ ".webp",
8532
+ ".heic",
8533
+ ".pdf",
8534
+ ".txt",
8535
+ ".log",
8536
+ ".md",
8537
+ ".markdown",
8538
+ ".json",
8539
+ ".jsonl",
8540
+ ".yaml",
8541
+ ".yml",
8542
+ ".toml",
8543
+ ".ini",
8544
+ ".xml",
8545
+ ".html",
8546
+ ".css",
8547
+ ".js",
8548
+ ".ts",
8549
+ ".jsx",
8550
+ ".tsx",
8551
+ ".dart",
8552
+ ".py",
8553
+ ".java",
8554
+ ".kt",
8555
+ ".swift",
8556
+ ".go",
8557
+ ".rs",
8558
+ ".rb",
8559
+ ".php",
8560
+ ".c",
8561
+ ".cc",
8562
+ ".cpp",
8563
+ ".h",
8564
+ ".hpp",
8565
+ ".cs",
8566
+ ".sql",
8567
+ ".csv",
8568
+ ".tsv",
8569
+ ".doc",
8570
+ ".docx",
8571
+ ".xls",
8572
+ ".xlsx",
8573
+ ".ppt",
8574
+ ".pptx",
8575
+ ".zip",
8576
+ ".rar",
8577
+ ".7z",
8578
+ ".tar",
8579
+ ".gz",
8580
+ ".mp4",
8581
+ ".mov",
8582
+ ".avi",
8583
+ ".mkv",
8584
+ ".webm",
8585
+ ".ogg",
8586
+ ".opus",
8587
+ ".mp3",
8588
+ ".wav",
8589
+ ".m4a"
8590
+ ]);
8591
+ function resolveDeliveryStagingTarget(paths, stagingDir) {
8592
+ const resolvedDir = path13.resolve(stagingDir);
8593
+ const relative = path13.relative(path13.resolve(paths.conversationsDir), resolvedDir);
8594
+ if (!relative || relative.startsWith("..") || path13.isAbsolute(relative)) {
8595
+ throw new LinkHttpError(
8596
+ 400,
8597
+ "delivery_staging_invalid",
8598
+ "delivery staging directory must be inside Hermes Link conversations"
8599
+ );
8600
+ }
8601
+ const segments = relative.split(path13.sep);
8602
+ if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
8603
+ throw new LinkHttpError(
8604
+ 400,
8605
+ "delivery_staging_invalid",
8606
+ "delivery staging directory is invalid"
8607
+ );
8608
+ }
8609
+ return {
8610
+ conversationId: segments[0],
8611
+ runId: segments[2],
8612
+ stagingDir: resolvedDir
8613
+ };
8614
+ }
8615
+ async function collectStagedDeliveryReferences(stagingDir) {
8616
+ const directoryStat = await lstat(stagingDir).catch((error) => {
8617
+ if (isNodeError9(error, "ENOENT")) {
8618
+ throw new LinkHttpError(
8619
+ 404,
8620
+ "delivery_staging_not_found",
8621
+ "delivery staging directory was not found"
8622
+ );
8623
+ }
8624
+ throw error;
8625
+ });
8626
+ if (!directoryStat.isDirectory()) {
8627
+ throw new LinkHttpError(
8628
+ 400,
8629
+ "delivery_staging_not_directory",
8630
+ "delivery staging path is not a directory"
8631
+ );
8632
+ }
8633
+ const entries = await readdir5(stagingDir, { withFileTypes: true });
8634
+ return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
8635
+ (left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
8636
+ ).slice(0, MAX_DELIVERY_FILES).map((entry) => {
8637
+ const sourcePath = path13.join(stagingDir, entry.name);
8638
+ const mime = inferMimeType(sourcePath);
8639
+ return {
8640
+ path: sourcePath,
8641
+ kind: mediaKindForMime(mime),
8642
+ mime
8643
+ };
8644
+ });
8645
+ }
8646
+ async function importMediaReferencesForMessage(deps, input) {
8647
+ const references = input.references.slice(0, input.maxReferences ?? MAX_DELIVERY_FILES);
8648
+ if (references.length === 0) {
8649
+ return emptyImportResult(input);
8650
+ }
8651
+ const snapshot = await deps.readSnapshot(input.conversationId);
8652
+ const assistant = snapshot.messages.find(
8653
+ (message) => message.id === input.messageId
8654
+ );
8655
+ if (!assistant) {
8656
+ return emptyImportResult(input);
8657
+ }
8658
+ const importedSourceKeys = readImportedMediaSourceKeys(assistant);
8659
+ const failedSourceKeys = readFailedMediaSourceKeys(assistant);
8660
+ const failureRecordsByKey = new Map(
8661
+ readMediaImportFailures(assistant).map((failure) => [failure.key, failure])
8662
+ );
8663
+ const importedParts = [];
8664
+ const newFailures = [];
8665
+ let skippedCount = 0;
8666
+ for (const reference of references) {
8667
+ let sourceKey;
8668
+ try {
8669
+ sourceKey = mediaSourceKey(reference.path);
8670
+ if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
8671
+ skippedCount += 1;
8672
+ continue;
8673
+ }
8674
+ const blob = await writeBlobFromFile(deps, input.conversationId, reference);
8675
+ const part = {
8676
+ type: reference.kind ?? mediaKindForMime(blob.mime),
8677
+ blob: blob.id,
8678
+ mime: blob.mime,
8679
+ size: blob.size,
8680
+ filename: blob.filename,
8681
+ url: `/api/v1/conversations/${encodeURIComponent(input.conversationId)}/blobs/${encodeURIComponent(blob.id)}`
8682
+ };
8683
+ assistant.parts.push(part);
8684
+ assistant.attachments.push({
8685
+ blob_id: blob.id,
8686
+ mime: blob.mime,
8687
+ size: blob.size,
8688
+ filename: blob.filename,
8689
+ source: "hermes_output"
8690
+ });
8691
+ importedSourceKeys.add(sourceKey);
8692
+ importedParts.push(part);
8693
+ } catch (error) {
8694
+ if (sourceKey && !failedSourceKeys.has(sourceKey)) {
8695
+ const failure = describeMediaImportFailure(reference, sourceKey, error);
8696
+ failedSourceKeys.add(sourceKey);
8697
+ failureRecordsByKey.set(sourceKey, failure);
8698
+ newFailures.push(failure);
8699
+ }
8700
+ void deps.logger.warn("conversation_media_import_failed", {
8701
+ conversation_id: input.conversationId,
8702
+ run_id: input.runId,
8703
+ message_id: input.messageId,
8704
+ error: error instanceof Error ? error.message : String(error)
8705
+ });
8706
+ }
8707
+ }
8708
+ if (importedParts.length === 0 && newFailures.length === 0) {
8709
+ return {
8710
+ ...emptyImportResult(input),
8711
+ discovered_count: references.length,
8712
+ skipped_count: skippedCount
8713
+ };
8714
+ }
8715
+ assistant.hermes = {
8716
+ ...toRecord7(assistant.hermes),
8717
+ imported_media_source_keys: [...importedSourceKeys],
8718
+ media_import_failed_source_keys: [...failedSourceKeys],
8719
+ media_import_failures: [...failureRecordsByKey.values()].slice(
8720
+ -MAX_MEDIA_IMPORT_FAILURES
8721
+ )
8722
+ };
8723
+ assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
8724
+ await deps.writeSnapshot(input.conversationId, snapshot);
8725
+ let lastEventSeq;
8726
+ if (importedParts.length > 0) {
8727
+ const event = await deps.appendEvent(input.conversationId, {
8728
+ type: "message.parts.created",
8729
+ message_id: input.messageId,
8730
+ run_id: input.runId,
8731
+ payload: { parts: importedParts }
8732
+ });
8733
+ lastEventSeq = event.seq;
8734
+ }
8735
+ return {
8736
+ conversation_id: input.conversationId,
8737
+ run_id: input.runId,
8738
+ message_id: input.messageId,
8739
+ discovered_count: references.length,
8740
+ imported_count: importedParts.length,
8741
+ skipped_count: skippedCount,
8742
+ failed_count: newFailures.length,
8743
+ parts: importedParts,
8744
+ ...lastEventSeq ? { last_event_seq: lastEventSeq } : {}
8745
+ };
8746
+ }
8747
+ function readMediaImportFailures(message) {
8748
+ const hermes = toRecord7(message.hermes);
8749
+ const failures = hermes.media_import_failures;
8750
+ if (!Array.isArray(failures)) {
8751
+ return [];
8752
+ }
8753
+ return failures.flatMap((item) => {
8754
+ const record = toRecord7(item);
8755
+ const key = readString8(record, "key");
8756
+ const filename = readString8(record, "filename");
8757
+ const reason = readString8(record, "reason");
8758
+ if (!key || !filename || !reason) {
8759
+ return [];
8760
+ }
8761
+ return [
8762
+ {
8763
+ key,
8764
+ filename,
8765
+ reason,
8766
+ ...readString8(record, "code") ? { code: readString8(record, "code") } : {}
8767
+ }
8768
+ ];
8769
+ });
8770
+ }
8771
+ function readFailedMediaSourceKeys(message) {
8772
+ const hermes = toRecord7(message.hermes);
8773
+ const keys = hermes.media_import_failed_source_keys;
8774
+ if (!Array.isArray(keys)) {
8775
+ return /* @__PURE__ */ new Set();
8776
+ }
8777
+ return new Set(
8778
+ keys.filter(
8779
+ (key) => typeof key === "string" && key.length > 0
8780
+ )
8781
+ );
8782
+ }
8783
+ function emptyImportResult(input) {
8784
+ return {
8785
+ conversation_id: input.conversationId,
8786
+ run_id: input.runId,
8787
+ message_id: input.messageId,
8788
+ discovered_count: 0,
8789
+ imported_count: 0,
8790
+ skipped_count: 0,
8791
+ failed_count: 0,
8792
+ parts: []
8793
+ };
8794
+ }
8795
+ async function writeBlobFromFile(deps, conversationId, source) {
8796
+ const sourcePath = resolveMediaSourcePath(source.path);
8797
+ const fileStat = await stat7(sourcePath).catch((error) => {
8798
+ if (isNodeError9(error, "ENOENT")) {
8799
+ throw new LinkHttpError(
8800
+ 404,
8801
+ "media_source_not_found",
8802
+ "Hermes output file was not found"
8803
+ );
8804
+ }
8805
+ throw error;
8806
+ });
8807
+ if (!fileStat.isFile()) {
8808
+ throw new LinkHttpError(
8809
+ 400,
8810
+ "media_source_not_file",
8811
+ "Hermes output media source is not a file"
8812
+ );
8813
+ }
8814
+ if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
8815
+ throw new LinkHttpError(
8816
+ 413,
8817
+ "media_source_too_large",
8818
+ "Hermes output media source is too large"
8819
+ );
8820
+ }
8821
+ return deps.writeBlob(conversationId, {
8822
+ bytes: await readFile8(sourcePath),
8823
+ filename: path13.basename(sourcePath),
8824
+ mime: source.mime ?? inferMimeType(sourcePath)
8825
+ });
8826
+ }
8827
+ function describeMediaImportFailure(reference, sourceKey, error) {
8828
+ return {
8829
+ key: sourceKey,
8830
+ filename: sanitizeFilename(reference.path, "attachment"),
8831
+ reason: error instanceof Error ? error.message : String(error),
8832
+ ...isNodeError9(error) && error.code ? { code: error.code } : {}
8833
+ };
8834
+ }
8835
+ function isSupportedDeliveryFilename(filename) {
8836
+ return SUPPORTED_DELIVERY_EXTENSIONS.has(path13.extname(filename).toLowerCase());
8837
+ }
8838
+ function readString8(payload, key) {
8839
+ const value = payload[key];
8840
+ return typeof value === "string" && value.trim() ? value.trim() : null;
8841
+ }
8842
+ function toRecord7(value) {
8843
+ return typeof value === "object" && value !== null ? value : {};
8844
+ }
8845
+ function isNodeError9(error, code) {
8846
+ return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
8847
+ }
8848
+
8519
8849
  // src/conversations/run-lifecycle.ts
8520
- import { readFile as readFile10, readdir as readdir5, stat as stat9 } from "fs/promises";
8521
- import path15 from "path";
8850
+ import { readdir as readdir6 } from "fs/promises";
8522
8851
 
8523
8852
  // src/hermes/api-server.ts
8524
8853
  async function listHermesModels(options = {}) {
@@ -8641,7 +8970,7 @@ async function createHermesRun(input, options = {}) {
8641
8970
  );
8642
8971
  }
8643
8972
  const payload = await readJsonResponse(response);
8644
- const runId = readString8(payload, "run_id") ?? readString8(payload, "runId") ?? readString8(payload, "id");
8973
+ const runId = readString9(payload, "run_id") ?? readString9(payload, "runId") ?? readString9(payload, "id");
8645
8974
  if (!runId) {
8646
8975
  throw new LinkHttpError(
8647
8976
  502,
@@ -8899,22 +9228,22 @@ function isRecord(value) {
8899
9228
  }
8900
9229
  function readUpstreamMessage(payload, raw) {
8901
9230
  const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
8902
- const message = readString8(error ?? {}, "message") ?? readString8(payload ?? {}, "message");
9231
+ const message = readString9(error ?? {}, "message") ?? readString9(payload ?? {}, "message");
8903
9232
  if (message) {
8904
9233
  return message;
8905
9234
  }
8906
9235
  const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
8907
9236
  return body || "empty response body";
8908
9237
  }
8909
- function readString8(payload, key) {
9238
+ function readString9(payload, key) {
8910
9239
  const value = payload[key];
8911
9240
  return typeof value === "string" && value.trim() ? value.trim() : null;
8912
9241
  }
8913
9242
 
8914
9243
  // src/conversations/history-builder.ts
8915
- import { readFile as readFile8, stat as stat7 } from "fs/promises";
9244
+ import { readFile as readFile9, stat as stat8 } from "fs/promises";
8916
9245
  import { createRequire as createRequire3 } from "module";
8917
- import path13 from "path";
9246
+ import path14 from "path";
8918
9247
  var nodeRequire3 = createRequire3(import.meta.url);
8919
9248
  var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
8920
9249
  var HERMES_HISTORY_COLUMNS = [
@@ -8976,13 +9305,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
8976
9305
  }
8977
9306
  const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
8978
9307
  const profileDir = resolveHermesProfileDir(normalizedProfileName);
8979
- const dbPath = path13.join(profileDir, "state.db");
9308
+ const dbPath = path14.join(profileDir, "state.db");
8980
9309
  const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
8981
9310
  sessionsDir: value.sessionsDir,
8982
9311
  configured: value.configured,
8983
9312
  configError: false
8984
9313
  })).catch(() => ({
8985
- sessionsDir: path13.join(profileDir, "sessions"),
9314
+ sessionsDir: path14.join(profileDir, "sessions"),
8986
9315
  configured: false,
8987
9316
  configError: true
8988
9317
  }));
@@ -9022,8 +9351,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
9022
9351
  };
9023
9352
  }
9024
9353
  async function readHermesStateDbHistory(dbPath, sessionId) {
9025
- const exists = await stat7(dbPath).then((value) => value.isFile()).catch((error) => {
9026
- if (isNodeError9(error, "ENOENT")) {
9354
+ const exists = await stat8(dbPath).then((value) => value.isFile()).catch((error) => {
9355
+ if (isNodeError10(error, "ENOENT")) {
9027
9356
  return false;
9028
9357
  }
9029
9358
  throw error;
@@ -9040,9 +9369,9 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
9040
9369
  if (!isValidSessionFileStem(sessionId)) {
9041
9370
  return empty;
9042
9371
  }
9043
- const transcriptPath = path13.join(sessionsDir, `${sessionId}.jsonl`);
9044
- const raw = await readFile8(transcriptPath, "utf8").catch((error) => {
9045
- if (isNodeError9(error, "ENOENT")) {
9372
+ const transcriptPath = path14.join(sessionsDir, `${sessionId}.jsonl`);
9373
+ const raw = await readFile9(transcriptPath, "utf8").catch((error) => {
9374
+ if (isNodeError10(error, "ENOENT")) {
9046
9375
  return "";
9047
9376
  }
9048
9377
  throw error;
@@ -9267,7 +9596,7 @@ function readTableColumns(db, table) {
9267
9596
  db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
9268
9597
  );
9269
9598
  }
9270
- function isNodeError9(error, code) {
9599
+ function isNodeError10(error, code) {
9271
9600
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
9272
9601
  }
9273
9602
  function isValidProfileName2(value) {
@@ -9285,8 +9614,8 @@ function normalizeProfileForCompare(value) {
9285
9614
 
9286
9615
  // src/hermes/stt.ts
9287
9616
  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";
9617
+ import { access as access2, readFile as readFile10, stat as stat9 } from "fs/promises";
9618
+ import path15 from "path";
9290
9619
  import { promisify as promisify3 } from "util";
9291
9620
  var execFileAsync3 = promisify3(execFile3);
9292
9621
  var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
@@ -9381,7 +9710,7 @@ async function buildHermesSttEnv(profileName) {
9381
9710
  };
9382
9711
  const devSource = await findDevHermesAgentSource();
9383
9712
  if (devSource) {
9384
- env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path14.delimiter);
9713
+ env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path15.delimiter);
9385
9714
  }
9386
9715
  return env;
9387
9716
  }
@@ -9428,14 +9757,14 @@ async function resolveHermesPythonCommand() {
9428
9757
  };
9429
9758
  }
9430
9759
  async function resolveExecutablePath(command) {
9431
- if (path14.isAbsolute(command)) {
9760
+ if (path15.isAbsolute(command)) {
9432
9761
  return await isExecutableFile(command) ? command : null;
9433
9762
  }
9434
9763
  const pathEnv = process.env.PATH ?? "";
9435
9764
  const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
9436
- for (const dir of pathEnv.split(path14.delimiter)) {
9765
+ for (const dir of pathEnv.split(path15.delimiter)) {
9437
9766
  for (const extension of extensions) {
9438
- const candidate = path14.join(dir, `${command}${extension}`);
9767
+ const candidate = path15.join(dir, `${command}${extension}`);
9439
9768
  if (await isExecutableFile(candidate)) {
9440
9769
  return candidate;
9441
9770
  }
@@ -9445,7 +9774,7 @@ async function resolveExecutablePath(command) {
9445
9774
  }
9446
9775
  async function isExecutableFile(filePath) {
9447
9776
  try {
9448
- const info = await stat8(filePath);
9777
+ const info = await stat9(filePath);
9449
9778
  if (!info.isFile()) {
9450
9779
  return false;
9451
9780
  }
@@ -9456,7 +9785,7 @@ async function isExecutableFile(filePath) {
9456
9785
  }
9457
9786
  }
9458
9787
  async function readShebang(filePath) {
9459
- const raw = await readFile9(filePath, "utf8").catch(() => "");
9788
+ const raw = await readFile10(filePath, "utf8").catch(() => "");
9460
9789
  const firstLine = raw.split(/\r?\n/u)[0]?.trim() ?? "";
9461
9790
  return firstLine.startsWith("#!") ? firstLine.slice(2).trim() : null;
9462
9791
  }
@@ -9475,8 +9804,8 @@ function shebangToPythonCommand(shebang) {
9475
9804
  }
9476
9805
  async function findDevHermesAgentSource() {
9477
9806
  const candidates = [
9478
- path14.resolve(process.cwd(), "reference/hermes-agent"),
9479
- path14.resolve(process.cwd(), "../../reference/hermes-agent")
9807
+ path15.resolve(process.cwd(), "reference/hermes-agent"),
9808
+ path15.resolve(process.cwd(), "../../reference/hermes-agent")
9480
9809
  ];
9481
9810
  for (const candidate of candidates) {
9482
9811
  if (await isDirectory(candidate)) {
@@ -9486,7 +9815,7 @@ async function findDevHermesAgentSource() {
9486
9815
  return null;
9487
9816
  }
9488
9817
  async function isDirectory(candidate) {
9489
- return stat8(candidate).then((info) => info.isDirectory()).catch(() => false);
9818
+ return stat9(candidate).then((info) => info.isDirectory()).catch(() => false);
9490
9819
  }
9491
9820
  function compactProcessOutput(value) {
9492
9821
  const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
@@ -9538,8 +9867,8 @@ function parseSseBlock(block) {
9538
9867
  if (decoded === null) {
9539
9868
  return null;
9540
9869
  }
9541
- const payload = toRecord7(decoded);
9542
- const payloadType = (readString9(payload, "type") ?? readString9(payload, "event") ?? readString9(payload, "object") ?? eventName) || "message";
9870
+ const payload = toRecord8(decoded);
9871
+ const payloadType = (readString10(payload, "type") ?? readString10(payload, "event") ?? readString10(payload, "object") ?? eventName) || "message";
9543
9872
  return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
9544
9873
  }
9545
9874
  function decodeJson(value) {
@@ -9555,11 +9884,11 @@ function decodeJson(value) {
9555
9884
  return { type: "message.delta", delta: value };
9556
9885
  }
9557
9886
  }
9558
- function readString9(body, key) {
9887
+ function readString10(body, key) {
9559
9888
  const value = body[key];
9560
9889
  return typeof value === "string" && value.trim() ? value.trim() : null;
9561
9890
  }
9562
- function toRecord7(value) {
9891
+ function toRecord8(value) {
9563
9892
  return typeof value === "object" && value !== null ? value : {};
9564
9893
  }
9565
9894
 
@@ -9583,8 +9912,8 @@ function normalizeHermesStreamEvent(event) {
9583
9912
  };
9584
9913
  }
9585
9914
  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;
9915
+ const toolName = readString11(event.payload, "tool") ?? readString11(event.payload, "name") ?? "tool";
9916
+ const preview = readString11(event.payload, "label") ?? readString11(event.payload, "preview") ?? toolName;
9588
9917
  return {
9589
9918
  ...event,
9590
9919
  payloadType: "tool.started",
@@ -9652,11 +9981,11 @@ function normalizeHermesResponseEvent(event) {
9652
9981
  }
9653
9982
  }
9654
9983
  function normalizeResponseOutputItemAdded(event) {
9655
- const item = toRecord8(event.payload.item);
9656
- if (readString10(item, "type") !== "function_call") {
9984
+ const item = toRecord9(event.payload.item);
9985
+ if (readString11(item, "type") !== "function_call") {
9657
9986
  return null;
9658
9987
  }
9659
- const toolName = readString10(item, "name") ?? "tool";
9988
+ const toolName = readString11(item, "name") ?? "tool";
9660
9989
  const argumentsValue = parseJsonValue(item.arguments) ?? item.arguments;
9661
9990
  return {
9662
9991
  ...event,
@@ -9666,16 +9995,16 @@ function normalizeResponseOutputItemAdded(event) {
9666
9995
  tool: toolName,
9667
9996
  tool_name: toolName,
9668
9997
  name: toolName,
9669
- tool_call_id: readString10(item, "call_id") ?? readString10(item, "id"),
9998
+ tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
9670
9999
  arguments: argumentsValue,
9671
10000
  preview: toolName,
9672
- response_item_id: readString10(item, "id") ?? void 0
10001
+ response_item_id: readString11(item, "id") ?? void 0
9673
10002
  }
9674
10003
  };
9675
10004
  }
9676
10005
  function normalizeResponseOutputItemDone(event) {
9677
- const item = toRecord8(event.payload.item);
9678
- if (readString10(item, "type") !== "function_call_output") {
10006
+ const item = toRecord9(event.payload.item);
10007
+ if (readString11(item, "type") !== "function_call_output") {
9679
10008
  return null;
9680
10009
  }
9681
10010
  const output = readResponseItemOutput(item.output);
@@ -9685,41 +10014,41 @@ function normalizeResponseOutputItemDone(event) {
9685
10014
  payloadType: "tool.completed",
9686
10015
  payload: {
9687
10016
  type: "tool.completed",
9688
- tool_call_id: readString10(item, "call_id") ?? readString10(item, "id"),
9689
- status: readString10(item, "status") ?? "completed",
10017
+ tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
10018
+ status: readString11(item, "status") ?? "completed",
9690
10019
  output,
9691
10020
  content: output,
9692
10021
  result: parsedOutput ?? output,
9693
- response_item_id: readString10(item, "id") ?? void 0
10022
+ response_item_id: readString11(item, "id") ?? void 0
9694
10023
  }
9695
10024
  };
9696
10025
  }
9697
10026
  function normalizeResponseCompleted(event) {
9698
- const response = toRecord8(event.payload.response);
10027
+ const response = toRecord9(event.payload.response);
9699
10028
  return {
9700
10029
  ...event,
9701
10030
  payloadType: "run.completed",
9702
10031
  payload: {
9703
10032
  type: "run.completed",
9704
- response_id: readString10(response, "id") ?? readString10(event.payload, "id"),
9705
- usage: toRecord8(response.usage),
10033
+ response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
10034
+ usage: toRecord9(response.usage),
9706
10035
  response
9707
10036
  }
9708
10037
  };
9709
10038
  }
9710
10039
  function normalizeResponseFailed(event) {
9711
- const response = toRecord8(event.payload.response);
9712
- const error = toRecord8(response.error);
10040
+ const response = toRecord9(event.payload.response);
10041
+ const error = toRecord9(response.error);
9713
10042
  return {
9714
10043
  ...event,
9715
10044
  payloadType: "run.failed",
9716
10045
  payload: {
9717
10046
  type: "run.failed",
9718
- response_id: readString10(response, "id") ?? readString10(event.payload, "id"),
10047
+ response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
9719
10048
  error: {
9720
- message: readString10(error, "message") ?? readString10(event.payload, "message") ?? "Hermes run failed"
10049
+ message: readString11(error, "message") ?? readString11(event.payload, "message") ?? "Hermes run failed"
9721
10050
  },
9722
- usage: toRecord8(response.usage),
10051
+ usage: toRecord9(response.usage),
9723
10052
  response
9724
10053
  }
9725
10054
  };
@@ -9743,8 +10072,8 @@ function readErrorMessage2(payload) {
9743
10072
  if (typeof payload.error === "string" && payload.error.trim()) {
9744
10073
  return payload.error.trim();
9745
10074
  }
9746
- const error = toRecord8(payload.error);
9747
- return readString10(error, "message") ?? readString10(payload, "message");
10075
+ const error = toRecord9(payload.error);
10076
+ return readString11(error, "message") ?? readString11(payload, "message");
9748
10077
  }
9749
10078
  function readDelta(payload) {
9750
10079
  return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
@@ -9779,15 +10108,15 @@ function isTopLevelErrorEvent(event) {
9779
10108
  }
9780
10109
  function readChatCompletionDelta(payload) {
9781
10110
  const choice = readFirstChoice(payload);
9782
- const delta = toRecord8(choice.delta);
10111
+ const delta = toRecord9(choice.delta);
9783
10112
  return readText2(delta, "content");
9784
10113
  }
9785
10114
  function readChatCompletionFinishReason(payload) {
9786
10115
  const choice = readFirstChoice(payload);
9787
- return readString10(choice, "finish_reason") ?? readString10(choice, "finishReason");
10116
+ return readString11(choice, "finish_reason") ?? readString11(choice, "finishReason");
9788
10117
  }
9789
10118
  function readChatCompletionUsage(payload) {
9790
- const usage = toRecord8(payload.usage);
10119
+ const usage = toRecord9(payload.usage);
9791
10120
  const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
9792
10121
  const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
9793
10122
  const total = readInteger2(usage, "total_tokens");
@@ -9815,7 +10144,7 @@ function readFirstChoice(payload) {
9815
10144
  if (!Array.isArray(choices)) {
9816
10145
  return {};
9817
10146
  }
9818
- return toRecord8(choices[0]);
10147
+ return toRecord9(choices[0]);
9819
10148
  }
9820
10149
  function readInteger2(payload, key) {
9821
10150
  const value = payload[key];
@@ -9828,7 +10157,7 @@ function readInteger2(payload, key) {
9828
10157
  }
9829
10158
  return void 0;
9830
10159
  }
9831
- function readString10(payload, key) {
10160
+ function readString11(payload, key) {
9832
10161
  const value = payload[key];
9833
10162
  return typeof value === "string" && value.trim() ? value.trim() : null;
9834
10163
  }
@@ -9843,7 +10172,7 @@ function readResponseItemOutput(value) {
9843
10172
  if (!Array.isArray(value)) {
9844
10173
  return stringifyJsonValue(value);
9845
10174
  }
9846
- const text = value.map(toRecord8).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
10175
+ const text = value.map(toRecord9).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
9847
10176
  return text || stringifyJsonValue(value);
9848
10177
  }
9849
10178
  function parseJsonValue(value) {
@@ -9869,13 +10198,11 @@ function stringifyJsonValue(value) {
9869
10198
  return String(value);
9870
10199
  }
9871
10200
  }
9872
- function toRecord8(value) {
10201
+ function toRecord9(value) {
9873
10202
  return typeof value === "object" && value !== null ? value : {};
9874
10203
  }
9875
10204
 
9876
10205
  // src/conversations/run-lifecycle.ts
9877
- var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
9878
- var MAX_MEDIA_IMPORT_FAILURES = 20;
9879
10206
  var ConversationRunLifecycle = class {
9880
10207
  constructor(deps) {
9881
10208
  this.deps = deps;
@@ -10357,8 +10684,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10357
10684
  }
10358
10685
  const textPart = assistant.parts.find((part) => part.type === "text");
10359
10686
  const currentText = textPart?.text ?? "";
10360
- const pendingDeliveryText = readString11(
10361
- toRecord9(assistant.hermes),
10687
+ const pendingDeliveryText = readString12(
10688
+ toRecord10(assistant.hermes),
10362
10689
  "pending_media_delivery_text"
10363
10690
  );
10364
10691
  const normalizedDelta = normalizeStreamingTextDelta(
@@ -10373,7 +10700,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10373
10700
  pendingDeliveryText ?? ""
10374
10701
  );
10375
10702
  const nextHermes = {
10376
- ...toRecord9(assistant.hermes),
10703
+ ...toRecord10(assistant.hermes),
10377
10704
  ...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
10378
10705
  };
10379
10706
  if (!extracted.pendingText) {
@@ -10626,119 +10953,11 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10626
10953
  return snapshot?.runs.find((item) => item.id === runId)?.assistant_message_id;
10627
10954
  }
10628
10955
  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)
10956
+ await importMediaReferencesForMessage(this.deps, {
10957
+ conversationId,
10958
+ runId,
10959
+ messageId,
10960
+ references
10742
10961
  });
10743
10962
  }
10744
10963
  async readHermesCronJobIds(profileName) {
@@ -10748,7 +10967,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
10748
10967
  includeDisabled: true
10749
10968
  });
10750
10969
  return new Set(
10751
- jobs.map((job) => readString11(job, "id") ?? readString11(job, "job_id")).filter((id) => Boolean(id))
10970
+ jobs.map((job) => readString12(job, "id") ?? readString12(job, "job_id")).filter((id) => Boolean(id))
10752
10971
  );
10753
10972
  }
10754
10973
  async bindNewCronJobsCreatedByRun(input) {
@@ -10773,7 +10992,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
10773
10992
  "",
10774
10993
  "Delivery staging directory for this run:",
10775
10994
  `- ${deliveryStagingDir}`,
10776
- "Before final delivery markers, copy each deliverable file into this directory and reference the staged copy path in <hermes_link_delivery>."
10995
+ "Copy every deliverable file into this directory, then run:",
10996
+ `hermeslink deliver ${quoteShellArg(deliveryStagingDir)}`
10777
10997
  ] : [],
10778
10998
  "",
10779
10999
  "Current runtime selected by Hermes Link:",
@@ -10782,52 +11002,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
10782
11002
  "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
11003
  ].join("\n");
10784
11004
  }
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
11005
  function appendMediaImportFailureNotice(message) {
10830
- const hermes = toRecord9(message.hermes);
11006
+ const hermes = toRecord10(message.hermes);
10831
11007
  if (hermes.media_import_failure_notice_appended === true) {
10832
11008
  return;
10833
11009
  }
@@ -10849,6 +11025,9 @@ function appendMediaImportFailureNotice(message) {
10849
11025
  media_import_failure_notice_appended: true
10850
11026
  };
10851
11027
  }
11028
+ function quoteShellArg(value) {
11029
+ return `'${value.replaceAll("'", "'\\''")}'`;
11030
+ }
10852
11031
  function formatMediaImportFailureNotice(failures) {
10853
11032
  const filenames = failures.map((failure) => failure.filename);
10854
11033
  const target = filenames.length === 1 ? `\u6587\u4EF6\u201C${filenames[0]}\u201D` : `${filenames.length} \u4E2A\u6587\u4EF6\uFF08${formatFilenameList(filenames)}\uFF09`;
@@ -10867,18 +11046,18 @@ function formatFilenameList(filenames) {
10867
11046
  return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
10868
11047
  }
10869
11048
  async function readdirWithDirs(directory) {
10870
- return readdir5(directory, { withFileTypes: true }).catch((error) => {
10871
- if (isNodeError10(error, "ENOENT")) {
11049
+ return readdir6(directory, { withFileTypes: true }).catch((error) => {
11050
+ if (isNodeError11(error, "ENOENT")) {
10872
11051
  return [];
10873
11052
  }
10874
11053
  throw error;
10875
11054
  });
10876
11055
  }
10877
- function readString11(payload, key) {
11056
+ function readString12(payload, key) {
10878
11057
  const value = payload[key];
10879
11058
  return typeof value === "string" && value.trim() ? value.trim() : null;
10880
11059
  }
10881
- function toRecord9(value) {
11060
+ function toRecord10(value) {
10882
11061
  return typeof value === "object" && value !== null ? value : {};
10883
11062
  }
10884
11063
  function formatFailureMessage(message, detail) {
@@ -10894,17 +11073,17 @@ function isFileSearchCompletion(payloadType, payload) {
10894
11073
  if (payloadType !== "tool.completed") {
10895
11074
  return false;
10896
11075
  }
10897
- const tool = toRecord9(payload.tool);
10898
- const toolCall = toRecord9(payload.tool_call ?? payload.toolCall);
10899
- const fn = toRecord9(toolCall.function ?? payload.function);
11076
+ const tool = toRecord10(payload.tool);
11077
+ const toolCall = toRecord10(payload.tool_call ?? payload.toolCall);
11078
+ const fn = toRecord10(toolCall.function ?? payload.function);
10900
11079
  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")
11080
+ readString12(payload, "tool_name"),
11081
+ readString12(payload, "toolName"),
11082
+ readString12(payload, "name"),
11083
+ readString12(payload, "tool"),
11084
+ readString12(tool, "name"),
11085
+ readString12(toolCall, "name"),
11086
+ readString12(fn, "name")
10908
11087
  ].filter((value) => Boolean(value)).map(normalizeToolName);
10909
11088
  return candidates.some(
10910
11089
  (name) => [
@@ -11059,10 +11238,10 @@ function readResponseId(payload) {
11059
11238
  if (!payload) {
11060
11239
  return null;
11061
11240
  }
11062
- const response = toRecord9(payload.response);
11063
- return readString11(payload, "response_id") ?? readString11(response, "id");
11241
+ const response = toRecord10(payload.response);
11242
+ return readString12(payload, "response_id") ?? readString12(response, "id");
11064
11243
  }
11065
- function isNodeError10(error, code) {
11244
+ function isNodeError11(error, code) {
11066
11245
  if (typeof error !== "object" || error === null || !("code" in error)) {
11067
11246
  return false;
11068
11247
  }
@@ -11342,6 +11521,42 @@ var ConversationService = class {
11342
11521
  async syncCronDeliveries() {
11343
11522
  await syncHermesLinkCronDeliveries(this.paths, this, this.logger);
11344
11523
  }
11524
+ async deliverStagedFiles(stagingDir) {
11525
+ const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
11526
+ return this.withConversationLock(target.conversationId, async () => {
11527
+ await this.store.readActiveManifest(target.conversationId);
11528
+ const snapshot = await this.store.readSnapshot(target.conversationId);
11529
+ const run = snapshot.runs.find((item) => item.id === target.runId);
11530
+ if (!run) {
11531
+ throw new LinkHttpError(404, "run_not_found", "Run was not found");
11532
+ }
11533
+ const assistant = snapshot.messages.find(
11534
+ (message) => message.id === run.assistant_message_id
11535
+ );
11536
+ if (!assistant) {
11537
+ throw new LinkHttpError(
11538
+ 404,
11539
+ "assistant_message_not_found",
11540
+ "Assistant message was not found"
11541
+ );
11542
+ }
11543
+ return importMediaReferencesForMessage(
11544
+ {
11545
+ logger: this.logger,
11546
+ readSnapshot: (conversationId) => this.store.readSnapshot(conversationId),
11547
+ writeSnapshot: (conversationId, nextSnapshot) => this.store.writeSnapshot(conversationId, nextSnapshot),
11548
+ appendEvent: (conversationId, input) => this.appendEvent(conversationId, input),
11549
+ writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input)
11550
+ },
11551
+ {
11552
+ conversationId: target.conversationId,
11553
+ runId: target.runId,
11554
+ messageId: assistant.id,
11555
+ references: await collectStagedDeliveryReferences(target.stagingDir)
11556
+ }
11557
+ );
11558
+ });
11559
+ }
11345
11560
  async getMessages(conversationId, options = {}) {
11346
11561
  return this.queries.getMessages(conversationId, options);
11347
11562
  }
@@ -12363,12 +12578,12 @@ async function readRawBody(request, maxBytes) {
12363
12578
  }
12364
12579
  return Buffer.concat(chunks);
12365
12580
  }
12366
- function readString12(body, key) {
12581
+ function readString13(body, key) {
12367
12582
  const value = body[key];
12368
12583
  return typeof value === "string" && value.trim() ? value.trim() : null;
12369
12584
  }
12370
12585
  function readOptionalProfileName(body) {
12371
- return readString12(body, "profile") ?? readString12(body, "profile_name") ?? readString12(body, "profileName") ?? void 0;
12586
+ return readString13(body, "profile") ?? readString13(body, "profile_name") ?? readString13(body, "profileName") ?? void 0;
12372
12587
  }
12373
12588
  function readStringArray(body, ...keys) {
12374
12589
  for (const key of keys) {
@@ -12601,7 +12816,7 @@ function registerConversationRoutes(router, options) {
12601
12816
  ctx.body = {
12602
12817
  ok: true,
12603
12818
  conversation: await conversations.createConversation({
12604
- title: readString12(body, "title") ?? void 0,
12819
+ title: readString13(body, "title") ?? void 0,
12605
12820
  profileName: readOptionalProfileName(body)
12606
12821
  })
12607
12822
  };
@@ -12672,7 +12887,7 @@ function registerConversationRoutes(router, options) {
12672
12887
  router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
12673
12888
  await authenticateRequest(ctx, paths);
12674
12889
  const body = await readJsonBody(ctx.req);
12675
- const content = readString12(body, "content") ?? readString12(body, "text") ?? readString12(body, "input") ?? "";
12890
+ const content = readString13(body, "content") ?? readString13(body, "text") ?? readString13(body, "input") ?? "";
12676
12891
  const attachments = readMessageAttachments(body.attachments ?? body.blobs);
12677
12892
  if (!content && attachments.length === 0) {
12678
12893
  throw new LinkHttpError(
@@ -12688,7 +12903,7 @@ function registerConversationRoutes(router, options) {
12688
12903
  conversationId: ctx.params.conversationId,
12689
12904
  content,
12690
12905
  attachments,
12691
- clientMessageId: readString12(body, "client_message_id") ?? readString12(body, "clientMessageId") ?? void 0,
12906
+ clientMessageId: readString13(body, "client_message_id") ?? readString13(body, "clientMessageId") ?? void 0,
12692
12907
  idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
12693
12908
  profileName: readOptionalProfileName(body)
12694
12909
  })
@@ -12697,7 +12912,7 @@ function registerConversationRoutes(router, options) {
12697
12912
  router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
12698
12913
  await authenticateRequest(ctx, paths);
12699
12914
  const body = await readJsonBody(ctx.req);
12700
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId") ?? readString12(body, "model");
12915
+ const modelId = readString13(body, "model_id") ?? readString13(body, "modelId") ?? readString13(body, "model");
12701
12916
  if (!modelId) {
12702
12917
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
12703
12918
  }
@@ -12727,7 +12942,7 @@ function registerConversationRoutes(router, options) {
12727
12942
  router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
12728
12943
  await authenticateRequest(ctx, paths);
12729
12944
  const body = await readJsonBody(ctx.req);
12730
- const title = readString12(body, "title") ?? readString12(body, "name") ?? readString12(body, "display_name");
12945
+ const title = readString13(body, "title") ?? readString13(body, "name") ?? readString13(body, "display_name");
12731
12946
  if (!title) {
12732
12947
  throw new LinkHttpError(400, "title_required", "title is required");
12733
12948
  }
@@ -12791,7 +13006,7 @@ function registerConversationRoutes(router, options) {
12791
13006
  async (ctx) => {
12792
13007
  await authenticateRequest(ctx, paths);
12793
13008
  const body = await readJsonBody(ctx.req);
12794
- const scope = readString12(body, "scope") ?? "always";
13009
+ const scope = readString13(body, "scope") ?? "always";
12795
13010
  ctx.body = {
12796
13011
  ok: true,
12797
13012
  ...await conversations.resolveApproval({
@@ -12851,7 +13066,7 @@ function registerConversationRoutes(router, options) {
12851
13066
  ctx.set("cache-control", "private, max-age=86400");
12852
13067
  ctx.set(
12853
13068
  "content-disposition",
12854
- `inline; filename="${blob.filename.replaceAll('"', "")}"`
13069
+ contentDispositionInline(blob.filename)
12855
13070
  );
12856
13071
  ctx.body = blob.bytes;
12857
13072
  }
@@ -12870,6 +13085,34 @@ function registerConversationRoutes(router, options) {
12870
13085
  }
12871
13086
  );
12872
13087
  }
13088
+ function contentDispositionInline(filename) {
13089
+ const fallback = asciiFilenameFallback(filename);
13090
+ return `inline; filename="${fallback}"; filename*=UTF-8''${encodeRfc5987Value(filename)}`;
13091
+ }
13092
+ function asciiFilenameFallback(filename) {
13093
+ const basename = filename.trim().split(/[\\/]/u).pop()?.trim() ?? "";
13094
+ const extension = safeAsciiExtension(basename);
13095
+ const stem = extension ? basename.slice(0, -extension.length) : basename;
13096
+ const asciiStem = stem.replace(/[^\x20-\x7E]/gu, "_").replace(/["\\]/gu, "_").replace(/[^A-Za-z0-9._ -]/gu, "_").replace(/_+/gu, "_").trim();
13097
+ if (/[A-Za-z0-9]/u.test(asciiStem)) {
13098
+ return `${asciiStem.slice(0, 120)}${extension}`;
13099
+ }
13100
+ return `attachment${extension}`;
13101
+ }
13102
+ function safeAsciiExtension(filename) {
13103
+ const dotIndex = filename.lastIndexOf(".");
13104
+ if (dotIndex < 0 || dotIndex === filename.length - 1) {
13105
+ return "";
13106
+ }
13107
+ const extension = filename.slice(dotIndex).toLowerCase();
13108
+ return /^\.[a-z0-9]{1,12}$/u.test(extension) ? extension : "";
13109
+ }
13110
+ function encodeRfc5987Value(value) {
13111
+ return encodeURIComponent(value).replace(
13112
+ /['()*]/gu,
13113
+ (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
13114
+ );
13115
+ }
12873
13116
  function isConversationNotificationEvent(event) {
12874
13117
  const type = event.type.toLowerCase();
12875
13118
  return type === "conversation.created" || type === "conversation.updated" || type === "conversation.deleted" || type === "message.created" || type === "message.completed" || type === "message.failed" || type === "run.completed" || type === "run.failed" || type === "run.cancelled" || type === "run.canceled" || type === "approval.requested" || readPayloadBool(event.payload, "requires_action") || readPayloadBool(event.payload, "requires_user_action") || readPayloadBool(event.payload, "requires_approval");
@@ -12923,7 +13166,7 @@ function createHttpErrorMiddleware(logger) {
12923
13166
  }
12924
13167
 
12925
13168
  // src/hermes/profiles.ts
12926
- import { mkdir as mkdir10, readdir as readdir6, readFile as readFile11, rename as rename4, rm as rm6, stat as stat10 } from "fs/promises";
13169
+ import { mkdir as mkdir10, readdir as readdir7, readFile as readFile11, rename as rename4, rm as rm6, stat as stat10 } from "fs/promises";
12927
13170
  import os4 from "os";
12928
13171
  import path16 from "path";
12929
13172
  import YAML2 from "yaml";
@@ -12933,9 +13176,9 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
12933
13176
  const profiles = /* @__PURE__ */ new Map();
12934
13177
  profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
12935
13178
  const profilesDir = path16.join(os4.homedir(), ".hermes", "profiles");
12936
- const entries = await readdir6(profilesDir, { withFileTypes: true }).catch(
13179
+ const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
12937
13180
  (error) => {
12938
- if (isNodeError11(error, "ENOENT")) {
13181
+ if (isNodeError12(error, "ENOENT")) {
12939
13182
  return [];
12940
13183
  }
12941
13184
  throw error;
@@ -12960,7 +13203,7 @@ async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
12960
13203
  assertProfileName(name);
12961
13204
  const profile = await profileInfo(name, paths);
12962
13205
  const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
12963
- if (isNodeError11(error, "ENOENT")) {
13206
+ if (isNodeError12(error, "ENOENT")) {
12964
13207
  return false;
12965
13208
  }
12966
13209
  throw error;
@@ -13001,7 +13244,7 @@ async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
13001
13244
  assertMutableProfile(name);
13002
13245
  const profile = await profileInfo(name, paths);
13003
13246
  const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
13004
- if (isNodeError11(error, "ENOENT")) {
13247
+ if (isNodeError12(error, "ENOENT")) {
13005
13248
  return false;
13006
13249
  }
13007
13250
  throw error;
@@ -13069,13 +13312,13 @@ function assertProfileName(name) {
13069
13312
  throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
13070
13313
  }
13071
13314
  }
13072
- function isNodeError11(error, code) {
13315
+ function isNodeError12(error, code) {
13073
13316
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
13074
13317
  }
13075
13318
  async function countSkills(root) {
13076
- const entries = await readdir6(root, { withFileTypes: true }).catch(
13319
+ const entries = await readdir7(root, { withFileTypes: true }).catch(
13077
13320
  (error) => {
13078
- if (isNodeError11(error, "ENOENT")) {
13321
+ if (isNodeError12(error, "ENOENT")) {
13079
13322
  return [];
13080
13323
  }
13081
13324
  throw error;
@@ -13102,7 +13345,7 @@ async function countConfiguredTools(profileName) {
13102
13345
  resolveHermesConfigPath(profileName),
13103
13346
  "utf8"
13104
13347
  ).catch((error) => {
13105
- if (isNodeError11(error, "ENOENT")) {
13348
+ if (isNodeError12(error, "ENOENT")) {
13106
13349
  return "";
13107
13350
  }
13108
13351
  throw error;
@@ -13110,14 +13353,14 @@ async function countConfiguredTools(profileName) {
13110
13353
  if (!raw.trim()) {
13111
13354
  return 0;
13112
13355
  }
13113
- const config = toRecord10(YAML2.parse(raw));
13356
+ const config = toRecord11(YAML2.parse(raw));
13114
13357
  const toolsets = /* @__PURE__ */ new Set();
13115
13358
  collectToolsetValues(config.toolsets, toolsets);
13116
- const platformToolsets = toRecord10(config.platform_toolsets);
13359
+ const platformToolsets = toRecord11(config.platform_toolsets);
13117
13360
  for (const value of Object.values(platformToolsets)) {
13118
13361
  collectToolsetValues(value, toolsets);
13119
13362
  }
13120
- const mcpServers = Object.keys(toRecord10(config.mcp_servers)).length;
13363
+ const mcpServers = Object.keys(toRecord11(config.mcp_servers)).length;
13121
13364
  return toolsets.size + mcpServers;
13122
13365
  }
13123
13366
  function collectToolsetValues(value, target) {
@@ -13131,7 +13374,7 @@ function collectToolsetValues(value, target) {
13131
13374
  target.add(value.trim());
13132
13375
  }
13133
13376
  }
13134
- function toRecord10(value) {
13377
+ function toRecord11(value) {
13135
13378
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
13136
13379
  }
13137
13380
 
@@ -13328,7 +13571,7 @@ function toHermesCronJobInput(input) {
13328
13571
  };
13329
13572
  }
13330
13573
  async function bindAndDecorateCronJobForHermesLink(input) {
13331
- const jobId = readString12(input.job, "id") ?? readString12(input.job, "job_id");
13574
+ const jobId = readString13(input.job, "id") ?? readString13(input.job, "job_id");
13332
13575
  if (!jobId) {
13333
13576
  return input.job;
13334
13577
  }
@@ -13345,9 +13588,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
13345
13588
  }
13346
13589
  function readCronJobCreateInput(body) {
13347
13590
  const input = {};
13348
- const name = readString12(body, "name") ?? readString12(body, "title");
13349
- const prompt = readString12(body, "prompt") ?? readString12(body, "description") ?? readString12(body, "task");
13350
- const schedule = readString12(body, "schedule");
13591
+ const name = readString13(body, "name") ?? readString13(body, "title");
13592
+ const prompt = readString13(body, "prompt") ?? readString13(body, "description") ?? readString13(body, "task");
13593
+ const schedule = readString13(body, "schedule");
13351
13594
  if (!name) {
13352
13595
  throw new LinkHttpError(400, "cron_job_name_required", "name is required");
13353
13596
  }
@@ -13368,7 +13611,7 @@ function readCronJobCreateInput(body) {
13368
13611
  input.name = name;
13369
13612
  input.prompt = prompt;
13370
13613
  input.schedule = schedule;
13371
- input.deliver = readString12(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
13614
+ input.deliver = readString13(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
13372
13615
  const skills = readOptionalCronSkills(body);
13373
13616
  if (skills) {
13374
13617
  input.skills = skills;
@@ -13596,7 +13839,7 @@ function registerModelConfigRoutes(router, options) {
13596
13839
  router.delete("/api/v1/model-configs", async (ctx) => {
13597
13840
  await authenticateRequest(ctx, paths);
13598
13841
  const body = await readJsonBody(ctx.req);
13599
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId");
13842
+ const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
13600
13843
  if (!modelId) {
13601
13844
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
13602
13845
  }
@@ -13651,7 +13894,7 @@ function registerModelConfigRoutes(router, options) {
13651
13894
  await authenticateRequest(ctx, paths);
13652
13895
  await getHermesProfileStatus(ctx.params.name, paths);
13653
13896
  const body = await readJsonBody(ctx.req);
13654
- const modelId = readString12(body, "model_id") ?? readString12(body, "modelId");
13897
+ const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
13655
13898
  if (!modelId) {
13656
13899
  throw new LinkHttpError(400, "model_id_required", "model_id is required");
13657
13900
  }
@@ -13668,9 +13911,9 @@ function registerModelConfigRoutes(router, options) {
13668
13911
  });
13669
13912
  }
13670
13913
  function readModelConfigInput(body) {
13671
- const id = readString12(body, "id") ?? readString12(body, "model_id") ?? readString12(body, "modelId");
13672
- const provider = readString12(body, "provider") ?? readString12(body, "provider_key") ?? readString12(body, "providerKey");
13673
- const baseUrl = readString12(body, "base_url") ?? readString12(body, "baseUrl");
13914
+ const id = readString13(body, "id") ?? readString13(body, "model_id") ?? readString13(body, "modelId");
13915
+ const provider = readString13(body, "provider") ?? readString13(body, "provider_key") ?? readString13(body, "providerKey");
13916
+ const baseUrl = readString13(body, "base_url") ?? readString13(body, "baseUrl");
13674
13917
  if (!id || !provider || !baseUrl) {
13675
13918
  throw new LinkHttpError(
13676
13919
  400,
@@ -13680,24 +13923,24 @@ function readModelConfigInput(body) {
13680
13923
  }
13681
13924
  return {
13682
13925
  id,
13683
- originalModelId: readString12(body, "original_model_id") ?? readString12(body, "originalModelId") ?? readString12(body, "original_id") ?? void 0,
13926
+ originalModelId: readString13(body, "original_model_id") ?? readString13(body, "originalModelId") ?? readString13(body, "original_id") ?? void 0,
13684
13927
  provider,
13685
- providerName: readString12(body, "provider_name") ?? readString12(body, "providerName") ?? void 0,
13928
+ providerName: readString13(body, "provider_name") ?? readString13(body, "providerName") ?? void 0,
13686
13929
  baseUrl,
13687
- apiKey: readString12(body, "api_key") ?? readString12(body, "apiKey") ?? void 0,
13688
- apiMode: readString12(body, "api_mode") ?? readString12(body, "apiMode") ?? void 0,
13930
+ apiKey: readString13(body, "api_key") ?? readString13(body, "apiKey") ?? void 0,
13931
+ apiMode: readString13(body, "api_mode") ?? readString13(body, "apiMode") ?? void 0,
13689
13932
  contextLength: readPositiveInteger2(
13690
13933
  body.context_length ?? body.contextLength
13691
13934
  ),
13692
- keyEnv: readString12(body, "key_env") ?? readString12(body, "keyEnv") ?? void 0,
13935
+ keyEnv: readString13(body, "key_env") ?? readString13(body, "keyEnv") ?? void 0,
13693
13936
  setDefault: readBoolean(body.set_default ?? body.setDefault),
13694
- reasoningEffort: readString12(body, "reasoning_effort") ?? readString12(body, "reasoningEffort") ?? void 0
13937
+ reasoningEffort: readString13(body, "reasoning_effort") ?? readString13(body, "reasoningEffort") ?? void 0
13695
13938
  };
13696
13939
  }
13697
13940
  function readModelDefaultsInput(body) {
13698
13941
  return {
13699
- taskModelId: readString12(body, "task_model_id") ?? readString12(body, "taskModelId") ?? readString12(body, "default_model_id") ?? readString12(body, "defaultModelId") ?? void 0,
13700
- compressionModelId: readString12(body, "compression_model_id") ?? readString12(body, "compressionModelId") ?? void 0
13942
+ taskModelId: readString13(body, "task_model_id") ?? readString13(body, "taskModelId") ?? readString13(body, "default_model_id") ?? readString13(body, "defaultModelId") ?? void 0,
13943
+ compressionModelId: readString13(body, "compression_model_id") ?? readString13(body, "compressionModelId") ?? void 0
13701
13944
  };
13702
13945
  }
13703
13946
  function shouldReloadGatewayAfterModelConfigChange(body) {
@@ -14178,7 +14421,7 @@ function copyModelConfig(source, target) {
14178
14421
  copied[key] = cloneJson(source[key]);
14179
14422
  }
14180
14423
  }
14181
- const sourceAuxiliary = toRecord11(source.auxiliary);
14424
+ const sourceAuxiliary = toRecord12(source.auxiliary);
14182
14425
  if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
14183
14426
  const targetAuxiliary = ensureRecord2(target, "auxiliary");
14184
14427
  targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
@@ -14187,12 +14430,12 @@ function copyModelConfig(source, target) {
14187
14430
  return copied;
14188
14431
  }
14189
14432
  function copyToolPermissionsConfig(source, target) {
14190
- const sourcePlatformToolsets = toRecord11(source.platform_toolsets);
14433
+ const sourcePlatformToolsets = toRecord12(source.platform_toolsets);
14191
14434
  if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
14192
14435
  const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
14193
14436
  targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
14194
14437
  }
14195
- const sourceStt = toRecord11(source.stt);
14438
+ const sourceStt = toRecord12(source.stt);
14196
14439
  if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
14197
14440
  const targetStt = ensureRecord2(target, "stt");
14198
14441
  targetStt.enabled = cloneJson(sourceStt.enabled);
@@ -14241,7 +14484,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
14241
14484
  async function writeEnvValues(profileName, values) {
14242
14485
  const envPath = path17.join(resolveHermesProfileDir(profileName), ".env");
14243
14486
  const existingRaw = await readFile12(envPath, "utf8").catch((error) => {
14244
- if (isNodeError12(error, "ENOENT")) {
14487
+ if (isNodeError13(error, "ENOENT")) {
14245
14488
  return "";
14246
14489
  }
14247
14490
  throw error;
@@ -14297,14 +14540,14 @@ function copyProperty(source, target, key) {
14297
14540
  async function readYamlConfig(configPath) {
14298
14541
  const existingRaw = await readFile12(configPath, "utf8").catch(
14299
14542
  (error) => {
14300
- if (isNodeError12(error, "ENOENT")) {
14543
+ if (isNodeError13(error, "ENOENT")) {
14301
14544
  return null;
14302
14545
  }
14303
14546
  throw error;
14304
14547
  }
14305
14548
  );
14306
14549
  return {
14307
- config: toRecord11(existingRaw ? YAML3.parse(existingRaw) : {}),
14550
+ config: toRecord12(existingRaw ? YAML3.parse(existingRaw) : {}),
14308
14551
  existingRaw
14309
14552
  };
14310
14553
  }
@@ -14383,7 +14626,7 @@ async function clearProfileCreationLogFiles(paths) {
14383
14626
  }
14384
14627
  async function pathExists(targetPath) {
14385
14628
  return await stat11(targetPath).then(() => true).catch((error) => {
14386
- if (isNodeError12(error, "ENOENT")) {
14629
+ if (isNodeError13(error, "ENOENT")) {
14387
14630
  return false;
14388
14631
  }
14389
14632
  throw error;
@@ -14415,7 +14658,7 @@ function ensureRecord2(target, key) {
14415
14658
  target[key] = next;
14416
14659
  return next;
14417
14660
  }
14418
- function toRecord11(value) {
14661
+ function toRecord12(value) {
14419
14662
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
14420
14663
  }
14421
14664
  function cloneJson(value) {
@@ -14430,7 +14673,7 @@ function formatEnvValue2(value) {
14430
14673
  function escapeRegExp2(value) {
14431
14674
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
14432
14675
  }
14433
- function isNodeError12(error, code) {
14676
+ function isNodeError13(error, code) {
14434
14677
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
14435
14678
  }
14436
14679
 
@@ -14515,16 +14758,16 @@ function readProfilePermissionsInput(body) {
14515
14758
  const approvals = readOptionalObject(body, "approvals");
14516
14759
  if (approvals) {
14517
14760
  input.approvals = {
14518
- mode: readString12(approvals, "mode") ?? readString12(approvals, "approval_mode") ?? readString12(approvals, "approvalMode") ?? void 0,
14761
+ mode: readString13(approvals, "mode") ?? readString13(approvals, "approval_mode") ?? readString13(approvals, "approvalMode") ?? void 0,
14519
14762
  timeout: readPositiveInteger2(approvals.timeout),
14520
- cronMode: readString12(approvals, "cron_mode") ?? readString12(approvals, "cronMode") ?? void 0
14763
+ cronMode: readString13(approvals, "cron_mode") ?? readString13(approvals, "cronMode") ?? void 0
14521
14764
  };
14522
14765
  }
14523
14766
  const terminal = readOptionalObject(body, "terminal");
14524
14767
  if (terminal) {
14525
14768
  input.terminal = {
14526
- backend: readString12(terminal, "backend") ?? void 0,
14527
- cwd: readString12(terminal, "cwd") ?? void 0,
14769
+ backend: readString13(terminal, "backend") ?? void 0,
14770
+ cwd: readString13(terminal, "cwd") ?? void 0,
14528
14771
  containerCpu: readPositiveInteger2(
14529
14772
  terminal.container_cpu ?? terminal.containerCpu
14530
14773
  ),
@@ -14641,7 +14884,7 @@ import {
14641
14884
  access as access3,
14642
14885
  copyFile as copyFile3,
14643
14886
  mkdir as mkdir12,
14644
- readdir as readdir7,
14887
+ readdir as readdir8,
14645
14888
  readFile as readFile13,
14646
14889
  rename as rename6,
14647
14890
  stat as stat12,
@@ -14650,8 +14893,8 @@ import {
14650
14893
  import path18 from "path";
14651
14894
  import YAML4 from "yaml";
14652
14895
  var ENTRY_DELIMITER = "\n\xA7\n";
14653
- var MEMORY_LIMIT = 2200;
14654
- var USER_LIMIT = 1375;
14896
+ var DEFAULT_MEMORY_LIMIT = 2200;
14897
+ var DEFAULT_USER_LIMIT = 1375;
14655
14898
  var CUSTOM_PROVIDER_CARD_ID = "__custom__";
14656
14899
  var CUSTOM_PROVIDER_REGISTRY_FILE = "memory-providers.json";
14657
14900
  var HINDSIGHT_DEFAULT_API_URL = "https://api.hindsight.vectorize.io";
@@ -14734,9 +14977,10 @@ var HermesMemoryError = class extends Error {
14734
14977
  };
14735
14978
  async function readHermesProfileMemory(profileName = "default") {
14736
14979
  const memoryDir = resolveMemoryDir(profileName);
14980
+ const limits = await readMemoryLimits(profileName);
14737
14981
  const [memoryStore, userStore, settings] = await Promise.all([
14738
- readMemoryStore(profileName, "memory"),
14739
- readMemoryStore(profileName, "user"),
14982
+ readMemoryStore(profileName, "memory", limits),
14983
+ readMemoryStore(profileName, "user", limits),
14740
14984
  readMemorySettings(profileName)
14741
14985
  ]);
14742
14986
  return {
@@ -14754,9 +14998,7 @@ async function addHermesMemoryEntry(profileName, target, content) {
14754
14998
  if (entries.includes(normalized)) {
14755
14999
  return entries;
14756
15000
  }
14757
- const next = [...entries, normalized];
14758
- assertWithinLimit(target, next);
14759
- return next;
15001
+ return [...entries, normalized];
14760
15002
  });
14761
15003
  return readHermesProfileMemory(profileName);
14762
15004
  }
@@ -14767,7 +15009,6 @@ async function replaceHermesMemoryEntry(profileName, target, oldText, content) {
14767
15009
  const index = findSingleMatch(entries, needle);
14768
15010
  const next = [...entries];
14769
15011
  next[index] = normalized;
14770
- assertWithinLimit(target, next);
14771
15012
  return next;
14772
15013
  });
14773
15014
  return readHermesProfileMemory(profileName);
@@ -14919,7 +15160,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
14919
15160
  "\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
14920
15161
  );
14921
15162
  }
14922
- const config = toRecord12(parsed);
15163
+ const config = toRecord13(parsed);
14923
15164
  if (Object.keys(config).length === 0 && parsed !== null) {
14924
15165
  throw new HermesMemoryError(
14925
15166
  "memory_provider_config_invalid",
@@ -15013,15 +15254,15 @@ async function patchHermesMemoryProvider(profileName, provider) {
15013
15254
  const configPath = resolveHermesConfigPath(profileName);
15014
15255
  const existingRaw = await readFile13(configPath, "utf8").catch(
15015
15256
  (error) => {
15016
- if (isNodeError13(error, "ENOENT")) {
15257
+ if (isNodeError14(error, "ENOENT")) {
15017
15258
  return null;
15018
15259
  }
15019
15260
  throw error;
15020
15261
  }
15021
15262
  );
15022
15263
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
15023
- const config = toRecord12(document.toJSON());
15024
- const memory = toRecord12(config.memory);
15264
+ const config = toRecord13(document.toJSON());
15265
+ const memory = toRecord13(config.memory);
15025
15266
  memory.provider = provider === "built-in" ? "" : provider;
15026
15267
  config.memory = memory;
15027
15268
  const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
@@ -15040,17 +15281,17 @@ async function patchHermesMemoryProvider(profileName, provider) {
15040
15281
  function resolveMemoryDir(profileName) {
15041
15282
  return path18.join(resolveHermesProfileDir(profileName), "memories");
15042
15283
  }
15043
- async function readMemoryStore(profileName, target) {
15284
+ async function readMemoryStore(profileName, target, limits) {
15044
15285
  const filePath = memoryFilePath(profileName, target);
15045
15286
  const entries = await readMemoryEntries(filePath);
15046
15287
  const fileStat = await stat12(filePath).catch((error) => {
15047
- if (isNodeError13(error, "ENOENT")) {
15288
+ if (isNodeError14(error, "ENOENT")) {
15048
15289
  return null;
15049
15290
  }
15050
15291
  throw error;
15051
15292
  });
15052
15293
  const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
15053
- const limit = target === "user" ? USER_LIMIT : MEMORY_LIMIT;
15294
+ const limit = memoryLimitForTarget(limits, target);
15054
15295
  return {
15055
15296
  target,
15056
15297
  label: target === "user" ? "\u5173\u4E8E\u7528\u6237" : "Agent \u7B14\u8BB0",
@@ -15073,7 +15314,7 @@ async function readMemoryStore(profileName, target) {
15073
15314
  }
15074
15315
  async function readMemoryEntries(filePath) {
15075
15316
  const raw = await readFile13(filePath, "utf8").catch((error) => {
15076
- if (isNodeError13(error, "ENOENT")) {
15317
+ if (isNodeError14(error, "ENOENT")) {
15077
15318
  return "";
15078
15319
  }
15079
15320
  throw error;
@@ -15091,7 +15332,7 @@ async function mutateMemoryEntries(profileName, target, mutate) {
15091
15332
  await writeMemoryEntries(profileName, target, mutate([...new Set(current)]));
15092
15333
  }
15093
15334
  async function writeMemoryEntries(profileName, target, entries) {
15094
- assertWithinLimit(target, entries);
15335
+ assertWithinLimit(target, entries, await readMemoryLimits(profileName));
15095
15336
  const filePath = memoryFilePath(profileName, target);
15096
15337
  const dir = path18.dirname(filePath);
15097
15338
  await mkdir12(dir, { recursive: true, mode: 448 });
@@ -15244,7 +15485,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15244
15485
  const config2 = await readJsonObject(
15245
15486
  memoryProviderConfigPath(profileName, "honcho") ?? ""
15246
15487
  );
15247
- return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString13(config2.apiKey)) || isConfiguredEnvValue(readString13(config2.api_key)) || isConfiguredEnvValue(readString13(config2.baseUrl)) ? { configured: true, issue: null } : {
15488
+ return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString14(config2.apiKey)) || isConfiguredEnvValue(readString14(config2.api_key)) || isConfiguredEnvValue(readString14(config2.baseUrl)) ? { configured: true, issue: null } : {
15248
15489
  configured: false,
15249
15490
  issue: "Honcho \u9700\u8981\u5148\u914D\u7F6E HONCHO_API_KEY\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
15250
15491
  };
@@ -15253,7 +15494,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15253
15494
  const config2 = await readJsonObject(
15254
15495
  memoryProviderConfigPath(profileName, "mem0") ?? ""
15255
15496
  );
15256
- return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString13(config2.api_key)) ? { configured: true, issue: null } : {
15497
+ return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString14(config2.api_key)) ? { configured: true, issue: null } : {
15257
15498
  configured: false,
15258
15499
  issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u673A Hermes .env \u914D\u7F6E MEM0_API_KEY\u3002"
15259
15500
  };
@@ -15295,7 +15536,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15295
15536
  memoryProviderConfigPath(profileName, provider) ?? ""
15296
15537
  );
15297
15538
  const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
15298
- const apiKey = readString13(config.apiKey) ?? readString13(config.api_key) ?? env.HINDSIGHT_API_KEY;
15539
+ const apiKey = readString14(config.apiKey) ?? readString14(config.api_key) ?? env.HINDSIGHT_API_KEY;
15299
15540
  if (mode === "cloud") {
15300
15541
  return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
15301
15542
  configured: false,
@@ -15303,15 +15544,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
15303
15544
  };
15304
15545
  }
15305
15546
  if (mode === "local_external") {
15306
- const apiUrl = readString13(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
15547
+ const apiUrl = readString14(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
15307
15548
  return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
15308
15549
  configured: false,
15309
15550
  issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
15310
15551
  };
15311
15552
  }
15312
15553
  if (mode === "local_embedded") {
15313
- const llmProvider = readString13(config.llm_provider) ?? "openai";
15314
- const llmModel = readString13(config.llm_model);
15554
+ const llmProvider = readString14(config.llm_provider) ?? "openai";
15555
+ const llmModel = readString14(config.llm_model);
15315
15556
  if (!llmModel) {
15316
15557
  return {
15317
15558
  configured: false,
@@ -15319,7 +15560,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15319
15560
  };
15320
15561
  }
15321
15562
  if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
15322
- readString13(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
15563
+ readString14(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
15323
15564
  )) {
15324
15565
  return {
15325
15566
  configured: false,
@@ -15327,7 +15568,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
15327
15568
  };
15328
15569
  }
15329
15570
  if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
15330
- readString13(config.llmApiKey) ?? readString13(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
15571
+ readString14(config.llmApiKey) ?? readString14(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
15331
15572
  )) {
15332
15573
  return {
15333
15574
  configured: false,
@@ -15467,8 +15708,8 @@ async function readProviderSettings(profileName, provider) {
15467
15708
  const config = await readJsonObject(
15468
15709
  memoryProviderConfigPath(profileName, provider) ?? ""
15469
15710
  );
15470
- const banks = toRecord12(config.banks);
15471
- const hermesBank = toRecord12(banks.hermes);
15711
+ const banks = toRecord13(config.banks);
15712
+ const hermesBank = toRecord13(banks.hermes);
15472
15713
  const mode = normalizeHindsightMode(config.mode);
15473
15714
  return [
15474
15715
  selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
@@ -15584,7 +15825,7 @@ function customProviderRegistryPath(profileName) {
15584
15825
  async function readCustomProviderRegistry(profileName) {
15585
15826
  const raw = await readFile13(customProviderRegistryPath(profileName), "utf8").catch(
15586
15827
  (error) => {
15587
- if (isNodeError13(error, "ENOENT")) {
15828
+ if (isNodeError14(error, "ENOENT")) {
15588
15829
  return "";
15589
15830
  }
15590
15831
  throw error;
@@ -15595,18 +15836,18 @@ async function readCustomProviderRegistry(profileName) {
15595
15836
  }
15596
15837
  try {
15597
15838
  const parsed = JSON.parse(raw);
15598
- const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord12(parsed).providers) ? toRecord12(parsed).providers : [];
15839
+ const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord13(parsed).providers) ? toRecord13(parsed).providers : [];
15599
15840
  return providers.map((item) => {
15600
15841
  if (typeof item === "string") {
15601
15842
  const id2 = normalizeCustomProviderId(item);
15602
15843
  return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
15603
15844
  }
15604
- const record = toRecord12(item);
15605
- const id = normalizeCustomProviderId(readString13(record.id) ?? "");
15845
+ const record = toRecord13(item);
15846
+ const id = normalizeCustomProviderId(readString14(record.id) ?? "");
15606
15847
  return {
15607
15848
  id,
15608
- label: readString13(record.label) ?? id,
15609
- description: readString13(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15849
+ label: readString14(record.label) ?? id,
15850
+ description: readString14(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15610
15851
  };
15611
15852
  }).filter((item) => item.id);
15612
15853
  } catch {
@@ -15631,9 +15872,9 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
15631
15872
  }
15632
15873
  async function discoverUserMemoryProviderDescriptors(profileName) {
15633
15874
  const pluginsDir = path18.join(resolveHermesProfileDir(profileName), "plugins");
15634
- const entries = await readdir7(pluginsDir, { withFileTypes: true }).catch(
15875
+ const entries = await readdir8(pluginsDir, { withFileTypes: true }).catch(
15635
15876
  (error) => {
15636
- if (isNodeError13(error, "ENOENT")) {
15877
+ if (isNodeError14(error, "ENOENT")) {
15637
15878
  return [];
15638
15879
  }
15639
15880
  throw error;
@@ -15657,8 +15898,8 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
15657
15898
  const meta = await readPluginMetadata(providerDir);
15658
15899
  descriptors.push({
15659
15900
  id: providerId,
15660
- label: readString13(meta.name) ?? providerId,
15661
- description: readString13(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15901
+ label: readString14(meta.name) ?? providerId,
15902
+ description: readString14(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
15662
15903
  });
15663
15904
  }
15664
15905
  return descriptors;
@@ -15674,7 +15915,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
15674
15915
  async function isMemoryProviderPluginDir(providerDir) {
15675
15916
  const source = await readFile13(path18.join(providerDir, "__init__.py"), "utf8").catch(
15676
15917
  (error) => {
15677
- if (isNodeError13(error, "ENOENT")) {
15918
+ if (isNodeError14(error, "ENOENT")) {
15678
15919
  return "";
15679
15920
  }
15680
15921
  throw error;
@@ -15686,13 +15927,13 @@ async function isMemoryProviderPluginDir(providerDir) {
15686
15927
  async function readPluginMetadata(providerDir) {
15687
15928
  const raw = await readFile13(path18.join(providerDir, "plugin.yaml"), "utf8").catch(
15688
15929
  (error) => {
15689
- if (isNodeError13(error, "ENOENT")) {
15930
+ if (isNodeError14(error, "ENOENT")) {
15690
15931
  return "";
15691
15932
  }
15692
15933
  throw error;
15693
15934
  }
15694
15935
  );
15695
- return raw ? toRecord12(YAML4.parse(raw)) : {};
15936
+ return raw ? toRecord13(YAML4.parse(raw)) : {};
15696
15937
  }
15697
15938
  async function resolveByteRoverCli() {
15698
15939
  const candidates = [
@@ -15712,30 +15953,30 @@ async function resolveByteRoverCli() {
15712
15953
  async function readHolographicProviderConfig(profileName) {
15713
15954
  const raw = await readFile13(resolveHermesConfigPath(profileName), "utf8").catch(
15714
15955
  (error) => {
15715
- if (isNodeError13(error, "ENOENT")) {
15956
+ if (isNodeError14(error, "ENOENT")) {
15716
15957
  return "";
15717
15958
  }
15718
15959
  throw error;
15719
15960
  }
15720
15961
  );
15721
- const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15722
- const plugins = toRecord12(config.plugins);
15723
- return toRecord12(plugins["hermes-memory-store"]);
15962
+ const config = raw ? toRecord13(YAML4.parse(raw)) : {};
15963
+ const plugins = toRecord13(config.plugins);
15964
+ return toRecord13(plugins["hermes-memory-store"]);
15724
15965
  }
15725
15966
  async function patchHolographicProviderConfig(profileName, patch) {
15726
15967
  const configPath = resolveHermesConfigPath(profileName);
15727
15968
  const existingRaw = await readFile13(configPath, "utf8").catch(
15728
15969
  (error) => {
15729
- if (isNodeError13(error, "ENOENT")) {
15970
+ if (isNodeError14(error, "ENOENT")) {
15730
15971
  return null;
15731
15972
  }
15732
15973
  throw error;
15733
15974
  }
15734
15975
  );
15735
15976
  const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
15736
- const config = toRecord12(document.toJSON());
15737
- const plugins = toRecord12(config.plugins);
15738
- const memoryStore = toRecord12(plugins["hermes-memory-store"]);
15977
+ const config = toRecord13(document.toJSON());
15978
+ const plugins = toRecord13(config.plugins);
15979
+ const memoryStore = toRecord13(plugins["hermes-memory-store"]);
15739
15980
  for (const [key, value] of Object.entries(patch)) {
15740
15981
  if (value !== void 0) {
15741
15982
  memoryStore[key] = value;
@@ -15764,7 +16005,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
15764
16005
  }
15765
16006
  const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
15766
16007
  const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
15767
- if (isNodeError13(error, "ENOENT")) {
16008
+ if (isNodeError14(error, "ENOENT")) {
15768
16009
  return "";
15769
16010
  }
15770
16011
  throw error;
@@ -15813,7 +16054,7 @@ function isMemoryEnvKeyWritable(key) {
15813
16054
  ].includes(key);
15814
16055
  }
15815
16056
  function normalizeHindsightMode(value) {
15816
- const mode = readString13(value) ?? "cloud";
16057
+ const mode = readString14(value) ?? "cloud";
15817
16058
  return mode === "local" ? "local_embedded" : mode;
15818
16059
  }
15819
16060
  async function readActiveMemoryProvider(profileName) {
@@ -15821,14 +16062,14 @@ async function readActiveMemoryProvider(profileName) {
15821
16062
  resolveHermesConfigPath(profileName),
15822
16063
  "utf8"
15823
16064
  ).catch((error) => {
15824
- if (isNodeError13(error, "ENOENT")) {
16065
+ if (isNodeError14(error, "ENOENT")) {
15825
16066
  return "";
15826
16067
  }
15827
16068
  throw error;
15828
16069
  });
15829
- const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15830
- const memory = toRecord12(config.memory);
15831
- const provider = readString13(memory.provider);
16070
+ const config = raw ? toRecord13(YAML4.parse(raw)) : {};
16071
+ const memory = toRecord13(config.memory);
16072
+ const provider = readString14(memory.provider);
15832
16073
  if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
15833
16074
  return null;
15834
16075
  }
@@ -15855,13 +16096,13 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
15855
16096
  }
15856
16097
  async function readJsonObject(filePath) {
15857
16098
  const raw = await readFile13(filePath, "utf8").catch((error) => {
15858
- if (isNodeError13(error, "ENOENT")) {
16099
+ if (isNodeError14(error, "ENOENT")) {
15859
16100
  return "{}";
15860
16101
  }
15861
16102
  throw error;
15862
16103
  });
15863
16104
  try {
15864
- return toRecord12(JSON.parse(raw || "{}"));
16105
+ return toRecord13(JSON.parse(raw || "{}"));
15865
16106
  } catch {
15866
16107
  throw new HermesMemoryError(
15867
16108
  "memory_provider_config_invalid",
@@ -15886,7 +16127,7 @@ function stringSetting(key, label, value, editable = true) {
15886
16127
  return {
15887
16128
  key,
15888
16129
  label,
15889
- value: readString13(value) ?? "",
16130
+ value: readString14(value) ?? "",
15890
16131
  editable,
15891
16132
  kind: "string"
15892
16133
  };
@@ -15901,11 +16142,31 @@ function textSetting(key, label, value, editable = true) {
15901
16142
  };
15902
16143
  }
15903
16144
  function selectSetting(key, label, value, options, editable = true) {
15904
- const stringValue = readString13(value) ?? options[0] ?? null;
16145
+ const stringValue = readString14(value) ?? options[0] ?? null;
15905
16146
  return { key, label, value: stringValue, editable, kind: "select", options };
15906
16147
  }
15907
- function assertWithinLimit(target, entries) {
15908
- const limit = target === "user" ? USER_LIMIT : MEMORY_LIMIT;
16148
+ async function readMemoryLimits(profileName) {
16149
+ const raw = await readFile13(
16150
+ resolveHermesConfigPath(profileName),
16151
+ "utf8"
16152
+ ).catch((error) => {
16153
+ if (isNodeError14(error, "ENOENT")) {
16154
+ return "";
16155
+ }
16156
+ throw error;
16157
+ });
16158
+ const config = raw ? toRecord13(YAML4.parse(raw)) : {};
16159
+ const memory = toRecord13(config.memory);
16160
+ return {
16161
+ memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
16162
+ user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
16163
+ };
16164
+ }
16165
+ function memoryLimitForTarget(limits, target) {
16166
+ return target === "user" ? limits.user : limits.memory;
16167
+ }
16168
+ function assertWithinLimit(target, entries, limits) {
16169
+ const limit = memoryLimitForTarget(limits, target);
15909
16170
  const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
15910
16171
  if (chars > limit) {
15911
16172
  throw new HermesMemoryError(
@@ -15951,12 +16212,16 @@ function hashString(value) {
15951
16212
  }
15952
16213
  return hash.toString(16);
15953
16214
  }
15954
- function toRecord12(value) {
16215
+ function toRecord13(value) {
15955
16216
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
15956
16217
  }
15957
- function readString13(value) {
16218
+ function readString14(value) {
15958
16219
  return typeof value === "string" && value.trim() ? value.trim() : null;
15959
16220
  }
16221
+ function readPositiveInteger3(value) {
16222
+ const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
16223
+ return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
16224
+ }
15960
16225
  function readBoolean2(value) {
15961
16226
  if (typeof value === "boolean") {
15962
16227
  return value;
@@ -15978,7 +16243,7 @@ function formatEnvValue3(value) {
15978
16243
  function escapeRegExp3(value) {
15979
16244
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
15980
16245
  }
15981
- function isNodeError13(error, code) {
16246
+ function isNodeError14(error, code) {
15982
16247
  return error instanceof Error && "code" in error && error.code === code;
15983
16248
  }
15984
16249
 
@@ -16092,7 +16357,7 @@ function registerProfileMemoryRoutes(router, options) {
16092
16357
  );
16093
16358
  }
16094
16359
  function readMemoryTarget(body) {
16095
- const raw = readString12(body, "target");
16360
+ const raw = readString13(body, "target");
16096
16361
  if (raw === "memory" || raw === "user") {
16097
16362
  return raw;
16098
16363
  }
@@ -16103,7 +16368,7 @@ function readMemoryTarget(body) {
16103
16368
  );
16104
16369
  }
16105
16370
  function readMemoryResetTarget(body) {
16106
- const raw = readString12(body, "target") ?? "all";
16371
+ const raw = readString13(body, "target") ?? "all";
16107
16372
  if (raw === "all" || raw === "memory" || raw === "user") {
16108
16373
  return raw;
16109
16374
  }
@@ -16114,7 +16379,7 @@ function readMemoryResetTarget(body) {
16114
16379
  );
16115
16380
  }
16116
16381
  function readRequiredMemoryContent(body) {
16117
- const content = readString12(body, "content") ?? readString12(body, "text");
16382
+ const content = readString13(body, "content") ?? readString13(body, "text");
16118
16383
  if (!content) {
16119
16384
  throw new LinkHttpError(
16120
16385
  400,
@@ -16125,7 +16390,7 @@ function readRequiredMemoryContent(body) {
16125
16390
  return content;
16126
16391
  }
16127
16392
  function readRequiredMemoryMatch(body) {
16128
- const oldText = readString12(body, "old_text") ?? readString12(body, "oldText") ?? readString12(body, "match");
16393
+ const oldText = readString13(body, "old_text") ?? readString13(body, "oldText") ?? readString13(body, "match");
16129
16394
  if (!oldText) {
16130
16395
  throw new LinkHttpError(
16131
16396
  400,
@@ -16136,7 +16401,7 @@ function readRequiredMemoryMatch(body) {
16136
16401
  return oldText;
16137
16402
  }
16138
16403
  function readRequiredMemoryProvider(body) {
16139
- const provider = readString12(body, "provider") ?? readString12(body, "provider_id") ?? readString12(body, "providerId");
16404
+ const provider = readString13(body, "provider") ?? readString13(body, "provider_id") ?? readString13(body, "providerId");
16140
16405
  if (!provider) {
16141
16406
  throw new LinkHttpError(
16142
16407
  400,
@@ -16148,7 +16413,7 @@ function readRequiredMemoryProvider(body) {
16148
16413
  }
16149
16414
  function readMemorySettingsPatch(body) {
16150
16415
  const input = {};
16151
- const mode = readString12(body, "mode");
16416
+ const mode = readString13(body, "mode");
16152
16417
  if (mode) {
16153
16418
  input.mode = mode;
16154
16419
  }
@@ -16160,7 +16425,7 @@ function readMemorySettingsPatch(body) {
16160
16425
  if (bankId !== void 0) {
16161
16426
  input.bankId = bankId;
16162
16427
  }
16163
- const llmProvider = readString12(body, "llm_provider") ?? readString12(body, "llmProvider");
16428
+ const llmProvider = readString13(body, "llm_provider") ?? readString13(body, "llmProvider");
16164
16429
  if (llmProvider) {
16165
16430
  input.llmProvider = llmProvider;
16166
16431
  }
@@ -16188,11 +16453,11 @@ function readMemorySettingsPatch(body) {
16188
16453
  if (autoRetain !== void 0) {
16189
16454
  input.autoRetain = autoRetain;
16190
16455
  }
16191
- const memoryMode = readString12(body, "memory_mode") ?? readString12(body, "memoryMode");
16456
+ const memoryMode = readString13(body, "memory_mode") ?? readString13(body, "memoryMode");
16192
16457
  if (memoryMode) {
16193
16458
  input.memoryMode = memoryMode;
16194
16459
  }
16195
- const recallBudget = readString12(body, "recall_budget") ?? readString12(body, "recallBudget");
16460
+ const recallBudget = readString13(body, "recall_budget") ?? readString13(body, "recallBudget");
16196
16461
  if (recallBudget) {
16197
16462
  input.recallBudget = recallBudget;
16198
16463
  }
@@ -16208,11 +16473,11 @@ function readMemorySettingsPatch(body) {
16208
16473
  if (profileFrequency !== void 0) {
16209
16474
  input.profileFrequency = profileFrequency;
16210
16475
  }
16211
- const captureMode = readString12(body, "capture_mode") ?? readString12(body, "captureMode");
16476
+ const captureMode = readString13(body, "capture_mode") ?? readString13(body, "captureMode");
16212
16477
  if (captureMode) {
16213
16478
  input.captureMode = captureMode;
16214
16479
  }
16215
- const searchMode = readString12(body, "search_mode") ?? readString12(body, "searchMode");
16480
+ const searchMode = readString13(body, "search_mode") ?? readString13(body, "searchMode");
16216
16481
  if (searchMode) {
16217
16482
  input.searchMode = searchMode;
16218
16483
  }
@@ -16246,11 +16511,11 @@ function readMemorySettingsPatch(body) {
16246
16511
  if (aiPeer !== void 0) {
16247
16512
  input.aiPeer = aiPeer;
16248
16513
  }
16249
- const recallMode = readString12(body, "recall_mode") ?? readString12(body, "recallMode");
16514
+ const recallMode = readString13(body, "recall_mode") ?? readString13(body, "recallMode");
16250
16515
  if (recallMode) {
16251
16516
  input.recallMode = recallMode;
16252
16517
  }
16253
- const writeFrequency = readString12(body, "write_frequency") ?? readString12(body, "writeFrequency");
16518
+ const writeFrequency = readString13(body, "write_frequency") ?? readString13(body, "writeFrequency");
16254
16519
  if (writeFrequency) {
16255
16520
  input.writeFrequency = writeFrequency;
16256
16521
  }
@@ -16258,7 +16523,7 @@ function readMemorySettingsPatch(body) {
16258
16523
  if (saveMessages !== void 0) {
16259
16524
  input.saveMessages = saveMessages;
16260
16525
  }
16261
- const sessionStrategy = readString12(body, "session_strategy") ?? readString12(body, "sessionStrategy");
16526
+ const sessionStrategy = readString13(body, "session_strategy") ?? readString13(body, "sessionStrategy");
16262
16527
  if (sessionStrategy) {
16263
16528
  input.sessionStrategy = sessionStrategy;
16264
16529
  }
@@ -16390,7 +16655,7 @@ import {
16390
16655
  copyFile as copyFile4,
16391
16656
  mkdir as mkdir13,
16392
16657
  readFile as readFile14,
16393
- readdir as readdir8,
16658
+ readdir as readdir9,
16394
16659
  rename as rename7,
16395
16660
  writeFile as writeFile6
16396
16661
  } from "fs/promises";
@@ -16485,9 +16750,9 @@ async function findSkillFiles(root) {
16485
16750
  return results.sort((left, right) => left.localeCompare(right));
16486
16751
  }
16487
16752
  async function collectSkillFiles(directory, results) {
16488
- const entries = await readdir8(directory, { withFileTypes: true }).catch(
16753
+ const entries = await readdir9(directory, { withFileTypes: true }).catch(
16489
16754
  (error) => {
16490
- if (isNodeError14(error, "ENOENT")) {
16755
+ if (isNodeError15(error, "ENOENT")) {
16491
16756
  return [];
16492
16757
  }
16493
16758
  throw error;
@@ -16512,7 +16777,7 @@ async function collectSkillFiles(directory, results) {
16512
16777
  async function readSkillMetadata(input) {
16513
16778
  const raw = await readFile14(input.skillFile, "utf8").catch(
16514
16779
  (error) => {
16515
- if (isNodeError14(error, "ENOENT") || isNodeError14(error, "EACCES")) {
16780
+ if (isNodeError15(error, "ENOENT") || isNodeError15(error, "EACCES")) {
16516
16781
  return null;
16517
16782
  }
16518
16783
  throw error;
@@ -16524,13 +16789,13 @@ async function readSkillMetadata(input) {
16524
16789
  const skillDir = path19.dirname(input.skillFile);
16525
16790
  const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
16526
16791
  const name = normalizeSkillName(
16527
- readString14(frontmatter.name) ?? path19.basename(skillDir)
16792
+ readString15(frontmatter.name) ?? path19.basename(skillDir)
16528
16793
  );
16529
16794
  if (!name) {
16530
16795
  return null;
16531
16796
  }
16532
16797
  const description = normalizeDescription(
16533
- readString14(frontmatter.description) ?? firstBodyDescription(body)
16798
+ readString15(frontmatter.description) ?? firstBodyDescription(body)
16534
16799
  );
16535
16800
  const provenance = input.provenance.get(name) ?? {
16536
16801
  source: "local",
@@ -16556,7 +16821,7 @@ function parseSkillDocument(raw) {
16556
16821
  }
16557
16822
  try {
16558
16823
  return {
16559
- frontmatter: toRecord13(YAML5.parse(match[1] ?? "")),
16824
+ frontmatter: toRecord14(YAML5.parse(match[1] ?? "")),
16560
16825
  body: content.slice(match[0].length)
16561
16826
  };
16562
16827
  } catch {
@@ -16589,7 +16854,7 @@ function normalizeDescription(value) {
16589
16854
  }
16590
16855
  async function readDisabledSkillNames(configPath) {
16591
16856
  const raw = await readFile14(configPath, "utf8").catch((error) => {
16592
- if (isNodeError14(error, "ENOENT")) {
16857
+ if (isNodeError15(error, "ENOENT")) {
16593
16858
  return "";
16594
16859
  }
16595
16860
  throw error;
@@ -16597,8 +16862,8 @@ async function readDisabledSkillNames(configPath) {
16597
16862
  if (!raw.trim()) {
16598
16863
  return /* @__PURE__ */ new Set();
16599
16864
  }
16600
- const config = toRecord13(YAML5.parse(raw));
16601
- const skills = toRecord13(config.skills);
16865
+ const config = toRecord14(YAML5.parse(raw));
16866
+ const skills = toRecord14(config.skills);
16602
16867
  return new Set(readStringList3(skills.disabled));
16603
16868
  }
16604
16869
  async function readSkillProvenance(root) {
@@ -16614,7 +16879,7 @@ async function readSkillProvenance(root) {
16614
16879
  async function readBundledSkillNames(root) {
16615
16880
  const raw = await readFile14(path19.join(root, ".bundled_manifest"), "utf8").catch(
16616
16881
  (error) => {
16617
- if (isNodeError14(error, "ENOENT")) {
16882
+ if (isNodeError15(error, "ENOENT")) {
16618
16883
  return "";
16619
16884
  }
16620
16885
  throw error;
@@ -16637,7 +16902,7 @@ async function readBundledSkillNames(root) {
16637
16902
  async function readHubInstalledSkills(root) {
16638
16903
  const raw = await readFile14(path19.join(root, ".hub", "lock.json"), "utf8").catch(
16639
16904
  (error) => {
16640
- if (isNodeError14(error, "ENOENT")) {
16905
+ if (isNodeError15(error, "ENOENT")) {
16641
16906
  return "";
16642
16907
  }
16643
16908
  throw error;
@@ -16648,17 +16913,17 @@ async function readHubInstalledSkills(root) {
16648
16913
  }
16649
16914
  let lock;
16650
16915
  try {
16651
- lock = toRecord13(JSON.parse(raw));
16916
+ lock = toRecord14(JSON.parse(raw));
16652
16917
  } catch {
16653
16918
  return /* @__PURE__ */ new Map();
16654
16919
  }
16655
- const installed = toRecord13(lock.installed);
16920
+ const installed = toRecord14(lock.installed);
16656
16921
  const result = /* @__PURE__ */ new Map();
16657
16922
  for (const [name, rawEntry] of Object.entries(installed)) {
16658
- const entry = toRecord13(rawEntry);
16923
+ const entry = toRecord14(rawEntry);
16659
16924
  result.set(normalizeSkillName(name), {
16660
- source: readString14(entry.source) ?? "hub",
16661
- trust: readString14(entry.trust_level) ?? null
16925
+ source: readString15(entry.source) ?? "hub",
16926
+ trust: readString15(entry.trust_level) ?? null
16662
16927
  });
16663
16928
  }
16664
16929
  return result;
@@ -16709,7 +16974,7 @@ function compareCategoryNames(left, right) {
16709
16974
  async function readHermesConfigDocument2(configPath) {
16710
16975
  const existingRaw = await readFile14(configPath, "utf8").catch(
16711
16976
  (error) => {
16712
- if (isNodeError14(error, "ENOENT")) {
16977
+ if (isNodeError15(error, "ENOENT")) {
16713
16978
  return null;
16714
16979
  }
16715
16980
  throw error;
@@ -16718,7 +16983,7 @@ async function readHermesConfigDocument2(configPath) {
16718
16983
  const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
16719
16984
  return {
16720
16985
  document,
16721
- config: toRecord13(document.toJSON()),
16986
+ config: toRecord14(document.toJSON()),
16722
16987
  existingRaw
16723
16988
  };
16724
16989
  }
@@ -16740,21 +17005,21 @@ function readStringList3(value) {
16740
17005
  }
16741
17006
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
16742
17007
  }
16743
- function readString14(value) {
17008
+ function readString15(value) {
16744
17009
  return typeof value === "string" && value.trim() ? value.trim() : null;
16745
17010
  }
16746
- function toRecord13(value) {
17011
+ function toRecord14(value) {
16747
17012
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
16748
17013
  }
16749
17014
  function ensureRecord3(target, key) {
16750
- const current = toRecord13(target[key]);
17015
+ const current = toRecord14(target[key]);
16751
17016
  if (current === target[key]) {
16752
17017
  return current;
16753
17018
  }
16754
17019
  target[key] = current;
16755
17020
  return current;
16756
17021
  }
16757
- function isNodeError14(error, code) {
17022
+ function isNodeError15(error, code) {
16758
17023
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
16759
17024
  }
16760
17025
 
@@ -17057,7 +17322,7 @@ function registerRunRoutes(router, options) {
17057
17322
  router.post("/api/v1/runs", async (ctx) => {
17058
17323
  await authenticateRequest(ctx, paths);
17059
17324
  const body = await readJsonBody(ctx.req);
17060
- const input = readString12(body, "input");
17325
+ const input = readString13(body, "input");
17061
17326
  if (!input) {
17062
17327
  throw new LinkHttpError(400, "run_input_required", "input is required");
17063
17328
  }
@@ -17065,11 +17330,11 @@ function registerRunRoutes(router, options) {
17065
17330
  ctx.body = await createHermesRun(
17066
17331
  {
17067
17332
  input,
17068
- instructions: readString12(body, "instructions") ?? void 0,
17333
+ instructions: readString13(body, "instructions") ?? void 0,
17069
17334
  conversation_history: readConversationHistory(
17070
17335
  body.conversation_history ?? body.conversationHistory
17071
17336
  ),
17072
- session_id: readString12(body, "session_id") ?? readString12(body, "sessionId") ?? void 0
17337
+ session_id: readString13(body, "session_id") ?? readString13(body, "sessionId") ?? void 0
17073
17338
  },
17074
17339
  { logger, profileName: readOptionalProfileName(body) }
17075
17340
  );
@@ -17462,24 +17727,24 @@ async function readRemoteRelease(options, now) {
17462
17727
  }
17463
17728
  }
17464
17729
  function normalizeServerReleaseSnapshot(payload) {
17465
- const snapshot = toRecord14(payload);
17730
+ const snapshot = toRecord15(payload);
17466
17731
  const remote = toNullableRecord(snapshot.remote);
17467
17732
  return {
17468
17733
  remote: remote ? normalizeServerRelease(remote) : null,
17469
- cacheState: readString15(snapshot, "cache_state") ?? readString15(snapshot, "cacheState"),
17470
- issue: readString15(snapshot, "issue")
17734
+ cacheState: readString16(snapshot, "cache_state") ?? readString16(snapshot, "cacheState"),
17735
+ issue: readString16(snapshot, "issue")
17471
17736
  };
17472
17737
  }
17473
17738
  function normalizeServerRelease(payload) {
17474
- const tag = readString15(payload, "tag");
17475
- const name = readString15(payload, "name");
17739
+ const tag = readString16(payload, "tag");
17740
+ const name = readString16(payload, "name");
17476
17741
  return {
17477
- version: readString15(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
17742
+ version: readString16(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
17478
17743
  tag,
17479
17744
  name,
17480
- releaseUrl: readString15(payload, "releaseUrl") ?? readString15(payload, "release_url"),
17481
- publishedAt: readString15(payload, "publishedAt") ?? readString15(payload, "published_at"),
17482
- fetchedAt: readString15(payload, "fetchedAt") ?? readString15(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
17745
+ releaseUrl: readString16(payload, "releaseUrl") ?? readString16(payload, "release_url"),
17746
+ publishedAt: readString16(payload, "publishedAt") ?? readString16(payload, "published_at"),
17747
+ fetchedAt: readString16(payload, "fetchedAt") ?? readString16(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
17483
17748
  };
17484
17749
  }
17485
17750
  async function readReleaseCache(paths) {
@@ -17551,7 +17816,7 @@ function compareSemver2(left, right) {
17551
17816
  }
17552
17817
  return 0;
17553
17818
  }
17554
- function toRecord14(value) {
17819
+ function toRecord15(value) {
17555
17820
  return typeof value === "object" && value !== null ? value : {};
17556
17821
  }
17557
17822
  function toNullableRecord(value) {
@@ -17597,7 +17862,7 @@ function isRecentRunningState2(state) {
17597
17862
  const startedAt = Date.parse(state.started_at);
17598
17863
  return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
17599
17864
  }
17600
- function readString15(payload, key) {
17865
+ function readString16(payload, key) {
17601
17866
  const value = payload[key];
17602
17867
  return typeof value === "string" && value.trim() ? value.trim() : null;
17603
17868
  }
@@ -19107,21 +19372,21 @@ async function readRemoteLinkPolicy(options) {
19107
19372
  }
19108
19373
  }
19109
19374
  function normalizeServerSnapshot(payload) {
19110
- const snapshot = toRecord15(payload);
19375
+ const snapshot = toRecord16(payload);
19111
19376
  const policy = toNullableRecord2(snapshot.policy);
19112
19377
  if (!policy) {
19113
19378
  return {
19114
19379
  remote: null,
19115
- issue: readString16(snapshot, "issue")
19380
+ issue: readString17(snapshot, "issue")
19116
19381
  };
19117
19382
  }
19118
19383
  const release = toNullableRecord2(snapshot.release);
19119
- const currentVersion = readString16(policy, "current_version") ?? readString16(policy, "currentVersion");
19120
- const minSafeVersion = readString16(policy, "min_safe_version") ?? readString16(policy, "minSafeVersion");
19384
+ const currentVersion = readString17(policy, "current_version") ?? readString17(policy, "currentVersion");
19385
+ const minSafeVersion = readString17(policy, "min_safe_version") ?? readString17(policy, "minSafeVersion");
19121
19386
  if (!currentVersion) {
19122
19387
  return {
19123
19388
  remote: null,
19124
- issue: readString16(snapshot, "issue")
19389
+ issue: readString17(snapshot, "issue")
19125
19390
  };
19126
19391
  }
19127
19392
  return {
@@ -19129,10 +19394,10 @@ function normalizeServerSnapshot(payload) {
19129
19394
  current_version: currentVersion,
19130
19395
  min_safe_version: minSafeVersion,
19131
19396
  target_version: currentVersion,
19132
- release_url: release ? readString16(release, "release_url") ?? readString16(release, "releaseUrl") : null,
19133
- published_at: release ? readString16(release, "published_at") ?? readString16(release, "publishedAt") : null
19397
+ release_url: release ? readString17(release, "release_url") ?? readString17(release, "releaseUrl") : null,
19398
+ published_at: release ? readString17(release, "published_at") ?? readString17(release, "publishedAt") : null
19134
19399
  },
19135
- issue: readString16(snapshot, "issue")
19400
+ issue: readString17(snapshot, "issue")
19136
19401
  };
19137
19402
  }
19138
19403
  async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
@@ -19246,13 +19511,13 @@ function isProcessAlive4(pid) {
19246
19511
  return false;
19247
19512
  }
19248
19513
  }
19249
- function toRecord15(value) {
19514
+ function toRecord16(value) {
19250
19515
  return typeof value === "object" && value !== null ? value : {};
19251
19516
  }
19252
19517
  function toNullableRecord2(value) {
19253
19518
  return typeof value === "object" && value !== null ? value : null;
19254
19519
  }
19255
- function readString16(payload, key) {
19520
+ function readString17(payload, key) {
19256
19521
  const value = payload[key];
19257
19522
  return typeof value === "string" && value.trim() ? value.trim() : null;
19258
19523
  }
@@ -19681,8 +19946,8 @@ function registerSystemRoutes(router, options) {
19681
19946
  });
19682
19947
  router.post("/api/v1/pairing/claim", async (ctx) => {
19683
19948
  const body = await readJsonBody(ctx.req);
19684
- const sessionId = readString12(body, "session_id") ?? readString12(body, "sessionId");
19685
- const claimToken = readString12(body, "claim_token") ?? readString12(body, "claimToken");
19949
+ const sessionId = readString13(body, "session_id") ?? readString13(body, "sessionId");
19950
+ const claimToken = readString13(body, "claim_token") ?? readString13(body, "claimToken");
19686
19951
  if (!sessionId || !claimToken) {
19687
19952
  throw new LinkHttpError(
19688
19953
  400,
@@ -19693,10 +19958,10 @@ function registerSystemRoutes(router, options) {
19693
19958
  const claimed = await claimPairing({
19694
19959
  sessionId,
19695
19960
  claimToken,
19696
- deviceLabel: readString12(body, "device_label") ?? readString12(body, "deviceLabel") ?? "HermesPilot App",
19697
- devicePlatform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform") ?? "unknown",
19698
- deviceModel: readString12(body, "device_model") ?? readString12(body, "deviceModel"),
19699
- appInstanceId: readString12(body, "app_instance_id") ?? readString12(body, "appInstanceId"),
19961
+ deviceLabel: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
19962
+ devicePlatform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
19963
+ deviceModel: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
19964
+ appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
19700
19965
  paths
19701
19966
  });
19702
19967
  ctx.body = claimed;
@@ -19771,9 +20036,9 @@ function registerSystemRoutes(router, options) {
19771
20036
  const body = await readJsonBody(ctx.req);
19772
20037
  const session = await createDeviceSession(
19773
20038
  {
19774
- label: readString12(body, "device_label") ?? readString12(body, "deviceLabel") ?? "HermesPilot App",
19775
- platform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform") ?? "unknown",
19776
- model: readString12(body, "device_model") ?? readString12(body, "deviceModel"),
20039
+ label: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
20040
+ platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
20041
+ model: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
19777
20042
  appInstanceId: auth.appInstanceId
19778
20043
  },
19779
20044
  paths
@@ -19802,7 +20067,7 @@ function registerSystemRoutes(router, options) {
19802
20067
  });
19803
20068
  router.post("/api/v1/auth/refresh", async (ctx) => {
19804
20069
  const body = await readJsonBody(ctx.req);
19805
- const refreshToken = readString12(body, "refresh_token") ?? readString12(body, "refreshToken");
20070
+ const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
19806
20071
  if (!refreshToken) {
19807
20072
  throw new LinkHttpError(
19808
20073
  400,
@@ -19813,10 +20078,10 @@ function registerSystemRoutes(router, options) {
19813
20078
  const session = await refreshDeviceSession(
19814
20079
  refreshToken,
19815
20080
  {
19816
- appInstanceId: readString12(body, "app_instance_id") ?? readString12(body, "appInstanceId"),
19817
- label: readString12(body, "device_label") ?? readString12(body, "deviceLabel"),
19818
- platform: readString12(body, "device_platform") ?? readString12(body, "devicePlatform"),
19819
- model: readString12(body, "device_model") ?? readString12(body, "deviceModel")
20081
+ appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
20082
+ label: readString13(body, "device_label") ?? readString13(body, "deviceLabel"),
20083
+ platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform"),
20084
+ model: readString13(body, "device_model") ?? readString13(body, "deviceModel")
19820
20085
  },
19821
20086
  paths
19822
20087
  );
@@ -19835,7 +20100,7 @@ function registerSystemRoutes(router, options) {
19835
20100
  });
19836
20101
  router.post("/api/v1/auth/logout", async (ctx) => {
19837
20102
  const body = await readJsonBody(ctx.req);
19838
- const refreshToken = readString12(body, "refresh_token") ?? readString12(body, "refreshToken");
20103
+ const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
19839
20104
  if (refreshToken) {
19840
20105
  await revokeDeviceRefreshToken(refreshToken, paths);
19841
20106
  }
@@ -20060,7 +20325,7 @@ function registerSystemRoutes(router, options) {
20060
20325
  router.patch("/api/v1/devices/:deviceId", async (ctx) => {
20061
20326
  const auth = await authenticateRequest(ctx, paths);
20062
20327
  const body = await readJsonBody(ctx.req);
20063
- const label = readString12(body, "label") ?? readString12(body, "device_label");
20328
+ const label = readString13(body, "label") ?? readString13(body, "device_label");
20064
20329
  if (!label) {
20065
20330
  throw new LinkHttpError(
20066
20331
  400,
@@ -20104,7 +20369,7 @@ function isActiveCronJob(job) {
20104
20369
  if (!enabled) {
20105
20370
  return false;
20106
20371
  }
20107
- const state = readString12(job, "state")?.toLowerCase();
20372
+ const state = readString13(job, "state")?.toLowerCase();
20108
20373
  return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
20109
20374
  }
20110
20375
  function filterLogsWithinHours(logs, hours, now = Date.now()) {
@@ -20198,7 +20463,7 @@ function registerLinkUpdateRoutes(router, options) {
20198
20463
  ctx.body = await startLinkUpdate({
20199
20464
  paths,
20200
20465
  logger,
20201
- targetVersion: readString12(body, "target_version") ?? readString12(body, "targetVersion")
20466
+ targetVersion: readString13(body, "target_version") ?? readString13(body, "targetVersion")
20202
20467
  });
20203
20468
  });
20204
20469
  router.get("/api/v1/link/update/events", async (ctx) => {
@@ -20232,7 +20497,7 @@ import QRCode from "qrcode";
20232
20497
  function registerPairingRoutes(router, options) {
20233
20498
  const { paths } = options;
20234
20499
  router.get("/pair", async (ctx) => {
20235
- const sessionId = readString12(ctx.query, "session_id");
20500
+ const sessionId = readString13(ctx.query, "session_id");
20236
20501
  if (!sessionId) {
20237
20502
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
20238
20503
  }
@@ -20257,7 +20522,7 @@ function registerPairingRoutes(router, options) {
20257
20522
  ctx.body = page;
20258
20523
  });
20259
20524
  router.get("/api/v1/pairing/session", async (ctx) => {
20260
- const sessionId = readString12(ctx.query, "session_id");
20525
+ const sessionId = readString13(ctx.query, "session_id");
20261
20526
  if (!sessionId) {
20262
20527
  throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
20263
20528
  }
@@ -20656,6 +20921,37 @@ function formatDate(value) {
20656
20921
  return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
20657
20922
  }
20658
20923
 
20924
+ // src/http/routes/internal.ts
20925
+ function registerInternalRoutes(router, options) {
20926
+ router.post("/internal/deliver", async (ctx) => {
20927
+ assertLoopbackRequest(ctx.req);
20928
+ const body = await readJsonBody(ctx.req);
20929
+ const stagingDir = readString13(body, "staging_dir") ?? readString13(body, "stagingDir");
20930
+ if (!stagingDir) {
20931
+ throw new LinkHttpError(
20932
+ 400,
20933
+ "delivery_staging_required",
20934
+ "delivery staging directory is required"
20935
+ );
20936
+ }
20937
+ ctx.body = {
20938
+ ok: true,
20939
+ ...await options.conversations.deliverStagedFiles(stagingDir)
20940
+ };
20941
+ });
20942
+ }
20943
+ function assertLoopbackRequest(request) {
20944
+ const address = request.socket.remoteAddress;
20945
+ if (address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1") {
20946
+ return;
20947
+ }
20948
+ throw new LinkHttpError(
20949
+ 403,
20950
+ "internal_route_forbidden",
20951
+ "internal route is only available on loopback"
20952
+ );
20953
+ }
20954
+
20659
20955
  // src/http/app.ts
20660
20956
  async function createApp(options = {}) {
20661
20957
  const paths = options.paths ?? resolveRuntimePaths();
@@ -20685,6 +20981,7 @@ async function createApp(options = {}) {
20685
20981
  logger,
20686
20982
  onPairingClaimed: options.onPairingClaimed
20687
20983
  });
20984
+ registerInternalRoutes(router, { conversations });
20688
20985
  registerPairingRoutes(router, { paths });
20689
20986
  registerHermesUpdateRoutes(router, { paths, logger });
20690
20987
  registerLinkUpdateRoutes(router, { paths, logger });
@@ -20714,11 +21011,13 @@ export {
20714
21011
  ensureHermesApiServerConfig,
20715
21012
  LinkHttpError,
20716
21013
  resolveRuntimePaths,
21014
+ createFileLogger,
20717
21015
  getLinkLogFile,
20718
21016
  ensureHermesApiServerAvailable,
20719
21017
  loadConfig,
20720
21018
  saveConfig,
20721
21019
  normalizeLanHost,
21020
+ ConversationService,
20722
21021
  loadIdentity,
20723
21022
  ensureIdentity,
20724
21023
  getIdentityStatus,