@agentmemory/agentmemory 0.9.21 → 0.9.23

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.
Files changed (95) hide show
  1. package/AGENTS.md +7 -2
  2. package/README.md +288 -33
  3. package/dist/cli.d.mts +5 -1
  4. package/dist/cli.d.mts.map +1 -0
  5. package/dist/cli.mjs +128 -703
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/connect-Cf9bmBqO.mjs +1020 -0
  8. package/dist/connect-Cf9bmBqO.mjs.map +1 -0
  9. package/dist/hooks/notification.mjs +46 -21
  10. package/dist/hooks/notification.mjs.map +1 -1
  11. package/dist/hooks/post-tool-failure.mjs +47 -21
  12. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  13. package/dist/hooks/post-tool-use.mjs +57 -22
  14. package/dist/hooks/post-tool-use.mjs.map +1 -1
  15. package/dist/hooks/pre-compact.mjs +26 -2
  16. package/dist/hooks/pre-compact.mjs.map +1 -1
  17. package/dist/hooks/pre-tool-use.mjs +19 -12
  18. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  19. package/dist/hooks/prompt-submit.mjs +39 -16
  20. package/dist/hooks/prompt-submit.mjs.map +1 -1
  21. package/dist/hooks/session-end.mjs +26 -33
  22. package/dist/hooks/session-end.mjs.map +1 -1
  23. package/dist/hooks/session-start.mjs +28 -3
  24. package/dist/hooks/session-start.mjs.map +1 -1
  25. package/dist/hooks/stop.mjs +14 -9
  26. package/dist/hooks/stop.mjs.map +1 -1
  27. package/dist/hooks/subagent-start.mjs +31 -4
  28. package/dist/hooks/subagent-start.mjs.map +1 -1
  29. package/dist/hooks/subagent-stop.mjs +45 -20
  30. package/dist/hooks/subagent-stop.mjs.map +1 -1
  31. package/dist/hooks/task-completed.mjs +44 -21
  32. package/dist/hooks/task-completed.mjs.map +1 -1
  33. package/dist/iii-config.docker.yaml +3 -2
  34. package/dist/iii-config.yaml +11 -2
  35. package/dist/{image-refs-R3tin9MR.mjs → image-refs-CJS5B9Gq.mjs} +2 -2
  36. package/dist/{image-refs-R3tin9MR.mjs.map → image-refs-CJS5B9Gq.mjs.map} +1 -1
  37. package/dist/{image-store-DyrKZKqZ.mjs → image-store-CdE0amb1.mjs} +1 -1
  38. package/dist/index.mjs +866 -380
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/logger-xlVlvCWX.mjs +43 -0
  41. package/dist/logger-xlVlvCWX.mjs.map +1 -0
  42. package/dist/schema-BkALl7Z_.mjs +74 -0
  43. package/dist/schema-BkALl7Z_.mjs.map +1 -0
  44. package/dist/{src-D5arboxc.mjs → src-DvS3bhMe.mjs} +844 -395
  45. package/dist/src-DvS3bhMe.mjs.map +1 -0
  46. package/dist/{standalone-C7BgzzIN.mjs → standalone-DHQcPX_g.mjs} +107 -14
  47. package/dist/standalone-DHQcPX_g.mjs.map +1 -0
  48. package/dist/standalone.d.mts.map +1 -1
  49. package/dist/standalone.mjs +108 -12
  50. package/dist/standalone.mjs.map +1 -1
  51. package/dist/{tools-registry-CRTWUFw9.mjs → tools-registry-DJizX9Az.mjs} +51 -12
  52. package/dist/tools-registry-DJizX9Az.mjs.map +1 -0
  53. package/dist/version-BPfyI4Kc.mjs +6 -0
  54. package/dist/version-BPfyI4Kc.mjs.map +1 -0
  55. package/dist/viewer/index.html +85 -10
  56. package/iii-config.docker.yaml +3 -2
  57. package/iii-config.yaml +11 -2
  58. package/package.json +6 -4
  59. package/plugin/.claude-plugin/plugin.json +2 -2
  60. package/plugin/.codex-plugin/plugin.json +2 -2
  61. package/plugin/.mcp.copilot.json +15 -0
  62. package/plugin/.mcp.json +3 -2
  63. package/plugin/hooks/hooks.copilot.json +72 -0
  64. package/plugin/opencode/agentmemory-capture.ts +34 -9
  65. package/plugin/plugin.json +15 -0
  66. package/plugin/scripts/diagnostics.d.mts +17 -0
  67. package/plugin/scripts/diagnostics.d.mts.map +1 -0
  68. package/plugin/scripts/diagnostics.mjs.map +1 -0
  69. package/plugin/scripts/notification.mjs +46 -21
  70. package/plugin/scripts/notification.mjs.map +1 -1
  71. package/plugin/scripts/post-tool-failure.mjs +47 -21
  72. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  73. package/plugin/scripts/post-tool-use.mjs +57 -22
  74. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  75. package/plugin/scripts/pre-compact.mjs +26 -2
  76. package/plugin/scripts/pre-compact.mjs.map +1 -1
  77. package/plugin/scripts/pre-tool-use.mjs +19 -12
  78. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  79. package/plugin/scripts/prompt-submit.mjs +39 -16
  80. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  81. package/plugin/scripts/session-end.mjs +26 -33
  82. package/plugin/scripts/session-end.mjs.map +1 -1
  83. package/plugin/scripts/session-start.mjs +28 -3
  84. package/plugin/scripts/session-start.mjs.map +1 -1
  85. package/plugin/scripts/stop.mjs +14 -9
  86. package/plugin/scripts/stop.mjs.map +1 -1
  87. package/plugin/scripts/subagent-start.mjs +31 -4
  88. package/plugin/scripts/subagent-start.mjs.map +1 -1
  89. package/plugin/scripts/subagent-stop.mjs +45 -20
  90. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  91. package/plugin/scripts/task-completed.mjs +44 -21
  92. package/plugin/scripts/task-completed.mjs.map +1 -1
  93. package/dist/src-D5arboxc.mjs.map +0 -1
  94. package/dist/standalone-C7BgzzIN.mjs.map +0 -1
  95. package/dist/tools-registry-CRTWUFw9.mjs.map +0 -1
