@agentmemory/agentmemory 0.9.31 → 0.9.32

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 (138) hide show
  1. package/.env.example +18 -15
  2. package/AGENTS.md +4 -4
  3. package/README.md +92 -270
  4. package/dist/.env.example +18 -15
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +530 -75
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{compression-recovery-CnhLQC6N.mjs → compression-recovery-CZh63Jmz.mjs} +174 -89
  9. package/dist/compression-recovery-CZh63Jmz.mjs.map +1 -0
  10. package/dist/{connect-BSLIAMpw.mjs → connect-BX3njQ3Y.mjs} +28 -14
  11. package/dist/connect-BX3njQ3Y.mjs.map +1 -0
  12. package/dist/hooks/_capture.d.mts +3 -2
  13. package/dist/hooks/_capture.d.mts.map +1 -1
  14. package/dist/hooks/_capture.mjs +87 -8
  15. package/dist/hooks/_capture.mjs.map +1 -1
  16. package/dist/hooks/post-commit.mjs +58 -5
  17. package/dist/hooks/post-commit.mjs.map +1 -1
  18. package/dist/hooks/post-tool-failure.mjs +7 -2
  19. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  20. package/dist/hooks/post-tool-use.mjs +7 -2
  21. package/dist/hooks/post-tool-use.mjs.map +1 -1
  22. package/dist/hooks/pre-compact.mjs +67 -12
  23. package/dist/hooks/pre-compact.mjs.map +1 -1
  24. package/dist/hooks/pre-tool-use.mjs +58 -5
  25. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  26. package/dist/hooks/session-end.mjs +14 -3
  27. package/dist/hooks/session-end.mjs.map +1 -1
  28. package/dist/hooks/session-start.mjs +58 -5
  29. package/dist/hooks/session-start.mjs.map +1 -1
  30. package/dist/hooks/stop.mjs +2 -1
  31. package/dist/hooks/stop.mjs.map +1 -1
  32. package/dist/hooks/subagent-stop.mjs +15 -5
  33. package/dist/hooks/subagent-stop.mjs.map +1 -1
  34. package/dist/hooks/task-completed.mjs +8 -1
  35. package/dist/hooks/task-completed.mjs.map +1 -1
  36. package/dist/index.mjs +724 -200
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/integrations/dsh.mjs +31 -4
  39. package/dist/integrations/dsh.mjs.map +1 -1
  40. package/dist/{secret-store-D9GwO0xp.mjs → secret-store-iYXRcrO8.mjs} +197 -55
  41. package/dist/secret-store-iYXRcrO8.mjs.map +1 -0
  42. package/dist/{src-DaMtDzt_.mjs → src-C23nMpda.mjs} +359 -62
  43. package/dist/src-C23nMpda.mjs.map +1 -0
  44. package/dist/{standalone-BlqshyZ5.mjs → standalone-BYo5USDb.mjs} +169 -10
  45. package/dist/standalone-BYo5USDb.mjs.map +1 -0
  46. package/dist/standalone.d.mts.map +1 -1
  47. package/dist/standalone.mjs +240 -10
  48. package/dist/standalone.mjs.map +1 -1
  49. package/dist/viewer/index.html +49 -28
  50. package/integrations/hermes/README.md +11 -1
  51. package/integrations/hermes/__init__.py +1 -0
  52. package/integrations/hermes/plugin.yaml +1 -1
  53. package/integrations/omp/dist/index.mjs +31 -4
  54. package/integrations/omp/package.json +1 -1
  55. package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +1 -1
  56. package/integrations/omp/skills/agentmemory-architecture/SKILL.md +5 -1
  57. package/integrations/omp/skills/agentmemory-config/REFERENCE.md +2 -1
  58. package/integrations/omp/skills/agentmemory-config/SKILL.md +29 -9
  59. package/integrations/omp/skills/agentmemory-hooks/SKILL.md +17 -6
  60. package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  61. package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  62. package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  63. package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +1 -1
  64. package/integrations/openclaw/README.md +10 -0
  65. package/integrations/openclaw/openclaw.plugin.json +1 -1
  66. package/integrations/openclaw/package.json +1 -1
  67. package/integrations/openclaw/plugin.yaml +1 -1
  68. package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +1 -1
  69. package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +5 -1
  70. package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +2 -1
  71. package/integrations/openclaw/skills/agentmemory-config/SKILL.md +29 -9
  72. package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +17 -6
  73. package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  74. package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  75. package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  76. package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +1 -1
  77. package/integrations/pi/README.md +1 -0
  78. package/integrations/pi/dist/index.mjs +92 -11
  79. package/integrations/pi/index.ts +56 -6
  80. package/integrations/pi/package.json +1 -1
  81. package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +1 -1
  82. package/integrations/pi/skills/agentmemory-architecture/SKILL.md +5 -1
  83. package/integrations/pi/skills/agentmemory-config/REFERENCE.md +2 -1
  84. package/integrations/pi/skills/agentmemory-config/SKILL.md +29 -9
  85. package/integrations/pi/skills/agentmemory-hooks/SKILL.md +17 -6
  86. package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  87. package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  88. package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  89. package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +1 -1
  90. package/package.json +4 -3
  91. package/plugin/.claude-plugin/plugin.json +16 -3
  92. package/plugin/.codex-plugin/plugin.json +1 -1
  93. package/plugin/.devin-plugin/plugin.json +3 -4
  94. package/plugin/.mcp.json +5 -1
  95. package/plugin/README.md +71 -0
  96. package/plugin/hooks/hooks.json +24 -12
  97. package/plugin/opencode/plugin.json +1 -1
  98. package/plugin/plugin.json +1 -1
  99. package/plugin/scripts/_capture.d.mts +3 -2
  100. package/plugin/scripts/_capture.d.mts.map +1 -1
  101. package/plugin/scripts/_capture.mjs +87 -8
  102. package/plugin/scripts/_capture.mjs.map +1 -1
  103. package/plugin/scripts/plugin-bridge.mjs +25 -6
  104. package/plugin/scripts/post-tool-failure.mjs +7 -2
  105. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  106. package/plugin/scripts/post-tool-use.mjs +7 -2
  107. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  108. package/plugin/scripts/pre-compact.mjs +67 -12
  109. package/plugin/scripts/pre-compact.mjs.map +1 -1
  110. package/plugin/scripts/pre-tool-use.mjs +58 -5
  111. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  112. package/plugin/scripts/session-end.mjs +14 -3
  113. package/plugin/scripts/session-end.mjs.map +1 -1
  114. package/plugin/scripts/session-start.mjs +58 -5
  115. package/plugin/scripts/session-start.mjs.map +1 -1
  116. package/plugin/scripts/stop.mjs +2 -1
  117. package/plugin/scripts/stop.mjs.map +1 -1
  118. package/plugin/scripts/subagent-stop.mjs +15 -5
  119. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  120. package/plugin/scripts/task-completed.mjs +8 -1
  121. package/plugin/scripts/task-completed.mjs.map +1 -1
  122. package/plugin/skills/agentmemory-agents/REFERENCE.md +1 -1
  123. package/plugin/skills/agentmemory-architecture/SKILL.md +5 -1
  124. package/plugin/skills/agentmemory-config/REFERENCE.md +2 -1
  125. package/plugin/skills/agentmemory-config/SKILL.md +29 -9
  126. package/plugin/skills/agentmemory-hooks/SKILL.md +17 -6
  127. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  128. package/plugin/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  129. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  130. package/plugin/skills/agentmemory-rest-api/SKILL.md +1 -1
  131. package/dist/compression-recovery-CnhLQC6N.mjs.map +0 -1
  132. package/dist/connect-BSLIAMpw.mjs.map +0 -1
  133. package/dist/secret-store-D9GwO0xp.mjs.map +0 -1
  134. package/dist/src-DaMtDzt_.mjs.map +0 -1
  135. package/dist/standalone-BlqshyZ5.mjs.map +0 -1
  136. package/plugin/scripts/post-commit.d.mts +0 -1
  137. package/plugin/scripts/post-commit.mjs +0 -204
  138. package/plugin/scripts/post-commit.mjs.map +0 -1
@@ -90,6 +90,60 @@ function canonicalSecretPath() {
90
90
  return join(homedir(), ".agentmemory", "secret");
91
91
  }
92
92
  //#endregion
