@hermespilot/link 0.3.2 → 0.3.3

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.3";
3728
3728
  var LINK_COMMAND = "hermeslink";
3729
3729
  var LINK_DEFAULT_PORT = 52379;
3730
3730
  var LINK_RUNTIME_DIR_NAME = ".hermeslink";
@@ -12851,7 +12851,7 @@ function registerConversationRoutes(router, options) {
12851
12851
  ctx.set("cache-control", "private, max-age=86400");
12852
12852
  ctx.set(
12853
12853
  "content-disposition",
12854
- `inline; filename="${blob.filename.replaceAll('"', "")}"`
12854
+ contentDispositionInline(blob.filename)
12855
12855
  );
12856
12856
  ctx.body = blob.bytes;
12857
12857
  }
@@ -12870,6 +12870,34 @@ function registerConversationRoutes(router, options) {
12870
12870
  }
12871
12871
  );
12872
12872
  }
12873
+ function contentDispositionInline(filename) {
12874
+ const fallback = asciiFilenameFallback(filename);
12875
+ return `inline; filename="${fallback}"; filename*=UTF-8''${encodeRfc5987Value(filename)}`;
12876
+ }
12877
+ function asciiFilenameFallback(filename) {
12878
+ const basename = filename.trim().split(/[\\/]/u).pop()?.trim() ?? "";
12879
+ const extension = safeAsciiExtension(basename);
12880
+ const stem = extension ? basename.slice(0, -extension.length) : basename;
12881
+ const asciiStem = stem.replace(/[^\x20-\x7E]/gu, "_").replace(/["\\]/gu, "_").replace(/[^A-Za-z0-9._ -]/gu, "_").replace(/_+/gu, "_").trim();
12882
+ if (/[A-Za-z0-9]/u.test(asciiStem)) {
12883
+ return `${asciiStem.slice(0, 120)}${extension}`;
12884
+ }
12885
+ return `attachment${extension}`;
12886
+ }
12887
+ function safeAsciiExtension(filename) {
12888
+ const dotIndex = filename.lastIndexOf(".");
12889
+ if (dotIndex < 0 || dotIndex === filename.length - 1) {
12890
+ return "";
12891
+ }
12892
+ const extension = filename.slice(dotIndex).toLowerCase();
12893
+ return /^\.[a-z0-9]{1,12}$/u.test(extension) ? extension : "";
12894
+ }
12895
+ function encodeRfc5987Value(value) {
12896
+ return encodeURIComponent(value).replace(
12897
+ /['()*]/gu,
12898
+ (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
12899
+ );
12900
+ }
12873
12901
  function isConversationNotificationEvent(event) {
12874
12902
  const type = event.type.toLowerCase();
12875
12903
  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");
@@ -14650,8 +14678,8 @@ import {
14650
14678
  import path18 from "path";
14651
14679
  import YAML4 from "yaml";
14652
14680
  var ENTRY_DELIMITER = "\n\xA7\n";
14653
- var MEMORY_LIMIT = 2200;
14654
- var USER_LIMIT = 1375;
14681
+ var DEFAULT_MEMORY_LIMIT = 2200;
14682
+ var DEFAULT_USER_LIMIT = 1375;
14655
14683
  var CUSTOM_PROVIDER_CARD_ID = "__custom__";
14656
14684
  var CUSTOM_PROVIDER_REGISTRY_FILE = "memory-providers.json";
14657
14685
  var HINDSIGHT_DEFAULT_API_URL = "https://api.hindsight.vectorize.io";
@@ -14734,9 +14762,10 @@ var HermesMemoryError = class extends Error {
14734
14762
  };
14735
14763
  async function readHermesProfileMemory(profileName = "default") {
14736
14764
  const memoryDir = resolveMemoryDir(profileName);
14765
+ const limits = await readMemoryLimits(profileName);
14737
14766
  const [memoryStore, userStore, settings] = await Promise.all([
14738
- readMemoryStore(profileName, "memory"),
14739
- readMemoryStore(profileName, "user"),
14767
+ readMemoryStore(profileName, "memory", limits),
14768
+ readMemoryStore(profileName, "user", limits),
14740
14769
  readMemorySettings(profileName)
14741
14770
  ]);
14742
14771
  return {
@@ -14754,9 +14783,7 @@ async function addHermesMemoryEntry(profileName, target, content) {
14754
14783
  if (entries.includes(normalized)) {
14755
14784
  return entries;
14756
14785
  }
14757
- const next = [...entries, normalized];
14758
- assertWithinLimit(target, next);
14759
- return next;
14786
+ return [...entries, normalized];
14760
14787
  });
14761
14788
  return readHermesProfileMemory(profileName);
14762
14789
  }
@@ -14767,7 +14794,6 @@ async function replaceHermesMemoryEntry(profileName, target, oldText, content) {
14767
14794
  const index = findSingleMatch(entries, needle);
14768
14795
  const next = [...entries];
14769
14796
  next[index] = normalized;
14770
- assertWithinLimit(target, next);
14771
14797
  return next;
14772
14798
  });
14773
14799
  return readHermesProfileMemory(profileName);
@@ -15040,7 +15066,7 @@ async function patchHermesMemoryProvider(profileName, provider) {
15040
15066
  function resolveMemoryDir(profileName) {
15041
15067
  return path18.join(resolveHermesProfileDir(profileName), "memories");
15042
15068
  }
15043
- async function readMemoryStore(profileName, target) {
15069
+ async function readMemoryStore(profileName, target, limits) {
15044
15070
  const filePath = memoryFilePath(profileName, target);
15045
15071
  const entries = await readMemoryEntries(filePath);
15046
15072
  const fileStat = await stat12(filePath).catch((error) => {
@@ -15050,7 +15076,7 @@ async function readMemoryStore(profileName, target) {
15050
15076
  throw error;
15051
15077
  });
15052
15078
  const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
15053
- const limit = target === "user" ? USER_LIMIT : MEMORY_LIMIT;
15079
+ const limit = memoryLimitForTarget(limits, target);
15054
15080
  return {
15055
15081
  target,
15056
15082
  label: target === "user" ? "\u5173\u4E8E\u7528\u6237" : "Agent \u7B14\u8BB0",
@@ -15091,7 +15117,7 @@ async function mutateMemoryEntries(profileName, target, mutate) {
15091
15117
  await writeMemoryEntries(profileName, target, mutate([...new Set(current)]));
15092
15118
  }
15093
15119
  async function writeMemoryEntries(profileName, target, entries) {
15094
- assertWithinLimit(target, entries);
15120
+ assertWithinLimit(target, entries, await readMemoryLimits(profileName));
15095
15121
  const filePath = memoryFilePath(profileName, target);
15096
15122
  const dir = path18.dirname(filePath);
15097
15123
  await mkdir12(dir, { recursive: true, mode: 448 });
@@ -15904,8 +15930,28 @@ function selectSetting(key, label, value, options, editable = true) {
15904
15930
  const stringValue = readString13(value) ?? options[0] ?? null;
15905
15931
  return { key, label, value: stringValue, editable, kind: "select", options };
15906
15932
  }
15907
- function assertWithinLimit(target, entries) {
15908
- const limit = target === "user" ? USER_LIMIT : MEMORY_LIMIT;
15933
+ async function readMemoryLimits(profileName) {
15934
+ const raw = await readFile13(
15935
+ resolveHermesConfigPath(profileName),
15936
+ "utf8"
15937
+ ).catch((error) => {
15938
+ if (isNodeError13(error, "ENOENT")) {
15939
+ return "";
15940
+ }
15941
+ throw error;
15942
+ });
15943
+ const config = raw ? toRecord12(YAML4.parse(raw)) : {};
15944
+ const memory = toRecord12(config.memory);
15945
+ return {
15946
+ memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
15947
+ user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
15948
+ };
15949
+ }
15950
+ function memoryLimitForTarget(limits, target) {
15951
+ return target === "user" ? limits.user : limits.memory;
15952
+ }
15953
+ function assertWithinLimit(target, entries, limits) {
15954
+ const limit = memoryLimitForTarget(limits, target);
15909
15955
  const chars = entries.length ? entries.join(ENTRY_DELIMITER).length : 0;
15910
15956
  if (chars > limit) {
15911
15957
  throw new HermesMemoryError(
@@ -15957,6 +16003,10 @@ function toRecord12(value) {
15957
16003
  function readString13(value) {
15958
16004
  return typeof value === "string" && value.trim() ? value.trim() : null;
15959
16005
  }
16006
+ function readPositiveInteger3(value) {
16007
+ const numberValue = typeof value === "number" ? value : typeof value === "string" ? Number(value.trim()) : NaN;
16008
+ return Number.isFinite(numberValue) && numberValue > 0 ? Math.floor(numberValue) : void 0;
16009
+ }
15960
16010
  function readBoolean2(value) {
15961
16011
  if (typeof value === "boolean") {
15962
16012
  return value;
package/dist/cli/index.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  startDaemonProcess,
31
31
  startLinkService,
32
32
  stopDaemonProcess
33
- } from "../chunk-PEHQ3AED.js";
33
+ } from "../chunk-IJTQ6YVR.js";
34
34
 
35
35
  // src/cli/index.ts
36
36
  import { Command } from "commander";
package/dist/http/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApp
3
- } from "../chunk-PEHQ3AED.js";
3
+ } from "../chunk-IJTQ6YVR.js";
4
4
  export {
5
5
  createApp
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hermespilot/link",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "private": false,
5
5
  "description": "Hermes Link companion service and CLI for connecting hermes-agent through HermesPilot",
6
6
  "license": "MIT",