@@ -1,4 +1,28 @@
1
1
  #!/usr/bin/env node
2
+ import { execSync } from "node:child_process";
3
+ import { basename } from "node:path";
4
+
5
+ //#region src/hooks/_project.ts
6
+ function resolveProject(cwd) {
7
+ const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
8
+ if (explicit && explicit.trim()) return explicit.trim();
9
+ const dir = cwd && cwd.trim() ? cwd : process.cwd();
10
+ try {
11
+ const top = execSync("git rev-parse --show-toplevel", {
12
+ cwd: dir,
13
+ stdio: [
14
+ "ignore",
15
+ "pipe",
16
+ "ignore"
17
+ ],
18
+ timeout: 500
19
+ }).toString().trim();
20
+ if (top) return basename(top);
21
+ } catch {}
22
+ return basename(dir);
23
+ }
24
+
25
+ //#endregion
2
26
  //#region src/hooks/task-completed.ts
3
27
  function isSdkChildContext(payload) {
4
28
  if (process.env["AGENTMEMORY_SDK_CHILD"] === "1") return true;
@@ -23,27 +47,26 @@ async function main() {
23
47
  }
24
48
  if (isSdkChildContext(data)) return;
25
49
  const sessionId = data.session_id || "unknown";
26
- try {
27
- await fetch(`${REST_URL}/agentmemory/observe`, {
28
- method: "POST",
29
- headers: authHeaders(),
30
- body: JSON.stringify({
31
- hookType: "task_completed",
32
- sessionId,
33
- project: data.cwd || process.cwd(),
34
- cwd: data.cwd || process.cwd(),
35
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
36
- data: {
37
- task_id: data.task_id,
38
- task_subject: data.task_subject,
39
- task_description: typeof data.task_description === "string" ? data.task_description.slice(0, 2e3) : "",
40
- teammate_name: data.teammate_name,
41
- team_name: data.team_name
42
- }
43
- }),
44
- signal: AbortSignal.timeout(2e3)
45
- });
46
- } catch {}
50
+ fetch(`${REST_URL}/agentmemory/observe`, {
51
+ method: "POST",
52
+ headers: authHeaders(),
53
+ body: JSON.stringify({
54
+ hookType: "task_completed",
55
+ sessionId,
56
+ project: resolveProject(data.cwd),
57
+ cwd: data.cwd || process.cwd(),
58
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
59
+ data: {
60
+ task_id: data.task_id,
61
+ task_subject: data.task_subject,
62
+ task_description: typeof data.task_description === "string" ? data.task_description.slice(0, 2e3) : "",
63
+ teammate_name: data.teammate_name,
64
+ team_name: data.team_name
65
+ }
66
+ }),
67
+ signal: AbortSignal.timeout(2e3)
68
+ }).catch(() => {});
69
+ setTimeout(() => process.exit(0), 500).unref();
47
70
  }