93
+ //#region src/hooks/_endpoint.ts
94
+ const OPTION_PREFIX = "CLAUDE_PLUGIN_OPTION_";
95
+ const DEFAULT_URL = "http://localhost:3111";
96
+ function filled(value) {
97
+ const trimmed = value?.trim();
98
+ if (!trimmed || trimmed.startsWith("${") && trimmed.endsWith("}")) return void 0;
99
+ return value;
100
+ }
101
+ function hookSetting(name, env = process.env) {
102
+ const optionKey = `${OPTION_PREFIX}${name}`;
103
+ const option = filled(env[optionKey])?.trim();
104
+ if (option) return option;
105
+ if (name === "AGENTMEMORY_SECRET" && optionKey in env) return void 0;
106
+ return filled(env[name]);
107
+ }
108
+ function resolveSettingSecret(url, env = process.env) {
109
+ const secretEnv = { ...env };
110
+ const explicit = hookSetting("AGENTMEMORY_SECRET", env);
111
+ if (explicit === void 0) delete secretEnv["AGENTMEMORY_SECRET"];
112
+ else secretEnv["AGENTMEMORY_SECRET"] = explicit;
113
+ return resolveClientSecret(url, secretEnv);
114
+ }
115
+ function secretTransportAllowed(url) {
116
+ try {
117
+ return new URL(url).protocol === "https:" || isLoopbackUrl(url);
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ function resolveHookEndpoint(env = process.env) {
123
+ const url = hookSetting("AGENTMEMORY_URL", env) || DEFAULT_URL;
124
+ const secret = resolveSettingSecret(url, env);
125
+ const blocked = !secretTransportAllowed(url);
126
+ return {
127
+ url,
128
+ secret: blocked ? "" : secret,
129
+ blocked
130
+ };
131
+ }
132
+ let blockedNoticeShown = false;
133
+ function reportBlockedTransport(endpoint) {
134
+ if (!endpoint.blocked) return false;
135
+ if (!blockedNoticeShown) {
136
+ blockedNoticeShown = true;
137
+ process.stderr.write("[agentmemory] AGENTMEMORY_URL must use HTTPS unless it is a loopback address. Nothing was sent.\n");
138
+ }
139
+ return true;
140
+ }
141
+ function hookAuthHeaders(endpoint) {
142
+ const h = { "Content-Type": "application/json" };
143
+ if (endpoint.secret) h["Authorization"] = `Bearer ${endpoint.secret}`;
144
+ return h;
145
+ }
146
+ //#endregion
93
147
  //#region src/capture/event-id.ts
94
148
  const HOST_ID_FIELDS = [
95
149
  "event_id",
@@ -236,7 +290,29 @@ function resolveDataDir(options = {}) {
236
290
  }
237
291
  //#endregion
238
292
  //#region src/functions/privacy.ts
239
- const PRIVATE_TAG_RE = /<private>[\s\S]*?<\/private>/gi;
293
+ const PRIVATE_TAG_TOKEN_RE = /<private(?=\s|>)[^>]*>|<\/private\s*>/gi;
294
+ function redactPrivateBlocks(input) {
295
+ const tokens = new RegExp(PRIVATE_TAG_TOKEN_RE.source, PRIVATE_TAG_TOKEN_RE.flags);
296
+ let out = "";
297
+ let cursor = 0;
298
+ let depth = 0;
299
+ for (let match = tokens.exec(input); match; match = tokens.exec(input)) {
300
+ const closing = match[0].startsWith("</");
301
+ if (depth === 0) {
302
+ if (closing) continue;
303
+ out += input.slice(cursor, match.index);
304
+ depth = 1;
305
+ } else if (closing) {
306
+ depth -= 1;
307
+ if (depth === 0) {
308
+ out += "[REDACTED]";
309
+ cursor = match.index + match[0].length;
310
+ }
311
+ } else depth += 1;
312
+ }
313
+ if (depth > 0) return `${out}[REDACTED]`;
314
+ return out + input.slice(cursor);
315
+ }
240
316
  const SECRET_PATTERN_SOURCES = [
241
317
  /(?:api[_-]?key|secret|token|password|credential|auth)[\s]*[=:]\s*["']?[A-Za-z0-9_\-/.+]{20,}["']?/gi,
242
318
  /Bearer\s+[A-Za-z0-9._\-+/=]{20,}/gi,
@@ -256,7 +332,7 @@ const SECRET_PATTERN_SOURCES = [
256
332
  const PRIVATE_KEY_BLOCK_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g;
257
333
  const URL_CREDENTIALS_RE = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/?#@:"'<>]*:[^\s/?#@"'<>]+@/gi;
258
334
  function stripPrivateData(input) {
259
- let result = input.replace(PRIVATE_TAG_RE, "[REDACTED]");
335
+ let result = redactPrivateBlocks(input);
260
336
  result = result.replace(new RegExp(PRIVATE_KEY_BLOCK_RE.source, PRIVATE_KEY_BLOCK_RE.flags), "[REDACTED_SECRET]");
261
337
  result = result.replace(new RegExp(URL_CREDENTIALS_RE.source, URL_CREDENTIALS_RE.flags), "$1[REDACTED_SECRET]@");
262
338
  for (const source of SECRET_PATTERN_SOURCES) {
@@ -829,16 +905,17 @@ async function drainSpool(url, send, options = {}) {
829
905
  }
830
906
  //#endregion
831
907
  //#region src/hooks/_capture.ts
832
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
833
- const SECRET = resolveClientSecret(REST_URL);
908
+ const ENDPOINT = resolveHookEndpoint();
909
+ const REST_URL = ENDPOINT.url;
834
910
  const DRAIN_CHILD_ENV = "AGENTMEMORY_CAPTURE_DRAIN_CHILD";
835
911
  const DRAIN_MAX_RECORDS = 500;
836
912
  const DRAIN_DEADLINE_MS = 2e4;
837
913
  const DRAIN_REQUEST_TIMEOUT_MS = 5e3;
838
914
  function authHeaders() {
839
- const h = { "Content-Type": "application/json" };
840
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
841
- return h;
915
+ return hookAuthHeaders(ENDPOINT);
916
+ }
917
+ function transportBlocked() {
918
+ return reportBlockedTransport(ENDPOINT);
842
919
  }
843
920
  function withEventId(body, host, content, options = {}) {
844
921
  const source = options.stable || hasHostIdentity(host) ? host : {
@@ -856,6 +933,7 @@ function classify(status) {
856
933
  return "rejected";
857
934
  }
858
935
  async function post(body, timeoutMs) {
936
+ if (transportBlocked()) throw new Error("insecure transport");
859
937
  const res = await fetch(`${REST_URL}/agentmemory/observe`, {
860
938
  method: "POST",
861
939
  headers: authHeaders(),
@@ -903,6 +981,7 @@ function startDrainChild() {
903
981
  } catch {}
904
982
  }
905
983
  async function captureObservation(body, timeoutMs) {
984
+ if (transportBlocked()) return "dropped";
906
985
  const eventId = body.eventId ?? deriveEventId(body.hookType, body.sessionId, null, body.data);
907
986
  const payload = {
908
987
  ...body,
@@ -939,6 +1018,6 @@ async function runDrainChild() {
939
1018
  }).catch(() => void 0);
940
1019
  }
941
1020
  //#endregion
942
- export { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, stripPrivateData, withEventId };
1021
+ export { REST_URL, authHeaders, captureObservation, isDrainChild, runDrainChild, stripPrivateData, transportBlocked, withEventId };
943
1022
 
944
1023
  //# sourceMappingURL=_capture.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_capture.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/capture/event-id.ts","../../src/cli-data-dir.ts","../../src/functions/privacy.ts","../../src/capture/spool.ts","../../src/hooks/_capture.ts"],"sourcesContent":["import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return process.env[\"AGENTMEMORY_DATA_DIR\"] || join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(process.env[\"AGENTMEMORY_ENV_FILE\"] || join(homedir(), \".agentmemory\", \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nfunction readSecretFile(path: string): string {\n try {\n return usable(readFileSync(path, \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nfunction legacyDataDirSecretPaths(env: NodeJS.ProcessEnv = process.env): string[] {\n const dataDir = env[\"AGENTMEMORY_DATA_DIR\"];\n if (!dataDir) return [];\n const paths = [join(dataDir, \"secret\"), join(dataDir, \".hmac\"), join(dataDir, \"..\", \".hmac\")];\n return [...new Set(paths.filter((path) => path !== secretFilePath()))];\n}\n\nexport function readStoredSecret(env: NodeJS.ProcessEnv = process.env): string {\n const canonicalPath = canonicalSecretPath();\n const localPath = secretFilePath();\n const legacyPaths = legacyDataDirSecretPaths(env);\n return readSecretFile(canonicalPath) ||\n (localPath === canonicalPath ? \"\" : readSecretFile(localPath)) ||\n legacyPaths.map(readSecretFile).find(Boolean) || \"\";\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (env.AGENTMEMORY_CREDENTIAL_MODE === \"explicit\") return \"\";\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret(env);\n}\n\nfunction canonicalSecretPath(): string {\n return join(homedir(), \".agentmemory\", \"secret\");\n}\n\nfunction writeSecretFile(path: string, secret: string): void {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n renameSync(staging, path);\n chmodSync(path, 0o600);\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n}\n\nfunction writeSecretIfAbsent(path: string, secret: string): string {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n const stored = readSecretFile(path);\n if (!stored) throw new Error(`Could not read stored API secret at ${path}.`);\n return stored;\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n\n const localPath = secretFilePath();\n const canonicalPath = canonicalSecretPath();\n const canonical = localPath === canonicalPath ? \"\" : readSecretFile(canonicalPath);\n if (canonical) {\n if (localPath !== canonicalPath && readSecretFile(localPath) !== canonical) {\n writeSecretFile(localPath, canonical);\n }\n return { secret: canonical, source: \"file\" };\n }\n\n const legacy = readSecretFile(localPath) || legacyDataDirSecretPaths(env).map(readSecretFile).find(Boolean) || \"\";\n const candidate = legacy || randomBytes(32).toString(\"hex\");\n const localSecret = writeSecretIfAbsent(localPath, candidate);\n const secret = localPath === canonicalPath\n ? localSecret\n : writeSecretIfAbsent(canonicalPath, localSecret);\n if (localPath !== canonicalPath && localSecret !== secret) writeSecretFile(localPath, secret);\n return { secret, source: legacy ? \"file\" : \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","import { createHash } from \"node:crypto\";\n\nexport const EVENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_.:-]{7,127}$/;\n\nconst HOST_ID_FIELDS = [\n \"event_id\",\n \"eventId\",\n \"tool_use_id\",\n \"toolUseId\",\n \"tool_call_id\",\n \"toolCallId\",\n \"call_id\",\n \"callId\",\n \"prompt_id\",\n \"promptId\",\n \"message_id\",\n \"messageId\",\n];\n\nconst HOST_TIME_FIELDS = [\"timestamp\", \"ts\", \"created_at\", \"createdAt\", \"event_time\"];\n\nexport function isValidEventId(value: unknown): value is string {\n return typeof value === \"string\" && EVENT_ID_RE.test(value);\n}\n\nexport function stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null || typeof value !== \"object\") {\n const encoded = JSON.stringify(value);\n return encoded === undefined ? \"null\" : encoded;\n }\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort();\n return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(\",\")}}`;\n}\n\nfunction hostScalar(host: Record<string, unknown>, fields: string[]): string | undefined {\n for (const field of fields) {\n const v = host[field];\n if (typeof v === \"string\" && v.trim()) return v.trim();\n if (typeof v === \"number\" && Number.isFinite(v)) return String(v);\n }\n return undefined;\n}\n\nfunction digest(parts: string[]): string {\n return createHash(\"sha256\").update(parts.join(\"\\u0000\")).digest(\"hex\").slice(0, 32);\n}\n\nexport function hasHostIdentity(host: Record<string, unknown> | null | undefined): boolean {\n const source = host && typeof host === \"object\" ? host : {};\n return hostScalar(source, HOST_ID_FIELDS) !== undefined || hostScalar(source, HOST_TIME_FIELDS) !== undefined;\n}\n\nexport function deriveEventId(\n hookType: string,\n sessionId: string,\n host: Record<string, unknown> | null | undefined,\n content: unknown,\n): string {\n const source = host && typeof host === \"object\" ? host : {};\n const hostId = hostScalar(source, HOST_ID_FIELDS);\n if (hostId) return `evh_${digest([\"host\", sessionId, hookType, hostId])}`;\n const hostTime = hostScalar(source, HOST_TIME_FIELDS) ?? \"\";\n return `evc_${digest([\"content\", sessionId, hookType, hostTime, stableStringify(content)])}`;\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport type DataDirSource = \"flag\" | \"env\" | \"default\";\n\nexport type ResolvedDataDir = {\n dataDir: string;\n source: DataDirSource;\n relocatedFrom?: string;\n};\n\ntype ResolveDataDirOptions = {\n args?: string[];\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n home?: string;\n platform?: NodeJS.Platform;\n};\n\nfunction argValue(args: string[], name: string): string | undefined {\n const equals = `${name}=`;\n const inline = args.find((arg) => arg.startsWith(equals));\n if (inline) return inline.slice(equals.length);\n\n const idx = args.indexOf(name);\n if (idx !== -1) return args[idx + 1];\n return undefined;\n}\n\nfunction expandHome(pathValue: string, home: string): string {\n if (pathValue === \"~\") return home;\n if (pathValue.startsWith(\"~/\") || pathValue.startsWith(\"~\\\\\")) {\n return join(home, pathValue.slice(2));\n }\n return pathValue;\n}\n\nfunction absoluteDataDir(pathValue: string, cwd: string, home: string): string {\n const expanded = expandHome(pathValue, home);\n return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);\n}\n\nfunction platformDefaultDataDir(\n env: NodeJS.ProcessEnv,\n home: string,\n nodePlatform: NodeJS.Platform,\n useXdg = true,\n): string {\n if (nodePlatform === \"darwin\") {\n return join(home, \"Library\", \"Application Support\", \"agentmemory\");\n }\n if (nodePlatform === \"win32\") {\n const appData = env[\"APPDATA\"];\n return appData\n ? join(appData, \"agentmemory\")\n : join(home, \".agentmemory\");\n }\n const xdgDataHome = env[\"XDG_DATA_HOME\"];\n if (useXdg && xdgDataHome && isAbsolute(xdgDataHome)) {\n return join(xdgDataHome, \"agentmemory\");\n }\n return join(home, \".local\", \"share\", \"agentmemory\");\n}\n\nfunction nearestGitParent(pathValue: string): string | undefined {\n let current = pathValue;\n while (true) {\n if (existsSync(join(current, \".git\"))) return current;\n const parent = dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n const pathFromParent = relative(parent, child);\n return (\n pathFromParent === \"\" ||\n (pathFromParent !== \"..\" &&\n !pathFromParent.startsWith(`..${sep}`) &&\n !isAbsolute(pathFromParent))\n );\n}\n\nexport function resolveDataDir(options: ResolveDataDirOptions = {}): ResolvedDataDir {\n const args = options.args ?? process.argv.slice(2);\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n const home = options.home ?? homedir();\n const nodePlatform = options.platform ?? platform();\n const parsedInstance = parseInt(argValue(args, \"--instance\") ?? \"0\", 10);\n const instance =\n parsedInstance > 0 && parsedInstance <= 50 ? parsedInstance : 0;\n const forInstance = (dataDir: string) =>\n instance ? join(dataDir, `instance-${instance}`) : dataDir;\n\n const flagValue = argValue(args, \"--data-dir\");\n if (flagValue) {\n return {\n dataDir: forInstance(absoluteDataDir(flagValue, cwd, home)),\n source: \"flag\",\n };\n }\n\n const envValue = env[\"AGENTMEMORY_DATA_DIR\"];\n if (envValue) {\n return {\n dataDir: forInstance(absoluteDataDir(envValue, cwd, home)),\n source: \"env\",\n };\n }\n\n const legacyDir = resolve(cwd, \"data\");\n const hasLegacyStore =\n existsSync(join(legacyDir, \"state_store.db\")) ||\n existsSync(join(legacyDir, \"iii-config.yaml\"));\n if (instance === 0 && hasLegacyStore) {\n return { dataDir: legacyDir, source: \"default\" };\n }\n\n const defaultDir = platformDefaultDataDir(env, home, nodePlatform);\n const gitParent = nearestGitParent(cwd);\n if (gitParent && isWithin(gitParent, defaultDir)) {\n const relocated = platformDefaultDataDir(env, home, nodePlatform, false);\n if (relocated !== defaultDir) {\n return {\n dataDir: forInstance(relocated),\n source: \"default\",\n relocatedFrom: defaultDir,\n };\n }\n }\n\n return {\n dataDir: forInstance(defaultDir),\n source: \"default\",\n };\n}\n","import type { IIIClient } from \"iii-sdk\";\n\nconst PRIVATE_TAG_RE = /<private>[\\s\\S]*?<\\/private>/gi;\n\nconst SECRET_PATTERN_SOURCES = [\n /(?:api[_-]?key|secret|token|password|credential|auth)[\\s]*[=:]\\s*[\"']?[A-Za-z0-9_\\-/.+]{20,}[\"']?/gi,\n /Bearer\\s+[A-Za-z0-9._\\-+/=]{20,}/gi,\n /(?<![\\p{L}\\p{N}\\p{M}_])sk-proj-[A-Za-z0-9\\-_]{20,}/gu,\n /(?<![\\p{L}\\p{N}\\p{M}_])(?:sk|pk|rk|ak)-[A-Za-z0-9][A-Za-z0-9\\-_]{19,}/gu,\n /(?<![\\p{L}\\p{N}\\p{M}_])sk-ant-[A-Za-z0-9\\-_]{20,}/gu,\n /gh[pus]_[A-Za-z0-9]{36,}/g,\n /github_pat_[A-Za-z0-9_]{22,}/g,\n /xoxb-[A-Za-z0-9\\-]+/g,\n /AKIA[0-9A-Z]{16}/g,\n /AIza[A-Za-z0-9\\-_]{35}/g,\n /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g,\n /npm_[A-Za-z0-9]{36}/g,\n /glpat-[A-Za-z0-9\\-_]{20,}/g,\n /dop_v1_[A-Za-z0-9]{64}/g,\n];\n\nconst PRIVATE_KEY_BLOCK_RE =\n /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\\s\\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g;\n\nconst URL_CREDENTIALS_RE =\n /\\b([a-z][a-z0-9+.-]*:\\/\\/)[^\\s/?#@:\"'<>]*:[^\\s/?#@\"'<>]+@/gi;\n\nexport function stripPrivateData(input: string): string {\n let result = input.replace(PRIVATE_TAG_RE, \"[REDACTED]\");\n result = result.replace(new RegExp(PRIVATE_KEY_BLOCK_RE.source, PRIVATE_KEY_BLOCK_RE.flags), \"[REDACTED_SECRET]\");\n result = result.replace(new RegExp(URL_CREDENTIALS_RE.source, URL_CREDENTIALS_RE.flags), \"$1[REDACTED_SECRET]@\");\n for (const source of SECRET_PATTERN_SOURCES) {\n const pattern = new RegExp(source.source, source.flags);\n result = result.replace(pattern, \"[REDACTED_SECRET]\");\n }\n return result;\n}\n\nexport function scrubRecord<T>(value: T, options: { scrubKeys?: boolean } = {}): T {\n if (typeof value === \"string\") return stripPrivateData(value) as T;\n if (Array.isArray(value)) return value.map((item) => scrubRecord(item, options)) as T;\n if (value !== null && typeof value === \"object\") {\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n const entries = Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n options.scrubKeys ? stripPrivateData(key) : key,\n item,\n ] as const);\n const reserved = new Set(entries.map(([key]) => key));\n const used = new Set<string>();\n return Object.fromEntries(\n entries.map(([key, item]) => {\n let unique = key;\n if (used.has(unique)) {\n let suffix = 2;\n do { unique = `${key}_${suffix++}`; } while (reserved.has(unique) || used.has(unique));\n }\n used.add(unique);\n return [unique, scrubRecord(item, options)];\n }),\n ) as T;\n }\n return value;\n}\n\nexport const SCRUBBED_WRITE_FUNCTIONS: ReadonlySet<string> = new Set([\n \"mem::remember\",\n \"mem::evolve\",\n \"mem::slot-create\",\n \"mem::slot-append\",\n \"mem::slot-replace\",\n \"mem::lesson-save\",\n \"mem::action-create\",\n \"mem::action-update\",\n \"mem::sketch-create\",\n \"mem::sketch-add\",\n \"mem::import\",\n \"mem::mesh-receive\",\n \"mem::team-share\",\n \"mem::signal-send\",\n \"mem::routine-create\",\n \"mem::checkpoint-create\",\n \"mem::checkpoint-resolve\",\n \"mem::sentinel-create\",\n \"mem::sentinel-trigger\",\n \"mem::core-add\",\n \"mem::facet-tag\",\n]);\n\ntype RegisterFunction = (id: string, handler: unknown, ...rest: unknown[]) => unknown;\n\nexport function withWriteScrubbing<T extends object>(sdk: T): T {\n return new Proxy(sdk, {\n get(target, prop) {\n const value = Reflect.get(target, prop, target);\n if (typeof value !== \"function\") return value;\n if (prop !== \"registerFunction\") return value.bind(target);\n const register = value as RegisterFunction;\n return (id: string, handler: unknown, ...rest: unknown[]) => {\n if (!SCRUBBED_WRITE_FUNCTIONS.has(id) || typeof handler !== \"function\") {\n return register.call(target, id, handler, ...rest);\n }\n const original = handler as (data: unknown, ...args: unknown[]) => unknown;\n const scrubbed = (data: unknown, ...args: unknown[]) => original(scrubRecord(data), ...args);\n return register.call(target, id, scrubbed, ...rest);\n };\n },\n });\n}\n\nexport function registerPrivacyFunction(sdk: IIIClient): void {\n sdk.registerFunction(\"mem::privacy\", \n async (data: { input?: unknown } | undefined) => {\n if (!data || typeof data.input !== \"string\") {\n return { output: \"\", error: \"invalid input: expected string field 'input'\" };\n }\n return { output: stripPrivateData(data.input) };\n },\n );\n}\n","import {\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n utimesSync,\n writeFileSync,\n writeSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { resolveDataDir } from \"../cli-data-dir.js\";\nimport { scrubRecord } from \"../functions/privacy.js\";\n\nexport interface SpoolRecord {\n v: 1;\n eventId: string;\n spooledAt: string;\n reason: string;\n attempts: number;\n body: Record<string, unknown>;\n bootId?: string;\n}\n\nexport interface SentMark {\n bootId: string;\n durableAfterMs: number;\n}\n\nexport interface SentReconcile {\n released: number;\n requeued: number;\n}\n\nexport interface SpoolPolicy {\n enabled: boolean;\n maxBytes: number;\n maxAgeMs: number;\n maxRecordBytes: number;\n}\n\nexport interface SpoolStats {\n spooled: number;\n dropped: number;\n droppedBytes: number;\n expired: number;\n delivered: number;\n duplicates: number;\n rejected: number;\n lastSpooledAt?: string;\n lastSpoolReason?: string;\n lastDropAt?: string;\n lastDropReason?: string;\n lastDrainAt?: string;\n lastDrainDelivered?: number;\n lastDrainDuplicates?: number;\n lastDrainRejected?: number;\n lastDrainRemaining?: number;\n lastDrainError?: string;\n}\n\nexport interface SpoolPaths {\n dir: string;\n name: string;\n file: string;\n lock: string;\n drainLock: string;\n stats: string;\n}\n\nexport interface SpoolSummary {\n enabled: boolean;\n path: string;\n records: number;\n bytes: number;\n oldestAt: string | null;\n retained: number;\n retainedBytes: number;\n maxBytes: number;\n maxAgeHours: number;\n stats: SpoolStats;\n}\n\nexport type SendOutcome = \"delivered\" | \"duplicate\" | \"rejected\" | \"retry\";\n\nexport interface DrainResult {\n claimed: number;\n delivered: number;\n duplicates: number;\n rejected: number;\n expired: number;\n corrupt: number;\n remaining: number;\n error?: string;\n skipped?: \"locked\" | \"empty\" | \"disabled\";\n}\n\nconst DEFAULT_MAX_BYTES = 5 * 1024 * 1024;\nconst MIN_MAX_BYTES = 64 * 1024;\nconst DEFAULT_MAX_AGE_HOURS = 168;\nconst LOCK_STALE_MS = 10_000;\nconst DRAIN_LOCK_STALE_MS = 120_000;\nconst ORPHAN_CLAIM_MS = 120_000;\nconst LOCK_WAIT_MS = 500;\nconst IMAGE_PLACEHOLDER = \"[image dropped from capture spool]\";\nconst BOOT_ID_RE = /^[A-Za-z0-9]{8,64}$/;\nconst SENT_FILE_RE = /^([A-Za-z0-9]{8,64})-(\\d+)\\.jsonl$/;\nconst SENT_BUCKET_MS = 1000;\n// Cover the maximum one-hour state flush interval plus its safety margin.\nconst MAX_DURABLE_AFTER_MS = 61 * 60_000;\n\nfunction emptyStats(): SpoolStats {\n return { spooled: 0, dropped: 0, droppedBytes: 0, expired: 0, delivered: 0, duplicates: 0, rejected: 0 };\n}\n\nfunction positiveInt(raw: string | undefined, fallback: number): number {\n if (!raw || !/^\\d+$/.test(raw.trim())) return fallback;\n const n = parseInt(raw.trim(), 10);\n return n > 0 ? n : fallback;\n}\n\nexport function spoolPolicy(env: NodeJS.ProcessEnv = process.env): SpoolPolicy {\n const maxBytes = Math.max(MIN_MAX_BYTES, positiveInt(env[\"AGENTMEMORY_CAPTURE_SPOOL_MAX_BYTES\"], DEFAULT_MAX_BYTES));\n const maxAgeHours = positiveInt(env[\"AGENTMEMORY_CAPTURE_SPOOL_MAX_AGE_HOURS\"], DEFAULT_MAX_AGE_HOURS);\n return {\n enabled: env[\"AGENTMEMORY_CAPTURE_SPOOL\"] !== \"false\",\n maxBytes,\n maxAgeMs: maxAgeHours * 3600_000,\n maxRecordBytes: Math.min(256 * 1024, Math.floor(maxBytes / 4)),\n };\n}\n\nexport function spoolDir(env: NodeJS.ProcessEnv = process.env): string {\n const explicit = env[\"AGENTMEMORY_CAPTURE_SPOOL_DIR\"];\n if (explicit && explicit.trim()) return resolve(explicit.trim());\n return join(resolveDataDir({ args: [], env }).dataDir, \"capture-spool\");\n}\n\nconst LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\", \"0.0.0.0\"]);\n\nexport function spoolTargetName(url: string): string {\n try {\n const u = new URL(url);\n const host = LOOPBACK.has(u.hostname) ? \"local\" : u.hostname.toLowerCase().replace(/[^a-z0-9.-]/g, \"_\");\n const port = u.port || (u.protocol === \"https:\" ? \"443\" : \"80\");\n return `${host}-${port}`;\n } catch {\n return \"local-3111\";\n }\n}\n\nexport function spoolPaths(url: string, dir: string = spoolDir()): SpoolPaths {\n const name = spoolTargetName(url);\n return {\n dir,\n name,\n file: join(dir, `${name}.jsonl`),\n lock: join(dir, `${name}.lock`),\n drainLock: join(dir, `${name}.drain.lock`),\n stats: join(dir, `${name}.stats.json`),\n };\n}\n\nfunction ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n try {\n chmodSync(dir, 0o700);\n } catch {}\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n}\n\nfunction tryLock(path: string, staleMs: number): boolean {\n try {\n const fd = openSync(path, \"wx\", 0o600);\n writeSync(fd, `${process.pid}\\n`);\n closeSync(fd);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") return false;\n try {\n if (Date.now() - statSync(path).mtimeMs > staleMs) unlinkSync(path);\n } catch {}\n return false;\n }\n}\n\nfunction releaseLock(path: string): void {\n try {\n unlinkSync(path);\n } catch {}\n}\n\nfunction withLock<T>(path: string, fn: (locked: boolean) => T, waitMs = LOCK_WAIT_MS): T {\n const deadline = Date.now() + waitMs;\n let locked = tryLock(path, LOCK_STALE_MS);\n while (!locked && Date.now() < deadline) {\n sleepSync(5);\n locked = tryLock(path, LOCK_STALE_MS);\n }\n try {\n return fn(locked);\n } finally {\n if (locked) releaseLock(path);\n }\n}\n\nexport function readSpoolStats(paths: SpoolPaths): SpoolStats {\n try {\n const parsed = JSON.parse(readFileSync(paths.stats, \"utf-8\")) as Partial<SpoolStats>;\n return { ...emptyStats(), ...parsed };\n } catch {\n return emptyStats();\n }\n}\n\nfunction writeStats(paths: SpoolPaths, patch: (stats: SpoolStats) => void): void {\n try {\n const stats = readSpoolStats(paths);\n patch(stats);\n const tmp = `${paths.stats}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(stats), { mode: 0o600 });\n renameSync(tmp, paths.stats);\n } catch {}\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nfunction withoutImage(body: Record<string, unknown>): Record<string, unknown> {\n const data = body[\"data\"];\n if (!data || typeof data !== \"object\" || Array.isArray(data)) return body;\n const d = data as Record<string, unknown>;\n if (d[\"image_data\"] === undefined) return body;\n return { ...body, data: { ...d, image_data: IMAGE_PLACEHOLDER } };\n}\n\nfunction parseLines(text: string): { records: SpoolRecord[]; corrupt: number } {\n const records: SpoolRecord[] = [];\n let corrupt = 0;\n for (const line of text.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const rec = JSON.parse(line) as SpoolRecord;\n if (rec && typeof rec.eventId === \"string\" && rec.body && typeof rec.body === \"object\") records.push(rec);\n else corrupt++;\n } catch {\n corrupt++;\n }\n }\n return { records, corrupt };\n}\n\nfunction isExpired(rec: SpoolRecord, policy: SpoolPolicy, now: number): boolean {\n const at = Date.parse(rec.spooledAt);\n return Number.isFinite(at) && now - at > policy.maxAgeMs;\n}\n\nfunction serialize(records: SpoolRecord[]): string {\n return records.map((r) => JSON.stringify(r)).join(\"\\n\") + (records.length ? \"\\n\" : \"\");\n}\n\nfunction rewriteLocked(paths: SpoolPaths, records: SpoolRecord[]): void {\n const tmp = `${paths.file}.${process.pid}.tmp`;\n const fd = openSync(tmp, \"w\", 0o600);\n try {\n writeSync(fd, serialize(records));\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n renameSync(tmp, paths.file);\n}\n\nfunction pruneExpiredLocked(paths: SpoolPaths, policy: SpoolPolicy): number {\n if (!existsSync(paths.file)) return 0;\n const { records } = parseLines(readFileSync(paths.file, \"utf-8\"));\n const now = Date.now();\n const kept = records.filter((r) => !isExpired(r, policy, now));\n const expired = records.length - kept.length;\n if (expired > 0) rewriteLocked(paths, kept);\n return expired;\n}\n\nexport interface AppendResult {\n spooled: boolean;\n dropped?: \"disabled\" | \"too-large\" | \"full\" | \"error\";\n}\n\nexport function appendSpool(\n url: string,\n eventId: string,\n body: Record<string, unknown>,\n reason: string,\n options: { policy?: SpoolPolicy; dir?: string; attempts?: number } = {},\n): AppendResult {\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled) return { spooled: false, dropped: \"disabled\" };\n const paths = spoolPaths(url, options.dir);\n const note = (dropReason: NonNullable<AppendResult[\"dropped\"]>, bytes: number) => {\n writeStats(paths, (s) => {\n s.dropped++;\n s.droppedBytes += bytes;\n s.lastDropAt = new Date().toISOString();\n s.lastDropReason = dropReason;\n });\n return { spooled: false, dropped: dropReason } as AppendResult;\n };\n try {\n ensureDir(paths.dir);\n const record: SpoolRecord = {\n v: 1,\n eventId,\n spooledAt: new Date().toISOString(),\n reason,\n attempts: options.attempts ?? 0,\n body: scrubRecord(JSON.parse(JSON.stringify(body)), { scrubKeys: true }),\n };\n let line = JSON.stringify(record) + \"\\n\";\n if (Buffer.byteLength(line) > policy.maxRecordBytes) {\n record.body = withoutImage(record.body);\n line = JSON.stringify(record) + \"\\n\";\n }\n const bytes = Buffer.byteLength(line);\n if (bytes > policy.maxRecordBytes) return note(\"too-large\", bytes);\n return withLock(paths.lock, (locked) => {\n let expired = 0;\n if (locked && fileSize(paths.file) + bytes > policy.maxBytes) expired = pruneExpiredLocked(paths, policy);\n const over = fileSize(paths.file) + sentBytes(paths) + bytes - policy.maxBytes;\n if (locked && over > 0) evictSent(paths, over);\n if (fileSize(paths.file) + sentBytes(paths) + bytes > policy.maxBytes) {\n if (expired) writeStats(paths, (s) => void (s.expired += expired));\n return note(\"full\", bytes);\n }\n const fd = openSync(paths.file, \"a\", 0o600);\n try {\n writeSync(fd, line);\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n writeStats(paths, (s) => {\n s.spooled++;\n s.expired += expired;\n s.lastSpooledAt = record.spooledAt;\n s.lastSpoolReason = reason;\n });\n return { spooled: true } as AppendResult;\n });\n } catch {\n return { spooled: false, dropped: \"error\" };\n }\n}\n\ninterface SentSegment {\n file: string;\n bootId: string;\n dueAt: number;\n bytes: number;\n}\n\nfunction listSent(paths: SpoolPaths): SentSegment[] {\n const segments: SentSegment[] = [];\n let entries: string[];\n try {\n entries = readdirSync(paths.dir);\n } catch {\n return segments;\n }\n const prefix = `${paths.name}.sent-`;\n for (const entry of entries) {\n if (!entry.startsWith(prefix)) continue;\n const match = entry.slice(prefix.length).match(SENT_FILE_RE);\n if (!match) continue;\n const file = join(paths.dir, entry);\n segments.push({ file, bootId: match[1]!, dueAt: Number(match[2]), bytes: fileSize(file) });\n }\n return segments;\n}\n\nfunction sentBytes(paths: SpoolPaths): number {\n return listSent(paths).reduce((n, s) => n + s.bytes, 0);\n}\n\nfunction evictSent(paths: SpoolPaths, need: number): void {\n let freed = 0;\n for (const seg of listSent(paths).sort((a, b) => a.dueAt - b.dueAt)) {\n if (freed >= need) return;\n try {\n unlinkSync(seg.file);\n freed += seg.bytes;\n } catch {}\n }\n}\n\nexport function parseSentMark(body: unknown): SentMark | null {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) return null;\n const bootId = (body as Record<string, unknown>)[\"bootId\"];\n const after = (body as Record<string, unknown>)[\"durableAfterMs\"];\n if (typeof bootId !== \"string\" || !BOOT_ID_RE.test(bootId)) return null;\n if (typeof after !== \"number\" || !Number.isFinite(after) || after < 0) return null;\n return { bootId, durableAfterMs: Math.min(Math.ceil(after), MAX_DURABLE_AFTER_MS) };\n}\n\nexport function retainSent(\n url: string,\n eventId: string,\n body: Record<string, unknown>,\n mark: SentMark,\n options: { policy?: SpoolPolicy; dir?: string; now?: number } = {},\n): boolean {\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled || !BOOT_ID_RE.test(mark.bootId)) return false;\n const paths = spoolPaths(url, options.dir);\n const now = options.now ?? Date.now();\n try {\n ensureDir(paths.dir);\n const record: SpoolRecord = {\n v: 1,\n eventId,\n spooledAt: new Date(now).toISOString(),\n reason: \"sent\",\n attempts: 0,\n body: scrubRecord(JSON.parse(JSON.stringify(body)), { scrubKeys: true }),\n bootId: mark.bootId,\n };\n let line = JSON.stringify(record) + \"\\n\";\n if (Buffer.byteLength(line) > policy.maxRecordBytes) {\n record.body = withoutImage(record.body);\n line = JSON.stringify(record) + \"\\n\";\n }\n const bytes = Buffer.byteLength(line);\n if (bytes > policy.maxRecordBytes) return false;\n const dueAt = Math.ceil((now + mark.durableAfterMs) / SENT_BUCKET_MS) * SENT_BUCKET_MS;\n const file = join(paths.dir, `${paths.name}.sent-${mark.bootId}-${dueAt}.jsonl`);\n return withLock(paths.lock, () => {\n if (fileSize(paths.file) + sentBytes(paths) + bytes > policy.maxBytes) return false;\n const fd = openSync(file, \"a\", 0o600);\n try {\n writeSync(fd, line);\n } finally {\n closeSync(fd);\n }\n return true;\n });\n } catch {\n return false;\n }\n}\n\nexport function reconcileSent(\n url: string,\n bootId: string,\n options: { policy?: SpoolPolicy; dir?: string; now?: number } = {},\n): SentReconcile {\n const result: SentReconcile = { released: 0, requeued: 0 };\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled) return result;\n const paths = spoolPaths(url, options.dir);\n const now = options.now ?? Date.now();\n for (const seg of listSent(paths)) {\n if (seg.bootId === bootId) {\n if (seg.dueAt > now) continue;\n try {\n unlinkSync(seg.file);\n result.released++;\n } catch {}\n continue;\n }\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${now}-${result.requeued}.jsonl`);\n try {\n renameSync(seg.file, target);\n } catch {\n continue;\n }\n try {\n utimesSync(target, 0, 0);\n } catch {}\n result.requeued++;\n }\n return result;\n}\n\nfunction claimFiles(paths: SpoolPaths): string[] {\n const claimed: string[] = [];\n try {\n const prefix = `${paths.name}.draining-`;\n for (const entry of readdirSync(paths.dir)) {\n if (!entry.startsWith(prefix) || !entry.endsWith(\".jsonl\")) continue;\n const full = join(paths.dir, entry);\n try {\n if (Date.now() - statSync(full).mtimeMs > ORPHAN_CLAIM_MS) claimed.push(full);\n } catch {}\n }\n } catch {}\n if (fileSize(paths.file) > 0) {\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${Date.now()}.jsonl`);\n withLock(paths.lock, (locked) => {\n if (!locked) return;\n try {\n renameSync(paths.file, target);\n claimed.push(target);\n } catch {}\n });\n }\n return claimed;\n}\n\nfunction requeueLocked(paths: SpoolPaths, leftover: SpoolRecord[]): void {\n if (leftover.length === 0) return;\n const existing = existsSync(paths.file) ? parseLines(readFileSync(paths.file, \"utf-8\")).records : [];\n rewriteLocked(paths, [...leftover, ...existing]);\n}\n\nfunction requeueUnlocked(paths: SpoolPaths, leftover: SpoolRecord[]): void {\n if (leftover.length === 0) return;\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${Date.now()}-requeue.jsonl`);\n const fd = openSync(target, \"wx\", 0o600);\n try {\n writeSync(fd, serialize(leftover));\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n try {\n utimesSync(target, 0, 0);\n } catch {}\n}\n\nexport function spoolHasRecords(url: string, dir?: string): boolean {\n return fileSize(spoolPaths(url, dir).file) > 0;\n}\n\nexport function drainInProgress(url: string, dir?: string): boolean {\n const paths = spoolPaths(url, dir);\n try {\n return Date.now() - statSync(paths.drainLock).mtimeMs < DRAIN_LOCK_STALE_MS;\n } catch {\n return false;\n }\n}\n\nexport async function drainSpool(\n url: string,\n send: (record: SpoolRecord) => Promise<SendOutcome>,\n options: { policy?: SpoolPolicy; dir?: string; maxRecords?: number; deadlineMs?: number } = {},\n): Promise<DrainResult> {\n const policy = options.policy ?? spoolPolicy();\n const result: DrainResult = { claimed: 0, delivered: 0, duplicates: 0, rejected: 0, expired: 0, corrupt: 0, remaining: 0 };\n const paths = spoolPaths(url, options.dir);\n if (!existsSync(paths.dir)) return { ...result, skipped: \"empty\" };\n try {\n ensureDir(paths.dir);\n } catch {}\n if (!tryLock(paths.drainLock, DRAIN_LOCK_STALE_MS)) return { ...result, skipped: \"locked\" };\n try {\n const files = claimFiles(paths);\n if (files.length === 0) return { ...result, skipped: \"empty\" };\n const records: SpoolRecord[] = [];\n for (const file of files) {\n try {\n const parsed = parseLines(readFileSync(file, \"utf-8\"));\n records.push(...parsed.records);\n result.corrupt += parsed.corrupt;\n } catch {}\n }\n result.claimed = records.length;\n const seen = new Set<string>();\n const now = Date.now();\n const deadline = options.deadlineMs ? now + options.deadlineMs : Number.POSITIVE_INFINITY;\n const maxRecords = options.maxRecords ?? Number.POSITIVE_INFINITY;\n const leftover: SpoolRecord[] = [];\n let stopped = false;\n let processed = 0;\n for (const rec of records) {\n if (seen.has(rec.eventId)) {\n result.duplicates++;\n continue;\n }\n seen.add(rec.eventId);\n if (isExpired(rec, policy, now)) {\n result.expired++;\n continue;\n }\n if (stopped || processed >= maxRecords || Date.now() > deadline) {\n leftover.push(rec);\n continue;\n }\n processed++;\n let outcome: SendOutcome;\n try {\n outcome = await send(rec);\n } catch (err) {\n outcome = \"retry\";\n result.error = err instanceof Error ? err.message : String(err);\n }\n if (outcome === \"delivered\") result.delivered++;\n else if (outcome === \"duplicate\") result.duplicates++;\n else if (outcome === \"rejected\") result.rejected++;\n else {\n stopped = true;\n leftover.push({ ...rec, attempts: (rec.attempts ?? 0) + 1 });\n }\n }\n result.remaining = leftover.length;\n withLock(paths.lock, (locked) => (locked ? requeueLocked(paths, leftover) : requeueUnlocked(paths, leftover)), 2000);\n for (const file of files) {\n try {\n unlinkSync(file);\n } catch {}\n }\n writeStats(paths, (s) => {\n s.delivered += result.delivered;\n s.duplicates += result.duplicates;\n s.rejected += result.rejected;\n s.expired += result.expired;\n s.lastDrainAt = new Date().toISOString();\n s.lastDrainDelivered = result.delivered;\n s.lastDrainDuplicates = result.duplicates;\n s.lastDrainRejected = result.rejected;\n s.lastDrainRemaining = result.remaining;\n if (result.error) s.lastDrainError = result.error;\n else delete s.lastDrainError;\n });\n return result;\n } finally {\n releaseLock(paths.drainLock);\n }\n}\n\nexport function spoolSummary(url: string, options: { policy?: SpoolPolicy; dir?: string } = {}): SpoolSummary {\n const policy = options.policy ?? spoolPolicy();\n const paths = spoolPaths(url, options.dir);\n let records = 0;\n let bytes = 0;\n let oldestAt: string | null = null;\n const files: string[] = [];\n if (existsSync(paths.file)) files.push(paths.file);\n try {\n for (const entry of readdirSync(paths.dir)) {\n if (entry.startsWith(`${paths.name}.draining-`) && entry.endsWith(\".jsonl\")) files.push(join(paths.dir, entry));\n }\n } catch {}\n for (const file of files) {\n try {\n const text = readFileSync(file, \"utf-8\");\n bytes += Buffer.byteLength(text);\n for (const rec of parseLines(text).records) {\n records++;\n if (!oldestAt || rec.spooledAt < oldestAt) oldestAt = rec.spooledAt;\n }\n } catch {}\n }\n let retained = 0;\n let retainedBytes = 0;\n for (const seg of listSent(paths)) {\n try {\n const text = readFileSync(seg.file, \"utf-8\");\n retainedBytes += Buffer.byteLength(text);\n retained += parseLines(text).records.length;\n } catch {}\n }\n return {\n enabled: policy.enabled,\n path: paths.file,\n records,\n bytes,\n oldestAt,\n retained,\n retainedBytes,\n maxBytes: policy.maxBytes,\n maxAgeHours: Math.round(policy.maxAgeMs / 3600_000),\n stats: readSpoolStats(paths),\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { resolveClientSecret } from \"../secret-store.js\";\nimport { deriveEventId, hasHostIdentity } from \"../capture/event-id.js\";\nimport {\n appendSpool,\n drainInProgress,\n drainSpool,\n parseSentMark,\n reconcileSent,\n retainSent,\n spoolHasRecords,\n type SendOutcome,\n type SentMark,\n type SpoolRecord,\n} from \"../capture/spool.js\";\n\nexport { stripPrivateData } from \"../functions/privacy.js\";\n\nexport const REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = resolveClientSecret(REST_URL);\nconst DRAIN_CHILD_ENV = \"AGENTMEMORY_CAPTURE_DRAIN_CHILD\";\nconst DRAIN_MAX_RECORDS = 500;\nconst DRAIN_DEADLINE_MS = 20_000;\nconst DRAIN_REQUEST_TIMEOUT_MS = 5000;\n\nexport type CaptureOutcome = \"delivered\" | \"duplicate\" | \"rejected\" | \"spooled\" | \"dropped\";\n\nexport interface ObserveBody {\n hookType: string;\n sessionId: string;\n project: string;\n cwd: string;\n timestamp: string;\n data: unknown;\n eventId?: string;\n}\n\nexport function 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\nexport function withEventId(\n body: ObserveBody,\n host: Record<string, unknown>,\n content?: unknown,\n options: { stable?: boolean } = {},\n): ObserveBody {\n const source = options.stable || hasHostIdentity(host) ? host : { ...host, timestamp: body.timestamp };\n return {\n ...body,\n eventId: deriveEventId(body.hookType, body.sessionId, source, content ?? body.data),\n };\n}\n\nfunction classify(status: number): SendOutcome {\n if (status >= 200 && status < 300) return status === 200 ? \"duplicate\" : \"delivered\";\n if (status === 408 || status === 429 || status >= 500) return \"retry\";\n return \"rejected\";\n}\n\nasync function post(body: Record<string, unknown>, timeoutMs: number): Promise<{ outcome: SendOutcome; mark: SentMark | null }> {\n const res = await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n const text = await res.text().catch(() => \"\");\n const outcome = classify(res.status);\n if (outcome !== \"delivered\" && outcome !== \"duplicate\") return { outcome, mark: null };\n try {\n return { outcome, mark: parseSentMark(JSON.parse(text)) };\n } catch {\n return { outcome, mark: null };\n }\n}\n\nfunction keepUntilDurable(eventId: string, body: Record<string, unknown>, mark: SentMark): number {\n const { requeued } = reconcileSent(REST_URL, mark.bootId);\n retainSent(REST_URL, eventId, body, mark);\n return requeued;\n}\n\nfunction startDrainChild(): void {\n if (process.env[DRAIN_CHILD_ENV] === \"1\") return;\n if (!process.argv[1] || drainInProgress(REST_URL)) return;\n try {\n const child = spawn(process.execPath, [process.argv[1]], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n env: { ...process.env, [DRAIN_CHILD_ENV]: \"1\" },\n });\n child.on(\"error\", () => {});\n child.unref();\n } catch {}\n}\n\nexport async function captureObservation(body: ObserveBody, timeoutMs: number): Promise<CaptureOutcome> {\n const eventId = body.eventId ?? deriveEventId(body.hookType, body.sessionId, null, body.data);\n const payload = { ...body, eventId } as unknown as Record<string, unknown>;\n let reason: string;\n try {\n const { outcome, mark } = await post(payload, timeoutMs);\n if (outcome !== \"retry\") {\n const requeued = mark ? keepUntilDurable(eventId, payload, mark) : 0;\n if (requeued > 0 || spoolHasRecords(REST_URL)) startDrainChild();\n return outcome;\n }\n reason = \"server-error\";\n } catch (err) {\n const name = err instanceof Error ? err.name : \"\";\n reason = name === \"TimeoutError\" || name === \"AbortError\" ? \"timeout\" : \"unreachable\";\n }\n return appendSpool(REST_URL, eventId, payload, reason).spooled ? \"spooled\" : \"dropped\";\n}\n\nexport function isDrainChild(): boolean {\n return process.env[DRAIN_CHILD_ENV] === \"1\";\n}\n\nexport async function runDrainChild(): Promise<void> {\n await drainSpool(\n REST_URL,\n async (record: SpoolRecord) => {\n const { outcome, mark } = await post({ ...record.body, eventId: record.eventId }, DRAIN_REQUEST_TIMEOUT_MS);\n if (mark) retainSent(REST_URL, record.eventId, record.body, mark);\n return outcome;\n },\n { maxRecords: DRAIN_MAX_RECORDS, deadlineMs: DRAIN_DEADLINE_MS },\n ).catch(() => undefined);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_capture.mjs","names":[],"sources":["../../src/secret-store.ts","../../src/hooks/_endpoint.ts","../../src/capture/event-id.ts","../../src/cli-data-dir.ts","../../src/functions/privacy.ts","../../src/capture/spool.ts","../../src/hooks/_capture.ts"],"sourcesContent":["import {\n chmodSync,\n closeSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst SECRET_KEY = \"AGENTMEMORY_SECRET\";\n\nexport function agentmemoryHomeDir(): string {\n return process.env[\"AGENTMEMORY_DATA_DIR\"] || join(homedir(), \".agentmemory\");\n}\n\nexport function secretFilePath(): string {\n return join(agentmemoryHomeDir(), \"secret\");\n}\n\nfunction usable(value: string | undefined): string {\n if (typeof value !== \"string\") return \"\";\n const trimmed = value.trim();\n if (!trimmed) return \"\";\n if (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\")) return \"\";\n return trimmed;\n}\n\nfunction unquote(value: string): string {\n const quote = value[0];\n if ((quote === '\"' || quote === \"'\") && value.length > 1) {\n const close = value.indexOf(quote, 1);\n if (close !== -1) return value.slice(1, close);\n }\n const hash = value.indexOf(\" #\");\n return hash === -1 ? value : value.slice(0, hash).trim();\n}\n\nexport function readEnvFileSecret(): string {\n let content: unknown;\n try {\n content = readFileSync(process.env[\"AGENTMEMORY_ENV_FILE\"] || join(homedir(), \".agentmemory\", \".env\"), \"utf-8\");\n } catch {\n return \"\";\n }\n if (typeof content !== \"string\") return \"\";\n let found = \"\";\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) continue;\n const key = trimmed.slice(0, eq).replace(/^export\\s+/, \"\").trim();\n if (key !== SECRET_KEY) continue;\n found = usable(unquote(trimmed.slice(eq + 1).trim()));\n }\n return found;\n}\n\nfunction readSecretFile(path: string): string {\n try {\n return usable(readFileSync(path, \"utf-8\"));\n } catch {\n return \"\";\n }\n}\n\nfunction legacyDataDirSecretPaths(env: NodeJS.ProcessEnv = process.env): string[] {\n const dataDir = env[\"AGENTMEMORY_DATA_DIR\"];\n if (!dataDir) return [];\n const paths = [join(dataDir, \"secret\"), join(dataDir, \".hmac\"), join(dataDir, \"..\", \".hmac\")];\n return [...new Set(paths.filter((path) => path !== secretFilePath()))];\n}\n\nexport function readStoredSecret(env: NodeJS.ProcessEnv = process.env): string {\n const canonicalPath = canonicalSecretPath();\n const localPath = secretFilePath();\n const legacyPaths = legacyDataDirSecretPaths(env);\n return readSecretFile(canonicalPath) ||\n (localPath === canonicalPath ? \"\" : readSecretFile(localPath)) ||\n legacyPaths.map(readSecretFile).find(Boolean) || \"\";\n}\n\nexport function explicitSecret(env: NodeJS.ProcessEnv = process.env): string {\n return usable(env[SECRET_KEY]) || readEnvFileSecret();\n}\n\nexport function isLoopbackUrl(url: string): boolean {\n let hostname: string;\n try {\n hostname = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n const bare = hostname.replace(/^\\[|\\]$/g, \"\");\n return bare === \"localhost\" || bare === \"::1\" || /^127(?:\\.\\d{1,3}){3}$/.test(bare);\n}\n\nexport function resolveClientSecret(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): string {\n const fromEnv = usable(env[SECRET_KEY]);\n if (fromEnv) return fromEnv;\n if (env.AGENTMEMORY_CREDENTIAL_MODE === \"explicit\") return \"\";\n if (!isLoopbackUrl(baseUrl)) return \"\";\n return readEnvFileSecret() || readStoredSecret(env);\n}\n\nfunction canonicalSecretPath(): string {\n return join(homedir(), \".agentmemory\", \"secret\");\n}\n\nfunction writeSecretFile(path: string, secret: string): void {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n renameSync(staging, path);\n chmodSync(path, 0o600);\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n}\n\nfunction writeSecretIfAbsent(path: string, secret: string): string {\n mkdirSync(join(path, \"..\"), { recursive: true, mode: 0o700 });\n const staging = `${path}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`;\n const fd = openSync(staging, \"wx\", 0o600);\n try {\n writeSync(fd, `${secret}\\n`);\n } finally {\n closeSync(fd);\n }\n try {\n chmodSync(staging, 0o600);\n linkSync(staging, path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n } finally {\n try { unlinkSync(staging); } catch {}\n }\n const stored = readSecretFile(path);\n if (!stored) throw new Error(`Could not read stored API secret at ${path}.`);\n return stored;\n}\n\nexport function ensureServerSecret(env: NodeJS.ProcessEnv = process.env): {\n secret: string;\n source: \"env\" | \"file\" | \"generated\";\n} {\n const configured = explicitSecret(env);\n if (configured) return { secret: configured, source: \"env\" };\n\n const localPath = secretFilePath();\n const canonicalPath = canonicalSecretPath();\n const canonical = localPath === canonicalPath ? \"\" : readSecretFile(canonicalPath);\n if (canonical) {\n if (localPath !== canonicalPath && readSecretFile(localPath) !== canonical) {\n writeSecretFile(localPath, canonical);\n }\n return { secret: canonical, source: \"file\" };\n }\n\n const legacy = readSecretFile(localPath) || legacyDataDirSecretPaths(env).map(readSecretFile).find(Boolean) || \"\";\n const candidate = legacy || randomBytes(32).toString(\"hex\");\n const localSecret = writeSecretIfAbsent(localPath, candidate);\n const secret = localPath === canonicalPath\n ? localSecret\n : writeSecretIfAbsent(canonicalPath, localSecret);\n if (localPath !== canonicalPath && localSecret !== secret) writeSecretFile(localPath, secret);\n return { secret, source: legacy ? \"file\" : \"generated\" };\n}\n\nexport function bearerHeaders(\n baseUrl: string,\n env: NodeJS.ProcessEnv = process.env,\n): Record<string, string> {\n const secret = resolveClientSecret(baseUrl, env);\n return secret ? { Authorization: `Bearer ${secret}` } : {};\n}\n","import { isLoopbackUrl, resolveClientSecret } from \"../secret-store.js\";\n\nconst OPTION_PREFIX = \"CLAUDE_PLUGIN_OPTION_\";\nconst DEFAULT_URL = \"http://localhost:3111\";\n\nexport interface HookEndpoint {\n url: string;\n secret: string;\n blocked: boolean;\n}\n\nfunction filled(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed || (trimmed.startsWith(\"${\") && trimmed.endsWith(\"}\"))) return undefined;\n return value;\n}\n\nexport function hookSetting(\n name: string,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n const optionKey = `${OPTION_PREFIX}${name}`;\n const option = filled(env[optionKey])?.trim();\n if (option) return option;\n if (name === \"AGENTMEMORY_SECRET\" && optionKey in env) return undefined;\n return filled(env[name]);\n}\n\nexport function resolveSettingSecret(url: string, env: NodeJS.ProcessEnv = process.env): string {\n const secretEnv: NodeJS.ProcessEnv = { ...env };\n const explicit = hookSetting(\"AGENTMEMORY_SECRET\", env);\n if (explicit === undefined) delete secretEnv[\"AGENTMEMORY_SECRET\"];\n else secretEnv[\"AGENTMEMORY_SECRET\"] = explicit;\n return resolveClientSecret(url, secretEnv);\n}\n\nexport function secretTransportAllowed(url: string): boolean {\n try {\n return new URL(url).protocol === \"https:\" || isLoopbackUrl(url);\n } catch {\n return false;\n }\n}\n\nexport function resolveHookEndpoint(env: NodeJS.ProcessEnv = process.env): HookEndpoint {\n const url = hookSetting(\"AGENTMEMORY_URL\", env) || DEFAULT_URL;\n const secret = resolveSettingSecret(url, env);\n const blocked = !secretTransportAllowed(url);\n return { url, secret: blocked ? \"\" : secret, blocked };\n}\n\nlet blockedNoticeShown = false;\n\nexport function reportBlockedTransport(endpoint: HookEndpoint): boolean {\n if (!endpoint.blocked) return false;\n if (!blockedNoticeShown) {\n blockedNoticeShown = true;\n process.stderr.write(\n \"[agentmemory] AGENTMEMORY_URL must use HTTPS unless it is a loopback address. Nothing was sent.\\n\",\n );\n }\n return true;\n}\n\nexport function hookAuthHeaders(endpoint: HookEndpoint): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (endpoint.secret) h[\"Authorization\"] = `Bearer ${endpoint.secret}`;\n return h;\n}\n","import { createHash } from \"node:crypto\";\n\nexport const EVENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_.:-]{7,127}$/;\n\nconst HOST_ID_FIELDS = [\n \"event_id\",\n \"eventId\",\n \"tool_use_id\",\n \"toolUseId\",\n \"tool_call_id\",\n \"toolCallId\",\n \"call_id\",\n \"callId\",\n \"prompt_id\",\n \"promptId\",\n \"message_id\",\n \"messageId\",\n];\n\nconst HOST_TIME_FIELDS = [\"timestamp\", \"ts\", \"created_at\", \"createdAt\", \"event_time\"];\n\nexport function isValidEventId(value: unknown): value is string {\n return typeof value === \"string\" && EVENT_ID_RE.test(value);\n}\n\nexport function stableStringify(value: unknown): string {\n if (value === undefined) return \"null\";\n if (value === null || typeof value !== \"object\") {\n const encoded = JSON.stringify(value);\n return encoded === undefined ? \"null\" : encoded;\n }\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort();\n return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(\",\")}}`;\n}\n\nfunction hostScalar(host: Record<string, unknown>, fields: string[]): string | undefined {\n for (const field of fields) {\n const v = host[field];\n if (typeof v === \"string\" && v.trim()) return v.trim();\n if (typeof v === \"number\" && Number.isFinite(v)) return String(v);\n }\n return undefined;\n}\n\nfunction digest(parts: string[]): string {\n return createHash(\"sha256\").update(parts.join(\"\\u0000\")).digest(\"hex\").slice(0, 32);\n}\n\nexport function hasHostIdentity(host: Record<string, unknown> | null | undefined): boolean {\n const source = host && typeof host === \"object\" ? host : {};\n return hostScalar(source, HOST_ID_FIELDS) !== undefined || hostScalar(source, HOST_TIME_FIELDS) !== undefined;\n}\n\nexport function deriveEventId(\n hookType: string,\n sessionId: string,\n host: Record<string, unknown> | null | undefined,\n content: unknown,\n): string {\n const source = host && typeof host === \"object\" ? host : {};\n const hostId = hostScalar(source, HOST_ID_FIELDS);\n if (hostId) return `evh_${digest([\"host\", sessionId, hookType, hostId])}`;\n const hostTime = hostScalar(source, HOST_TIME_FIELDS) ?? \"\";\n return `evc_${digest([\"content\", sessionId, hookType, hostTime, stableStringify(content)])}`;\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\nexport type DataDirSource = \"flag\" | \"env\" | \"default\";\n\nexport type ResolvedDataDir = {\n dataDir: string;\n source: DataDirSource;\n relocatedFrom?: string;\n};\n\ntype ResolveDataDirOptions = {\n args?: string[];\n env?: NodeJS.ProcessEnv;\n cwd?: string;\n home?: string;\n platform?: NodeJS.Platform;\n};\n\nfunction argValue(args: string[], name: string): string | undefined {\n const equals = `${name}=`;\n const inline = args.find((arg) => arg.startsWith(equals));\n if (inline) return inline.slice(equals.length);\n\n const idx = args.indexOf(name);\n if (idx !== -1) return args[idx + 1];\n return undefined;\n}\n\nfunction expandHome(pathValue: string, home: string): string {\n if (pathValue === \"~\") return home;\n if (pathValue.startsWith(\"~/\") || pathValue.startsWith(\"~\\\\\")) {\n return join(home, pathValue.slice(2));\n }\n return pathValue;\n}\n\nfunction absoluteDataDir(pathValue: string, cwd: string, home: string): string {\n const expanded = expandHome(pathValue, home);\n return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);\n}\n\nfunction platformDefaultDataDir(\n env: NodeJS.ProcessEnv,\n home: string,\n nodePlatform: NodeJS.Platform,\n useXdg = true,\n): string {\n if (nodePlatform === \"darwin\") {\n return join(home, \"Library\", \"Application Support\", \"agentmemory\");\n }\n if (nodePlatform === \"win32\") {\n const appData = env[\"APPDATA\"];\n return appData\n ? join(appData, \"agentmemory\")\n : join(home, \".agentmemory\");\n }\n const xdgDataHome = env[\"XDG_DATA_HOME\"];\n if (useXdg && xdgDataHome && isAbsolute(xdgDataHome)) {\n return join(xdgDataHome, \"agentmemory\");\n }\n return join(home, \".local\", \"share\", \"agentmemory\");\n}\n\nfunction nearestGitParent(pathValue: string): string | undefined {\n let current = pathValue;\n while (true) {\n if (existsSync(join(current, \".git\"))) return current;\n const parent = dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n const pathFromParent = relative(parent, child);\n return (\n pathFromParent === \"\" ||\n (pathFromParent !== \"..\" &&\n !pathFromParent.startsWith(`..${sep}`) &&\n !isAbsolute(pathFromParent))\n );\n}\n\nexport function resolveDataDir(options: ResolveDataDirOptions = {}): ResolvedDataDir {\n const args = options.args ?? process.argv.slice(2);\n const env = options.env ?? process.env;\n const cwd = options.cwd ?? process.cwd();\n const home = options.home ?? homedir();\n const nodePlatform = options.platform ?? platform();\n const parsedInstance = parseInt(argValue(args, \"--instance\") ?? \"0\", 10);\n const instance =\n parsedInstance > 0 && parsedInstance <= 50 ? parsedInstance : 0;\n const forInstance = (dataDir: string) =>\n instance ? join(dataDir, `instance-${instance}`) : dataDir;\n\n const flagValue = argValue(args, \"--data-dir\");\n if (flagValue) {\n return {\n dataDir: forInstance(absoluteDataDir(flagValue, cwd, home)),\n source: \"flag\",\n };\n }\n\n const envValue = env[\"AGENTMEMORY_DATA_DIR\"];\n if (envValue) {\n return {\n dataDir: forInstance(absoluteDataDir(envValue, cwd, home)),\n source: \"env\",\n };\n }\n\n const legacyDir = resolve(cwd, \"data\");\n const hasLegacyStore =\n existsSync(join(legacyDir, \"state_store.db\")) ||\n existsSync(join(legacyDir, \"iii-config.yaml\"));\n if (instance === 0 && hasLegacyStore) {\n return { dataDir: legacyDir, source: \"default\" };\n }\n\n const defaultDir = platformDefaultDataDir(env, home, nodePlatform);\n const gitParent = nearestGitParent(cwd);\n if (gitParent && isWithin(gitParent, defaultDir)) {\n const relocated = platformDefaultDataDir(env, home, nodePlatform, false);\n if (relocated !== defaultDir) {\n return {\n dataDir: forInstance(relocated),\n source: \"default\",\n relocatedFrom: defaultDir,\n };\n }\n }\n\n return {\n dataDir: forInstance(defaultDir),\n source: \"default\",\n };\n}\n","import type { IIIClient } from \"iii-sdk\";\n\nconst PRIVATE_TAG_TOKEN_RE = /<private(?=\\s|>)[^>]*>|<\\/private\\s*>/gi;\n\nfunction redactPrivateBlocks(input: string): string {\n const tokens = new RegExp(PRIVATE_TAG_TOKEN_RE.source, PRIVATE_TAG_TOKEN_RE.flags);\n let out = \"\";\n let cursor = 0;\n let depth = 0;\n for (let match = tokens.exec(input); match; match = tokens.exec(input)) {\n const closing = match[0].startsWith(\"</\");\n if (depth === 0) {\n if (closing) continue;\n out += input.slice(cursor, match.index);\n depth = 1;\n } else if (closing) {\n depth -= 1;\n if (depth === 0) {\n out += \"[REDACTED]\";\n cursor = match.index + match[0].length;\n }\n } else {\n depth += 1;\n }\n }\n if (depth > 0) return `${out}[REDACTED]`;\n return out + input.slice(cursor);\n}\n\nconst SECRET_PATTERN_SOURCES = [\n /(?:api[_-]?key|secret|token|password|credential|auth)[\\s]*[=:]\\s*[\"']?[A-Za-z0-9_\\-/.+]{20,}[\"']?/gi,\n /Bearer\\s+[A-Za-z0-9._\\-+/=]{20,}/gi,\n /(?<![\\p{L}\\p{N}\\p{M}_])sk-proj-[A-Za-z0-9\\-_]{20,}/gu,\n /(?<![\\p{L}\\p{N}\\p{M}_])(?:sk|pk|rk|ak)-[A-Za-z0-9][A-Za-z0-9\\-_]{19,}/gu,\n /(?<![\\p{L}\\p{N}\\p{M}_])sk-ant-[A-Za-z0-9\\-_]{20,}/gu,\n /gh[pus]_[A-Za-z0-9]{36,}/g,\n /github_pat_[A-Za-z0-9_]{22,}/g,\n /xoxb-[A-Za-z0-9\\-]+/g,\n /AKIA[0-9A-Z]{16}/g,\n /AIza[A-Za-z0-9\\-_]{35}/g,\n /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g,\n /npm_[A-Za-z0-9]{36}/g,\n /glpat-[A-Za-z0-9\\-_]{20,}/g,\n /dop_v1_[A-Za-z0-9]{64}/g,\n];\n\nconst PRIVATE_KEY_BLOCK_RE =\n /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\\s\\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g;\n\nconst URL_CREDENTIALS_RE =\n /\\b([a-z][a-z0-9+.-]*:\\/\\/)[^\\s/?#@:\"'<>]*:[^\\s/?#@\"'<>]+@/gi;\n\nexport function stripPrivateData(input: string): string {\n let result = redactPrivateBlocks(input);\n result = result.replace(new RegExp(PRIVATE_KEY_BLOCK_RE.source, PRIVATE_KEY_BLOCK_RE.flags), \"[REDACTED_SECRET]\");\n result = result.replace(new RegExp(URL_CREDENTIALS_RE.source, URL_CREDENTIALS_RE.flags), \"$1[REDACTED_SECRET]@\");\n for (const source of SECRET_PATTERN_SOURCES) {\n const pattern = new RegExp(source.source, source.flags);\n result = result.replace(pattern, \"[REDACTED_SECRET]\");\n }\n return result;\n}\n\nexport function scrubRecord<T>(value: T, options: { scrubKeys?: boolean } = {}): T {\n if (typeof value === \"string\") return stripPrivateData(value) as T;\n if (Array.isArray(value)) return value.map((item) => scrubRecord(item, options)) as T;\n if (value !== null && typeof value === \"object\") {\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n const entries = Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n options.scrubKeys ? stripPrivateData(key) : key,\n item,\n ] as const);\n const reserved = new Set(entries.map(([key]) => key));\n const used = new Set<string>();\n return Object.fromEntries(\n entries.map(([key, item]) => {\n let unique = key;\n if (used.has(unique)) {\n let suffix = 2;\n do { unique = `${key}_${suffix++}`; } while (reserved.has(unique) || used.has(unique));\n }\n used.add(unique);\n return [unique, scrubRecord(item, options)];\n }),\n ) as T;\n }\n return value;\n}\n\nexport const SCRUBBED_WRITE_FUNCTIONS: ReadonlySet<string> = new Set([\n \"mem::remember\",\n \"mem::evolve\",\n \"mem::slot-create\",\n \"mem::slot-append\",\n \"mem::slot-replace\",\n \"mem::lesson-save\",\n \"mem::action-create\",\n \"mem::action-update\",\n \"mem::sketch-create\",\n \"mem::sketch-add\",\n \"mem::import\",\n \"mem::mesh-receive\",\n \"mem::team-share\",\n \"mem::signal-send\",\n \"mem::routine-create\",\n \"mem::checkpoint-create\",\n \"mem::checkpoint-resolve\",\n \"mem::sentinel-create\",\n \"mem::sentinel-trigger\",\n \"mem::core-add\",\n \"mem::facet-tag\",\n]);\n\ntype RegisterFunction = (id: string, handler: unknown, ...rest: unknown[]) => unknown;\n\nexport function withWriteScrubbing<T extends object>(sdk: T): T {\n return new Proxy(sdk, {\n get(target, prop) {\n const value = Reflect.get(target, prop, target);\n if (typeof value !== \"function\") return value;\n if (prop !== \"registerFunction\") return value.bind(target);\n const register = value as RegisterFunction;\n return (id: string, handler: unknown, ...rest: unknown[]) => {\n if (!SCRUBBED_WRITE_FUNCTIONS.has(id) || typeof handler !== \"function\") {\n return register.call(target, id, handler, ...rest);\n }\n const original = handler as (data: unknown, ...args: unknown[]) => unknown;\n const scrubbed = (data: unknown, ...args: unknown[]) => original(scrubRecord(data), ...args);\n return register.call(target, id, scrubbed, ...rest);\n };\n },\n });\n}\n\nexport function registerPrivacyFunction(sdk: IIIClient): void {\n sdk.registerFunction(\"mem::privacy\", \n async (data: { input?: unknown } | undefined) => {\n if (!data || typeof data.input !== \"string\") {\n return { output: \"\", error: \"invalid input: expected string field 'input'\" };\n }\n return { output: stripPrivateData(data.input) };\n },\n );\n}\n","import {\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n unlinkSync,\n utimesSync,\n writeFileSync,\n writeSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { resolveDataDir } from \"../cli-data-dir.js\";\nimport { scrubRecord } from \"../functions/privacy.js\";\n\nexport interface SpoolRecord {\n v: 1;\n eventId: string;\n spooledAt: string;\n reason: string;\n attempts: number;\n body: Record<string, unknown>;\n bootId?: string;\n}\n\nexport interface SentMark {\n bootId: string;\n durableAfterMs: number;\n}\n\nexport interface SentReconcile {\n released: number;\n requeued: number;\n}\n\nexport interface SpoolPolicy {\n enabled: boolean;\n maxBytes: number;\n maxAgeMs: number;\n maxRecordBytes: number;\n}\n\nexport interface SpoolStats {\n spooled: number;\n dropped: number;\n droppedBytes: number;\n expired: number;\n delivered: number;\n duplicates: number;\n rejected: number;\n lastSpooledAt?: string;\n lastSpoolReason?: string;\n lastDropAt?: string;\n lastDropReason?: string;\n lastDrainAt?: string;\n lastDrainDelivered?: number;\n lastDrainDuplicates?: number;\n lastDrainRejected?: number;\n lastDrainRemaining?: number;\n lastDrainError?: string;\n}\n\nexport interface SpoolPaths {\n dir: string;\n name: string;\n file: string;\n lock: string;\n drainLock: string;\n stats: string;\n}\n\nexport interface SpoolSummary {\n enabled: boolean;\n path: string;\n records: number;\n bytes: number;\n oldestAt: string | null;\n retained: number;\n retainedBytes: number;\n maxBytes: number;\n maxAgeHours: number;\n stats: SpoolStats;\n}\n\nexport type SendOutcome = \"delivered\" | \"duplicate\" | \"rejected\" | \"retry\";\n\nexport interface DrainResult {\n claimed: number;\n delivered: number;\n duplicates: number;\n rejected: number;\n expired: number;\n corrupt: number;\n remaining: number;\n error?: string;\n skipped?: \"locked\" | \"empty\" | \"disabled\";\n}\n\nconst DEFAULT_MAX_BYTES = 5 * 1024 * 1024;\nconst MIN_MAX_BYTES = 64 * 1024;\nconst DEFAULT_MAX_AGE_HOURS = 168;\nconst LOCK_STALE_MS = 10_000;\nconst DRAIN_LOCK_STALE_MS = 120_000;\nconst ORPHAN_CLAIM_MS = 120_000;\nconst LOCK_WAIT_MS = 500;\nconst IMAGE_PLACEHOLDER = \"[image dropped from capture spool]\";\nconst BOOT_ID_RE = /^[A-Za-z0-9]{8,64}$/;\nconst SENT_FILE_RE = /^([A-Za-z0-9]{8,64})-(\\d+)\\.jsonl$/;\nconst SENT_BUCKET_MS = 1000;\n// Cover the maximum one-hour state flush interval plus its safety margin.\nconst MAX_DURABLE_AFTER_MS = 61 * 60_000;\n\nfunction emptyStats(): SpoolStats {\n return { spooled: 0, dropped: 0, droppedBytes: 0, expired: 0, delivered: 0, duplicates: 0, rejected: 0 };\n}\n\nfunction positiveInt(raw: string | undefined, fallback: number): number {\n if (!raw || !/^\\d+$/.test(raw.trim())) return fallback;\n const n = parseInt(raw.trim(), 10);\n return n > 0 ? n : fallback;\n}\n\nexport function spoolPolicy(env: NodeJS.ProcessEnv = process.env): SpoolPolicy {\n const maxBytes = Math.max(MIN_MAX_BYTES, positiveInt(env[\"AGENTMEMORY_CAPTURE_SPOOL_MAX_BYTES\"], DEFAULT_MAX_BYTES));\n const maxAgeHours = positiveInt(env[\"AGENTMEMORY_CAPTURE_SPOOL_MAX_AGE_HOURS\"], DEFAULT_MAX_AGE_HOURS);\n return {\n enabled: env[\"AGENTMEMORY_CAPTURE_SPOOL\"] !== \"false\",\n maxBytes,\n maxAgeMs: maxAgeHours * 3600_000,\n maxRecordBytes: Math.min(256 * 1024, Math.floor(maxBytes / 4)),\n };\n}\n\nexport function spoolDir(env: NodeJS.ProcessEnv = process.env): string {\n const explicit = env[\"AGENTMEMORY_CAPTURE_SPOOL_DIR\"];\n if (explicit && explicit.trim()) return resolve(explicit.trim());\n return join(resolveDataDir({ args: [], env }).dataDir, \"capture-spool\");\n}\n\nconst LOOPBACK = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\", \"0.0.0.0\"]);\n\nexport function spoolTargetName(url: string): string {\n try {\n const u = new URL(url);\n const host = LOOPBACK.has(u.hostname) ? \"local\" : u.hostname.toLowerCase().replace(/[^a-z0-9.-]/g, \"_\");\n const port = u.port || (u.protocol === \"https:\" ? \"443\" : \"80\");\n return `${host}-${port}`;\n } catch {\n return \"local-3111\";\n }\n}\n\nexport function spoolPaths(url: string, dir: string = spoolDir()): SpoolPaths {\n const name = spoolTargetName(url);\n return {\n dir,\n name,\n file: join(dir, `${name}.jsonl`),\n lock: join(dir, `${name}.lock`),\n drainLock: join(dir, `${name}.drain.lock`),\n stats: join(dir, `${name}.stats.json`),\n };\n}\n\nfunction ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n try {\n chmodSync(dir, 0o700);\n } catch {}\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n}\n\nfunction tryLock(path: string, staleMs: number): boolean {\n try {\n const fd = openSync(path, \"wx\", 0o600);\n writeSync(fd, `${process.pid}\\n`);\n closeSync(fd);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") return false;\n try {\n if (Date.now() - statSync(path).mtimeMs > staleMs) unlinkSync(path);\n } catch {}\n return false;\n }\n}\n\nfunction releaseLock(path: string): void {\n try {\n unlinkSync(path);\n } catch {}\n}\n\nfunction withLock<T>(path: string, fn: (locked: boolean) => T, waitMs = LOCK_WAIT_MS): T {\n const deadline = Date.now() + waitMs;\n let locked = tryLock(path, LOCK_STALE_MS);\n while (!locked && Date.now() < deadline) {\n sleepSync(5);\n locked = tryLock(path, LOCK_STALE_MS);\n }\n try {\n return fn(locked);\n } finally {\n if (locked) releaseLock(path);\n }\n}\n\nexport function readSpoolStats(paths: SpoolPaths): SpoolStats {\n try {\n const parsed = JSON.parse(readFileSync(paths.stats, \"utf-8\")) as Partial<SpoolStats>;\n return { ...emptyStats(), ...parsed };\n } catch {\n return emptyStats();\n }\n}\n\nfunction writeStats(paths: SpoolPaths, patch: (stats: SpoolStats) => void): void {\n try {\n const stats = readSpoolStats(paths);\n patch(stats);\n const tmp = `${paths.stats}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(stats), { mode: 0o600 });\n renameSync(tmp, paths.stats);\n } catch {}\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nfunction withoutImage(body: Record<string, unknown>): Record<string, unknown> {\n const data = body[\"data\"];\n if (!data || typeof data !== \"object\" || Array.isArray(data)) return body;\n const d = data as Record<string, unknown>;\n if (d[\"image_data\"] === undefined) return body;\n return { ...body, data: { ...d, image_data: IMAGE_PLACEHOLDER } };\n}\n\nfunction parseLines(text: string): { records: SpoolRecord[]; corrupt: number } {\n const records: SpoolRecord[] = [];\n let corrupt = 0;\n for (const line of text.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const rec = JSON.parse(line) as SpoolRecord;\n if (rec && typeof rec.eventId === \"string\" && rec.body && typeof rec.body === \"object\") records.push(rec);\n else corrupt++;\n } catch {\n corrupt++;\n }\n }\n return { records, corrupt };\n}\n\nfunction isExpired(rec: SpoolRecord, policy: SpoolPolicy, now: number): boolean {\n const at = Date.parse(rec.spooledAt);\n return Number.isFinite(at) && now - at > policy.maxAgeMs;\n}\n\nfunction serialize(records: SpoolRecord[]): string {\n return records.map((r) => JSON.stringify(r)).join(\"\\n\") + (records.length ? \"\\n\" : \"\");\n}\n\nfunction rewriteLocked(paths: SpoolPaths, records: SpoolRecord[]): void {\n const tmp = `${paths.file}.${process.pid}.tmp`;\n const fd = openSync(tmp, \"w\", 0o600);\n try {\n writeSync(fd, serialize(records));\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n renameSync(tmp, paths.file);\n}\n\nfunction pruneExpiredLocked(paths: SpoolPaths, policy: SpoolPolicy): number {\n if (!existsSync(paths.file)) return 0;\n const { records } = parseLines(readFileSync(paths.file, \"utf-8\"));\n const now = Date.now();\n const kept = records.filter((r) => !isExpired(r, policy, now));\n const expired = records.length - kept.length;\n if (expired > 0) rewriteLocked(paths, kept);\n return expired;\n}\n\nexport interface AppendResult {\n spooled: boolean;\n dropped?: \"disabled\" | \"too-large\" | \"full\" | \"error\";\n}\n\nexport function appendSpool(\n url: string,\n eventId: string,\n body: Record<string, unknown>,\n reason: string,\n options: { policy?: SpoolPolicy; dir?: string; attempts?: number } = {},\n): AppendResult {\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled) return { spooled: false, dropped: \"disabled\" };\n const paths = spoolPaths(url, options.dir);\n const note = (dropReason: NonNullable<AppendResult[\"dropped\"]>, bytes: number) => {\n writeStats(paths, (s) => {\n s.dropped++;\n s.droppedBytes += bytes;\n s.lastDropAt = new Date().toISOString();\n s.lastDropReason = dropReason;\n });\n return { spooled: false, dropped: dropReason } as AppendResult;\n };\n try {\n ensureDir(paths.dir);\n const record: SpoolRecord = {\n v: 1,\n eventId,\n spooledAt: new Date().toISOString(),\n reason,\n attempts: options.attempts ?? 0,\n body: scrubRecord(JSON.parse(JSON.stringify(body)), { scrubKeys: true }),\n };\n let line = JSON.stringify(record) + \"\\n\";\n if (Buffer.byteLength(line) > policy.maxRecordBytes) {\n record.body = withoutImage(record.body);\n line = JSON.stringify(record) + \"\\n\";\n }\n const bytes = Buffer.byteLength(line);\n if (bytes > policy.maxRecordBytes) return note(\"too-large\", bytes);\n return withLock(paths.lock, (locked) => {\n let expired = 0;\n if (locked && fileSize(paths.file) + bytes > policy.maxBytes) expired = pruneExpiredLocked(paths, policy);\n const over = fileSize(paths.file) + sentBytes(paths) + bytes - policy.maxBytes;\n if (locked && over > 0) evictSent(paths, over);\n if (fileSize(paths.file) + sentBytes(paths) + bytes > policy.maxBytes) {\n if (expired) writeStats(paths, (s) => void (s.expired += expired));\n return note(\"full\", bytes);\n }\n const fd = openSync(paths.file, \"a\", 0o600);\n try {\n writeSync(fd, line);\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n writeStats(paths, (s) => {\n s.spooled++;\n s.expired += expired;\n s.lastSpooledAt = record.spooledAt;\n s.lastSpoolReason = reason;\n });\n return { spooled: true } as AppendResult;\n });\n } catch {\n return { spooled: false, dropped: \"error\" };\n }\n}\n\ninterface SentSegment {\n file: string;\n bootId: string;\n dueAt: number;\n bytes: number;\n}\n\nfunction listSent(paths: SpoolPaths): SentSegment[] {\n const segments: SentSegment[] = [];\n let entries: string[];\n try {\n entries = readdirSync(paths.dir);\n } catch {\n return segments;\n }\n const prefix = `${paths.name}.sent-`;\n for (const entry of entries) {\n if (!entry.startsWith(prefix)) continue;\n const match = entry.slice(prefix.length).match(SENT_FILE_RE);\n if (!match) continue;\n const file = join(paths.dir, entry);\n segments.push({ file, bootId: match[1]!, dueAt: Number(match[2]), bytes: fileSize(file) });\n }\n return segments;\n}\n\nfunction sentBytes(paths: SpoolPaths): number {\n return listSent(paths).reduce((n, s) => n + s.bytes, 0);\n}\n\nfunction evictSent(paths: SpoolPaths, need: number): void {\n let freed = 0;\n for (const seg of listSent(paths).sort((a, b) => a.dueAt - b.dueAt)) {\n if (freed >= need) return;\n try {\n unlinkSync(seg.file);\n freed += seg.bytes;\n } catch {}\n }\n}\n\nexport function parseSentMark(body: unknown): SentMark | null {\n if (!body || typeof body !== \"object\" || Array.isArray(body)) return null;\n const bootId = (body as Record<string, unknown>)[\"bootId\"];\n const after = (body as Record<string, unknown>)[\"durableAfterMs\"];\n if (typeof bootId !== \"string\" || !BOOT_ID_RE.test(bootId)) return null;\n if (typeof after !== \"number\" || !Number.isFinite(after) || after < 0) return null;\n return { bootId, durableAfterMs: Math.min(Math.ceil(after), MAX_DURABLE_AFTER_MS) };\n}\n\nexport function retainSent(\n url: string,\n eventId: string,\n body: Record<string, unknown>,\n mark: SentMark,\n options: { policy?: SpoolPolicy; dir?: string; now?: number } = {},\n): boolean {\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled || !BOOT_ID_RE.test(mark.bootId)) return false;\n const paths = spoolPaths(url, options.dir);\n const now = options.now ?? Date.now();\n try {\n ensureDir(paths.dir);\n const record: SpoolRecord = {\n v: 1,\n eventId,\n spooledAt: new Date(now).toISOString(),\n reason: \"sent\",\n attempts: 0,\n body: scrubRecord(JSON.parse(JSON.stringify(body)), { scrubKeys: true }),\n bootId: mark.bootId,\n };\n let line = JSON.stringify(record) + \"\\n\";\n if (Buffer.byteLength(line) > policy.maxRecordBytes) {\n record.body = withoutImage(record.body);\n line = JSON.stringify(record) + \"\\n\";\n }\n const bytes = Buffer.byteLength(line);\n if (bytes > policy.maxRecordBytes) return false;\n const dueAt = Math.ceil((now + mark.durableAfterMs) / SENT_BUCKET_MS) * SENT_BUCKET_MS;\n const file = join(paths.dir, `${paths.name}.sent-${mark.bootId}-${dueAt}.jsonl`);\n return withLock(paths.lock, () => {\n if (fileSize(paths.file) + sentBytes(paths) + bytes > policy.maxBytes) return false;\n const fd = openSync(file, \"a\", 0o600);\n try {\n writeSync(fd, line);\n } finally {\n closeSync(fd);\n }\n return true;\n });\n } catch {\n return false;\n }\n}\n\nexport function reconcileSent(\n url: string,\n bootId: string,\n options: { policy?: SpoolPolicy; dir?: string; now?: number } = {},\n): SentReconcile {\n const result: SentReconcile = { released: 0, requeued: 0 };\n const policy = options.policy ?? spoolPolicy();\n if (!policy.enabled) return result;\n const paths = spoolPaths(url, options.dir);\n const now = options.now ?? Date.now();\n for (const seg of listSent(paths)) {\n if (seg.bootId === bootId) {\n if (seg.dueAt > now) continue;\n try {\n unlinkSync(seg.file);\n result.released++;\n } catch {}\n continue;\n }\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${now}-${result.requeued}.jsonl`);\n try {\n renameSync(seg.file, target);\n } catch {\n continue;\n }\n try {\n utimesSync(target, 0, 0);\n } catch {}\n result.requeued++;\n }\n return result;\n}\n\nfunction claimFiles(paths: SpoolPaths): string[] {\n const claimed: string[] = [];\n try {\n const prefix = `${paths.name}.draining-`;\n for (const entry of readdirSync(paths.dir)) {\n if (!entry.startsWith(prefix) || !entry.endsWith(\".jsonl\")) continue;\n const full = join(paths.dir, entry);\n try {\n if (Date.now() - statSync(full).mtimeMs > ORPHAN_CLAIM_MS) claimed.push(full);\n } catch {}\n }\n } catch {}\n if (fileSize(paths.file) > 0) {\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${Date.now()}.jsonl`);\n withLock(paths.lock, (locked) => {\n if (!locked) return;\n try {\n renameSync(paths.file, target);\n claimed.push(target);\n } catch {}\n });\n }\n return claimed;\n}\n\nfunction requeueLocked(paths: SpoolPaths, leftover: SpoolRecord[]): void {\n if (leftover.length === 0) return;\n const existing = existsSync(paths.file) ? parseLines(readFileSync(paths.file, \"utf-8\")).records : [];\n rewriteLocked(paths, [...leftover, ...existing]);\n}\n\nfunction requeueUnlocked(paths: SpoolPaths, leftover: SpoolRecord[]): void {\n if (leftover.length === 0) return;\n const target = join(paths.dir, `${paths.name}.draining-${process.pid}-${Date.now()}-requeue.jsonl`);\n const fd = openSync(target, \"wx\", 0o600);\n try {\n writeSync(fd, serialize(leftover));\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n try {\n utimesSync(target, 0, 0);\n } catch {}\n}\n\nexport function spoolHasRecords(url: string, dir?: string): boolean {\n return fileSize(spoolPaths(url, dir).file) > 0;\n}\n\nexport function drainInProgress(url: string, dir?: string): boolean {\n const paths = spoolPaths(url, dir);\n try {\n return Date.now() - statSync(paths.drainLock).mtimeMs < DRAIN_LOCK_STALE_MS;\n } catch {\n return false;\n }\n}\n\nexport async function drainSpool(\n url: string,\n send: (record: SpoolRecord) => Promise<SendOutcome>,\n options: { policy?: SpoolPolicy; dir?: string; maxRecords?: number; deadlineMs?: number } = {},\n): Promise<DrainResult> {\n const policy = options.policy ?? spoolPolicy();\n const result: DrainResult = { claimed: 0, delivered: 0, duplicates: 0, rejected: 0, expired: 0, corrupt: 0, remaining: 0 };\n const paths = spoolPaths(url, options.dir);\n if (!existsSync(paths.dir)) return { ...result, skipped: \"empty\" };\n try {\n ensureDir(paths.dir);\n } catch {}\n if (!tryLock(paths.drainLock, DRAIN_LOCK_STALE_MS)) return { ...result, skipped: \"locked\" };\n try {\n const files = claimFiles(paths);\n if (files.length === 0) return { ...result, skipped: \"empty\" };\n const records: SpoolRecord[] = [];\n for (const file of files) {\n try {\n const parsed = parseLines(readFileSync(file, \"utf-8\"));\n records.push(...parsed.records);\n result.corrupt += parsed.corrupt;\n } catch {}\n }\n result.claimed = records.length;\n const seen = new Set<string>();\n const now = Date.now();\n const deadline = options.deadlineMs ? now + options.deadlineMs : Number.POSITIVE_INFINITY;\n const maxRecords = options.maxRecords ?? Number.POSITIVE_INFINITY;\n const leftover: SpoolRecord[] = [];\n let stopped = false;\n let processed = 0;\n for (const rec of records) {\n if (seen.has(rec.eventId)) {\n result.duplicates++;\n continue;\n }\n seen.add(rec.eventId);\n if (isExpired(rec, policy, now)) {\n result.expired++;\n continue;\n }\n if (stopped || processed >= maxRecords || Date.now() > deadline) {\n leftover.push(rec);\n continue;\n }\n processed++;\n let outcome: SendOutcome;\n try {\n outcome = await send(rec);\n } catch (err) {\n outcome = \"retry\";\n result.error = err instanceof Error ? err.message : String(err);\n }\n if (outcome === \"delivered\") result.delivered++;\n else if (outcome === \"duplicate\") result.duplicates++;\n else if (outcome === \"rejected\") result.rejected++;\n else {\n stopped = true;\n leftover.push({ ...rec, attempts: (rec.attempts ?? 0) + 1 });\n }\n }\n result.remaining = leftover.length;\n withLock(paths.lock, (locked) => (locked ? requeueLocked(paths, leftover) : requeueUnlocked(paths, leftover)), 2000);\n for (const file of files) {\n try {\n unlinkSync(file);\n } catch {}\n }\n writeStats(paths, (s) => {\n s.delivered += result.delivered;\n s.duplicates += result.duplicates;\n s.rejected += result.rejected;\n s.expired += result.expired;\n s.lastDrainAt = new Date().toISOString();\n s.lastDrainDelivered = result.delivered;\n s.lastDrainDuplicates = result.duplicates;\n s.lastDrainRejected = result.rejected;\n s.lastDrainRemaining = result.remaining;\n if (result.error) s.lastDrainError = result.error;\n else delete s.lastDrainError;\n });\n return result;\n } finally {\n releaseLock(paths.drainLock);\n }\n}\n\nexport function spoolSummary(url: string, options: { policy?: SpoolPolicy; dir?: string } = {}): SpoolSummary {\n const policy = options.policy ?? spoolPolicy();\n const paths = spoolPaths(url, options.dir);\n let records = 0;\n let bytes = 0;\n let oldestAt: string | null = null;\n const files: string[] = [];\n if (existsSync(paths.file)) files.push(paths.file);\n try {\n for (const entry of readdirSync(paths.dir)) {\n if (entry.startsWith(`${paths.name}.draining-`) && entry.endsWith(\".jsonl\")) files.push(join(paths.dir, entry));\n }\n } catch {}\n for (const file of files) {\n try {\n const text = readFileSync(file, \"utf-8\");\n bytes += Buffer.byteLength(text);\n for (const rec of parseLines(text).records) {\n records++;\n if (!oldestAt || rec.spooledAt < oldestAt) oldestAt = rec.spooledAt;\n }\n } catch {}\n }\n let retained = 0;\n let retainedBytes = 0;\n for (const seg of listSent(paths)) {\n try {\n const text = readFileSync(seg.file, \"utf-8\");\n retainedBytes += Buffer.byteLength(text);\n retained += parseLines(text).records.length;\n } catch {}\n }\n return {\n enabled: policy.enabled,\n path: paths.file,\n records,\n bytes,\n oldestAt,\n retained,\n retainedBytes,\n maxBytes: policy.maxBytes,\n maxAgeHours: Math.round(policy.maxAgeMs / 3600_000),\n stats: readSpoolStats(paths),\n };\n}\n","import { spawn } from \"node:child_process\";\nimport { hookAuthHeaders, reportBlockedTransport, resolveHookEndpoint } from \"./_endpoint.js\";\nimport { deriveEventId, hasHostIdentity } from \"../capture/event-id.js\";\nimport {\n appendSpool,\n drainInProgress,\n drainSpool,\n parseSentMark,\n reconcileSent,\n retainSent,\n spoolHasRecords,\n type SendOutcome,\n type SentMark,\n type SpoolRecord,\n} from \"../capture/spool.js\";\n\nexport { stripPrivateData } from \"../functions/privacy.js\";\n\nconst ENDPOINT = resolveHookEndpoint();\nexport const REST_URL = ENDPOINT.url;\nconst DRAIN_CHILD_ENV = \"AGENTMEMORY_CAPTURE_DRAIN_CHILD\";\nconst DRAIN_MAX_RECORDS = 500;\nconst DRAIN_DEADLINE_MS = 20_000;\nconst DRAIN_REQUEST_TIMEOUT_MS = 5000;\n\nexport type CaptureOutcome = \"delivered\" | \"duplicate\" | \"rejected\" | \"spooled\" | \"dropped\";\n\nexport interface ObserveBody {\n hookType: string;\n sessionId: string;\n project: string;\n cwd: string;\n timestamp: string;\n data: unknown;\n eventId?: string;\n}\n\nexport function authHeaders(): Record<string, string> {\n return hookAuthHeaders(ENDPOINT);\n}\n\nexport function transportBlocked(): boolean {\n return reportBlockedTransport(ENDPOINT);\n}\n\nexport function withEventId(\n body: ObserveBody,\n host: Record<string, unknown>,\n content?: unknown,\n options: { stable?: boolean } = {},\n): ObserveBody {\n const source = options.stable || hasHostIdentity(host) ? host : { ...host, timestamp: body.timestamp };\n return {\n ...body,\n eventId: deriveEventId(body.hookType, body.sessionId, source, content ?? body.data),\n };\n}\n\nfunction classify(status: number): SendOutcome {\n if (status >= 200 && status < 300) return status === 200 ? \"duplicate\" : \"delivered\";\n if (status === 408 || status === 429 || status >= 500) return \"retry\";\n return \"rejected\";\n}\n\nasync function post(body: Record<string, unknown>, timeoutMs: number): Promise<{ outcome: SendOutcome; mark: SentMark | null }> {\n if (transportBlocked()) throw new Error(\"insecure transport\");\n const res = await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n const text = await res.text().catch(() => \"\");\n const outcome = classify(res.status);\n if (outcome !== \"delivered\" && outcome !== \"duplicate\") return { outcome, mark: null };\n try {\n return { outcome, mark: parseSentMark(JSON.parse(text)) };\n } catch {\n return { outcome, mark: null };\n }\n}\n\nfunction keepUntilDurable(eventId: string, body: Record<string, unknown>, mark: SentMark): number {\n const { requeued } = reconcileSent(REST_URL, mark.bootId);\n retainSent(REST_URL, eventId, body, mark);\n return requeued;\n}\n\nfunction startDrainChild(): void {\n if (process.env[DRAIN_CHILD_ENV] === \"1\") return;\n if (!process.argv[1] || drainInProgress(REST_URL)) return;\n try {\n const child = spawn(process.execPath, [process.argv[1]], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: true,\n env: { ...process.env, [DRAIN_CHILD_ENV]: \"1\" },\n });\n child.on(\"error\", () => {});\n child.unref();\n } catch {}\n}\n\nexport async function captureObservation(body: ObserveBody, timeoutMs: number): Promise<CaptureOutcome> {\n if (transportBlocked()) return \"dropped\";\n const eventId = body.eventId ?? deriveEventId(body.hookType, body.sessionId, null, body.data);\n const payload = { ...body, eventId } as unknown as Record<string, unknown>;\n let reason: string;\n try {\n const { outcome, mark } = await post(payload, timeoutMs);\n if (outcome !== \"retry\") {\n const requeued = mark ? keepUntilDurable(eventId, payload, mark) : 0;\n if (requeued > 0 || spoolHasRecords(REST_URL)) startDrainChild();\n return outcome;\n }\n reason = \"server-error\";\n } catch (err) {\n const name = err instanceof Error ? err.name : \"\";\n reason = name === \"TimeoutError\" || name === \"AbortError\" ? \"timeout\" : \"unreachable\";\n }\n return appendSpool(REST_URL, eventId, payload, reason).spooled ? \"spooled\" : \"dropped\";\n}\n\nexport function isDrainChild(): boolean {\n return process.env[DRAIN_CHILD_ENV] === \"1\";\n}\n\nexport async function runDrainChild(): Promise<void> {\n await drainSpool(\n REST_URL,\n async (record: SpoolRecord) => {\n const { outcome, mark } = await post({ ...record.body, eventId: record.eventId }, DRAIN_REQUEST_TIMEOUT_MS);\n if (mark) retainSent(REST_URL, record.eventId, record.body, mark);\n return outcome;\n },\n { maxRecords: DRAIN_MAX_RECORDS, deadlineMs: DRAIN_DEADLINE_MS },\n ).catch(() => undefined);\n}\n"],"mappings":"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"}
@@ -91,6 +91,60 @@ function canonicalSecretPath() {
91
91
  return join(homedir(), ".agentmemory", "secret");
92
92
  }
93
93
  //#endregion
94
+ //#region src/hooks/_endpoint.ts
95
+ const OPTION_PREFIX = "CLAUDE_PLUGIN_OPTION_";
96
+ const DEFAULT_URL = "http://localhost:3111";
97
+ function filled(value) {
98
+ const trimmed = value?.trim();
99
+ if (!trimmed || trimmed.startsWith("${") && trimmed.endsWith("}")) return void 0;
100
+ return value;
101
+ }
102
+ function hookSetting(name, env = process.env) {
103
+ const optionKey = `${OPTION_PREFIX}${name}`;
104
+ const option = filled(env[optionKey])?.trim();
105
+ if (option) return option;
106
+ if (name === "AGENTMEMORY_SECRET" && optionKey in env) return void 0;
107
+ return filled(env[name]);
108
+ }
109
+ function resolveSettingSecret(url, env = process.env) {
110
+ const secretEnv = { ...env };
111
+ const explicit = hookSetting("AGENTMEMORY_SECRET", env);
112
+ if (explicit === void 0) delete secretEnv["AGENTMEMORY_SECRET"];
113
+ else secretEnv["AGENTMEMORY_SECRET"] = explicit;
114
+ return resolveClientSecret(url, secretEnv);
115
+ }
116
+ function secretTransportAllowed(url) {
117
+ try {
118
+ return new URL(url).protocol === "https:" || isLoopbackUrl(url);
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ function resolveHookEndpoint(env = process.env) {
124
+ const url = hookSetting("AGENTMEMORY_URL", env) || DEFAULT_URL;
125
+ const secret = resolveSettingSecret(url, env);
126
+ const blocked = !secretTransportAllowed(url);
127
+ return {
128
+ url,
129
+ secret: blocked ? "" : secret,
130
+ blocked
131
+ };
132
+ }
133
+ let blockedNoticeShown = false;
134
+ function reportBlockedTransport(endpoint) {
135
+ if (!endpoint.blocked) return false;
136
+ if (!blockedNoticeShown) {
137
+ blockedNoticeShown = true;
138
+ process.stderr.write("[agentmemory] AGENTMEMORY_URL must use HTTPS unless it is a loopback address. Nothing was sent.\n");
139
+ }
140
+ return true;
141
+ }
142
+ function hookAuthHeaders(endpoint) {
143
+ const h = { "Content-Type": "application/json" };
144
+ if (endpoint.secret) h["Authorization"] = `Bearer ${endpoint.secret}`;
145
+ return h;
146
+ }
147
+ //#endregion
94
148
  //#region src/hooks/_project.ts
95
149
  function hookCwd(data) {
96
150
  if (!data || typeof data !== "object") return void 0;
@@ -110,13 +164,11 @@ function isSdkChildContext(payload) {
110
164
  if (!payload || typeof payload !== "object") return false;
111
165
  return payload.entrypoint === "sdk-ts";
112
166
  }
113
- const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
114
- const SECRET = resolveClientSecret(REST_URL);
167
+ const ENDPOINT = resolveHookEndpoint();
168
+ const REST_URL = ENDPOINT.url;
115
169
  const TIMEOUT_MS = 1500;
116
170
  function authHeaders() {
117
- const h = { "Content-Type": "application/json" };
118
- if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
119
- return h;
171
+ return hookAuthHeaders(ENDPOINT);
120
172
  }
121
173
  async function git(args, cwd) {
122
174
  try {
@@ -188,6 +240,7 @@ async function main() {
188
240
  authoredAt: authoredAt || void 0,
189
241
  files
190
242
  };
243
+ if (reportBlockedTransport(ENDPOINT)) return;
191
244
  try {
192
245
  await fetch(`${REST_URL}/agentmemory/session/commit`, {
193
246
  method: "POST",