48
71
  main();
49
72
 
@@ -1 +1 @@
1
- {"version":3,"file":"task-completed.mjs","names":[],"sources":["../../src/hooks/task-completed.ts"],"sourcesContent":["#!/usr/bin/env node\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) return;\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"task_completed\",\n sessionId,\n project: data.cwd || process.cwd(),\n cwd: data.cwd || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n task_id: data.task_id,\n task_subject: data.task_subject,\n task_description: typeof data.task_description === \"string\"\n ? data.task_description.slice(0, 2000)\n : \"\",\n teammate_name: data.teammate_name,\n team_name: data.team_name,\n },\n }),\n signal: AbortSignal.timeout(2000),\n });\n } catch {\n // fire and forget\n }\n}\n\nmain();\n"],"mappings":";;AAEA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,uBAAuB;GAC7C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB,UAAU;IACV;IACA,SAAS,KAAK,OAAO,QAAQ,KAAK;IAClC,KAAK,KAAK,OAAO,QAAQ,KAAK;IAC9B,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,MAAM;KACJ,SAAS,KAAK;KACd,cAAc,KAAK;KACnB,kBAAkB,OAAO,KAAK,qBAAqB,WAC/C,KAAK,iBAAiB,MAAM,GAAG,IAAK,GACpC;KACJ,eAAe,KAAK;KACpB,WAAW,KAAK;KACjB;IACF,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;SACI;;AAKV,MAAM"}
1
+ {"version":3,"file":"task-completed.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/task-completed.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n","#!/usr/bin/env node\nimport { resolveProject } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) return;\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"task_completed\",\n sessionId,\n project: resolveProject(data.cwd as string | undefined),\n cwd: (data.cwd as string | undefined) || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n task_id: data.task_id,\n task_subject: data.task_subject,\n task_description: typeof data.task_description === \"string\"\n ? data.task_description.slice(0, 2000)\n : \"\",\n teammate_name: data.teammate_name,\n team_name: data.team_name,\n },\n }),\n signal: AbortSignal.timeout(2000),\n }).catch(() => {});\n setTimeout(() => process.exit(0), 500).unref();\n}\n\nmain();\n"],"mappings":";;;;;AAIA,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;;;;ACftB,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAa,KAAK,cAAyB;AAEjD,OAAM,GAAG,SAAS,uBAAuB;EACvC,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU;GACnB,UAAU;GACV;GACA,SAAS,eAAe,KAAK,IAA0B;GACvD,KAAM,KAAK,OAA8B,QAAQ,KAAK;GACtD,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,MAAM;IACJ,SAAS,KAAK;IACd,cAAc,KAAK;IACnB,kBAAkB,OAAO,KAAK,qBAAqB,WAC/C,KAAK,iBAAiB,MAAM,GAAG,IAAK,GACpC;IACJ,eAAe,KAAK;IACpB,WAAW,KAAK;IACjB;GACF,CAAC;EACF,QAAQ,YAAY,QAAQ,IAAK;EAClC,CAAC,CAAC,YAAY,GAAG;AAClB,kBAAiB,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO;;AAGhD,MAAM"}
@@ -40,10 +40,11 @@ workers:
40
40
  enabled: true
41
41
  service_name: agentmemory
42
42
  exporter: memory
43
- sampling_ratio: 1.0
43
+ # See iii-config.yaml for the rationale on 0.1 / console-off (#519).
44
+ sampling_ratio: 0.1
44
45
  metrics_enabled: true
45
46
  logs_enabled: true
46
- logs_console_output: true
47
+ logs_console_output: false
47
48
  - name: iii-exec
48
49
  config:
49
50
  watch:
@@ -40,10 +40,19 @@ workers:
40
40
  enabled: true
41
41
  service_name: agentmemory
42
42
  exporter: memory
43
- sampling_ratio: 1.0
43
+ # 0.1 instead of 1.0: at full sampling under sustained load the
44
+ # log subscriber falls behind, the worker emits a "Log trigger
45
+ # subscriber lagged" WARN, and that WARN re-enters the same
46
+ # stream the subscriber is failing to drain — a positive
47
+ # feedback loop that wrote 137 GB to daemon.log.new on one user
48
+ # in a few days (#519).
49
+ sampling_ratio: 0.1
44
50
  metrics_enabled: true
45
51
  logs_enabled: true
46
- logs_console_output: true
52
+ # Console output is off by default; the feedback path needs the
53
+ # console layer to amplify. Re-enable per-session via env if you
54
+ # need verbose tracing for debugging.
55
+ logs_console_output: false
47
56
  - name: iii-exec
48
57
  config:
49
58
  watch:
@@ -1,4 +1,4 @@
1
- import { i as KV } from "./cli.mjs";
1
+ import { t as KV } from "./schema-BkALl7Z_.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join, resolve, sep } from "node:path";
4
4
  import { homedir } from "node:os";
@@ -113,4 +113,4 @@ async function decrementImageRef(kv, sdk, filePath) {
113
113
 
114
114
  //#endregion
115
115
  export { isManagedImagePath as a, decrementImageRef, getImageRefCount, getMaxBytes as i, incrementImageRef, IMAGES_DIR as n, saveImageToDisk as o, deleteImage as r, touchImage as s, withKeyedLock as t };
116
- //# sourceMappingURL=image-refs-R3tin9MR.mjs.map
116
+ //# sourceMappingURL=image-refs-CJS5B9Gq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-refs-R3tin9MR.mjs","names":[],"sources":["../src/utils/image-store.ts","../src/state/keyed-mutex.ts","../src/functions/image-refs.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, writeFile, unlink, utimes, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\n\nexport const IMAGES_DIR = join(homedir(), \".agentmemory\", \"images\");\n\nconst DEFAULT_MAX_BYTES = 500 * 1024 * 1024;\n\nexport function getMaxBytes(): number {\n return Number(process.env.AGENTMEMORY_IMAGE_STORE_MAX_BYTES) || DEFAULT_MAX_BYTES;\n}\n\nexport function isManagedImagePath(filePath: string): boolean {\n const resolved = resolve(filePath);\n const normalizedImagesDir = resolve(IMAGES_DIR);\n return resolved.startsWith(normalizedImagesDir + sep) || resolved === normalizedImagesDir;\n}\n\nfunction contentHash(data: string): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nexport async function saveImageToDisk(base64Data: string): Promise<{ filePath: string; bytesWritten: number }> {\n if (!base64Data) return { filePath: \"\", bytesWritten: 0 };\n\n if (!existsSync(IMAGES_DIR)) {\n await mkdir(IMAGES_DIR, { recursive: true });\n }\n\n let cleanBase64 = base64Data;\n let ext = \"png\";\n\n if (base64Data.startsWith(\"data:image/\")) {\n const commaIdx = base64Data.indexOf(\",\");\n if (commaIdx !== -1) {\n const meta = base64Data.substring(0, commaIdx);\n if (meta.includes(\"jpeg\") || meta.includes(\"jpg\")) ext = \"jpg\";\n else if (meta.includes(\"webp\")) ext = \"webp\";\n else if (meta.includes(\"gif\")) ext = \"gif\";\n cleanBase64 = base64Data.substring(commaIdx + 1);\n }\n } else if (base64Data.startsWith(\"/9j/\")) {\n ext = \"jpg\";\n }\n\n const hash = contentHash(cleanBase64);\n const filePath = join(IMAGES_DIR, `${hash}.${ext}`);\n\n if (existsSync(filePath)) {\n return { filePath, bytesWritten: 0 };\n }\n\n const buffer = Buffer.from(cleanBase64, \"base64\");\n await writeFile(filePath, buffer);\n\n const s = await stat(filePath);\n\n return { filePath, bytesWritten: s.size };\n}\n\nexport async function deleteImage(filePath: string | undefined): Promise<{ deletedBytes: number }> {\n if (!filePath) return { deletedBytes: 0 };\n if (!isManagedImagePath(filePath)) return { deletedBytes: 0 };\n try {\n if (existsSync(filePath)) {\n const s = await stat(filePath);\n const size = s.size;\n await unlink(filePath);\n return { deletedBytes: size };\n }\n } catch (err) {\n console.error(\"[agentmemory] Failed to delete image context:\", err);\n }\n return { deletedBytes: 0 };\n}\n\n/** Touch an image file to update its mtime (marking it as recently used for LRU eviction) */\nexport async function touchImage(filePath: string): Promise<void> {\n if (!filePath || !isManagedImagePath(filePath)) return;\n try {\n if (existsSync(filePath)) {\n const now = new Date();\n await utimes(filePath, now, now);\n }\n } catch (err) {\n // Ignore touch errors silently\n }\n}\n","const locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const cleanup = next.then(\n () => {},\n () => {},\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n return next;\n}\n","import type { ISdk } from \"iii-sdk\";\nimport { KV } from \"../state/schema.js\";\nimport { StateKV } from \"../state/kv.js\";\nimport { deleteImage, touchImage } from \"../utils/image-store.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\n\nexport async function getImageRefCount(kv: StateKV, filePath: string): Promise<number> {\n const count = await kv.get<number>(KV.imageRefs, filePath);\n return count ? Number(count) : 0;\n}\n\nexport async function incrementImageRef(kv: StateKV, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n await kv.set(KV.imageRefs, filePath, current + 1);\n await touchImage(filePath);\n });\n}\n\nexport async function decrementImageRef(kv: StateKV, sdk: ISdk, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n if (current <= 1) {\n await kv.delete(KV.imageEmbeddings, filePath);\n await kv.delete(KV.imageRefs, filePath);\n const { deletedBytes } = await deleteImage(filePath);\n if (deletedBytes > 0) {\n sdk.triggerVoid(\"mem::disk-size-delta\", { deltaBytes: -deletedBytes });\n }\n } else {\n await kv.set(KV.imageRefs, filePath, current - 1);\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"image-refs-CJS5B9Gq.mjs","names":[],"sources":["../src/utils/image-store.ts","../src/state/keyed-mutex.ts","../src/functions/image-refs.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, writeFile, unlink, utimes, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\n\nexport const IMAGES_DIR = join(homedir(), \".agentmemory\", \"images\");\n\nconst DEFAULT_MAX_BYTES = 500 * 1024 * 1024;\n\nexport function getMaxBytes(): number {\n return Number(process.env.AGENTMEMORY_IMAGE_STORE_MAX_BYTES) || DEFAULT_MAX_BYTES;\n}\n\nexport function isManagedImagePath(filePath: string): boolean {\n const resolved = resolve(filePath);\n const normalizedImagesDir = resolve(IMAGES_DIR);\n return resolved.startsWith(normalizedImagesDir + sep) || resolved === normalizedImagesDir;\n}\n\nfunction contentHash(data: string): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nexport async function saveImageToDisk(base64Data: string): Promise<{ filePath: string; bytesWritten: number }> {\n if (!base64Data) return { filePath: \"\", bytesWritten: 0 };\n\n if (!existsSync(IMAGES_DIR)) {\n await mkdir(IMAGES_DIR, { recursive: true });\n }\n\n let cleanBase64 = base64Data;\n let ext = \"png\";\n\n if (base64Data.startsWith(\"data:image/\")) {\n const commaIdx = base64Data.indexOf(\",\");\n if (commaIdx !== -1) {\n const meta = base64Data.substring(0, commaIdx);\n if (meta.includes(\"jpeg\") || meta.includes(\"jpg\")) ext = \"jpg\";\n else if (meta.includes(\"webp\")) ext = \"webp\";\n else if (meta.includes(\"gif\")) ext = \"gif\";\n cleanBase64 = base64Data.substring(commaIdx + 1);\n }\n } else if (base64Data.startsWith(\"/9j/\")) {\n ext = \"jpg\";\n }\n\n const hash = contentHash(cleanBase64);\n const filePath = join(IMAGES_DIR, `${hash}.${ext}`);\n\n if (existsSync(filePath)) {\n return { filePath, bytesWritten: 0 };\n }\n\n const buffer = Buffer.from(cleanBase64, \"base64\");\n await writeFile(filePath, buffer);\n\n const s = await stat(filePath);\n\n return { filePath, bytesWritten: s.size };\n}\n\nexport async function deleteImage(filePath: string | undefined): Promise<{ deletedBytes: number }> {\n if (!filePath) return { deletedBytes: 0 };\n if (!isManagedImagePath(filePath)) return { deletedBytes: 0 };\n try {\n if (existsSync(filePath)) {\n const s = await stat(filePath);\n const size = s.size;\n await unlink(filePath);\n return { deletedBytes: size };\n }\n } catch (err) {\n console.error(\"[agentmemory] Failed to delete image context:\", err);\n }\n return { deletedBytes: 0 };\n}\n\n/** Touch an image file to update its mtime (marking it as recently used for LRU eviction) */\nexport async function touchImage(filePath: string): Promise<void> {\n if (!filePath || !isManagedImagePath(filePath)) return;\n try {\n if (existsSync(filePath)) {\n const now = new Date();\n await utimes(filePath, now, now);\n }\n } catch (err) {\n // Ignore touch errors silently\n }\n}\n","const locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const cleanup = next.then(\n () => {},\n () => {},\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n return next;\n}\n","import type { ISdk } from \"iii-sdk\";\nimport { KV } from \"../state/schema.js\";\nimport { StateKV } from \"../state/kv.js\";\nimport { deleteImage, touchImage } from \"../utils/image-store.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\n\nexport async function getImageRefCount(kv: StateKV, filePath: string): Promise<number> {\n const count = await kv.get<number>(KV.imageRefs, filePath);\n return count ? Number(count) : 0;\n}\n\nexport async function incrementImageRef(kv: StateKV, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n await kv.set(KV.imageRefs, filePath, current + 1);\n await touchImage(filePath);\n });\n}\n\nexport async function decrementImageRef(kv: StateKV, sdk: ISdk, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n if (current <= 1) {\n await kv.delete(KV.imageEmbeddings, filePath);\n await kv.delete(KV.imageRefs, filePath);\n const { deletedBytes } = await deleteImage(filePath);\n if (deletedBytes > 0) {\n sdk.triggerVoid(\"mem::disk-size-delta\", { deltaBytes: -deletedBytes });\n }\n } else {\n await kv.set(KV.imageRefs, filePath, current - 1);\n }\n });\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { a as isManagedImagePath, i as getMaxBytes, n as IMAGES_DIR, o as saveImageToDisk, r as deleteImage, s as touchImage } from "./image-refs-R3tin9MR.mjs";
1
+ import { a as isManagedImagePath, i as getMaxBytes, n as IMAGES_DIR, o as saveImageToDisk, r as deleteImage, s as touchImage } from "./image-refs-CJS5B9Gq.mjs";
2
2
 
3
3
  export { deleteImage, saveImageToDisk };