@agentmemory/agentmemory 0.9.20 → 0.9.22

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 (55) hide show
  1. package/.env.example +2 -0
  2. package/README.md +166 -12
  3. package/dist/.env.example +2 -0
  4. package/dist/cli.d.mts +5 -1
  5. package/dist/cli.d.mts.map +1 -0
  6. package/dist/cli.mjs +122 -693
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/connect-BQQXpyDS.mjs +763 -0
  9. package/dist/connect-BQQXpyDS.mjs.map +1 -0
  10. package/dist/hooks/post-tool-use.mjs +1 -1
  11. package/dist/hooks/post-tool-use.mjs.map +1 -1
  12. package/dist/hooks/stop.mjs +8 -0
  13. package/dist/hooks/stop.mjs.map +1 -1
  14. package/dist/{image-refs-R3tin9MR.mjs → image-refs-CJS5B9Gq.mjs} +2 -2
  15. package/dist/{image-refs-R3tin9MR.mjs.map → image-refs-CJS5B9Gq.mjs.map} +1 -1
  16. package/dist/{image-store-DyrKZKqZ.mjs → image-store-CdE0amb1.mjs} +1 -1
  17. package/dist/index.mjs +881 -281
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/logger-xlVlvCWX.mjs +43 -0
  20. package/dist/logger-xlVlvCWX.mjs.map +1 -0
  21. package/dist/schema-BkALl7Z_.mjs +74 -0
  22. package/dist/schema-BkALl7Z_.mjs.map +1 -0
  23. package/dist/{src-DPSaLB5-.mjs → src-gpTAJuBy.mjs} +861 -287
  24. package/dist/src-gpTAJuBy.mjs.map +1 -0
  25. package/dist/{standalone-DMLk7YxP.mjs → standalone-C4i7ktpn.mjs} +48 -12
  26. package/dist/standalone-C4i7ktpn.mjs.map +1 -0
  27. package/dist/standalone.d.mts.map +1 -1
  28. package/dist/standalone.mjs +45 -10
  29. package/dist/standalone.mjs.map +1 -1
  30. package/dist/{tools-registry-Dz8ssuMf.mjs → tools-registry-B7Y6nJsr.mjs} +39 -11
  31. package/dist/tools-registry-B7Y6nJsr.mjs.map +1 -0
  32. package/dist/version-DvQMNbEH.mjs +6 -0
  33. package/dist/version-DvQMNbEH.mjs.map +1 -0
  34. package/dist/viewer/index.html +134 -21
  35. package/package.json +6 -4
  36. package/plugin/.claude-plugin/plugin.json +1 -1
  37. package/plugin/.codex-plugin/plugin.json +1 -1
  38. package/plugin/.mcp.json +3 -2
  39. package/plugin/hooks/hooks.codex.json +6 -6
  40. package/plugin/hooks/hooks.json +12 -12
  41. package/plugin/opencode/README.md +229 -0
  42. package/plugin/opencode/agentmemory-capture.ts +687 -0
  43. package/plugin/opencode/commands/recall.md +19 -0
  44. package/plugin/opencode/commands/remember.md +19 -0
  45. package/plugin/opencode/plugin.json +12 -0
  46. package/plugin/scripts/diagnostics.d.mts +17 -0
  47. package/plugin/scripts/diagnostics.d.mts.map +1 -0
  48. package/plugin/scripts/diagnostics.mjs.map +1 -0
  49. package/plugin/scripts/post-tool-use.mjs +1 -1
  50. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  51. package/plugin/scripts/stop.mjs +8 -0
  52. package/plugin/scripts/stop.mjs.map +1 -1
  53. package/dist/src-DPSaLB5-.mjs.map +0 -1
  54. package/dist/standalone-DMLk7YxP.mjs.map +0 -1
  55. package/dist/tools-registry-Dz8ssuMf.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-BQQXpyDS.mjs","names":["entryMatches","adapter","adapter","adapter","adapter","adapter","DOCS","adapter","adapter","adapter","DOCS","adapter","adapter","claudeCode","codex","cursor","geminiCli","qwen","antigravity","kiro","openclaw","hermes","pi","openhuman"],"sources":["../src/cli/connect/util.ts","../src/cli/connect/json-mcp-adapter.ts","../src/cli/connect/antigravity.ts","../src/cli/connect/codex-hooks.ts","../src/cli/connect/claude-code.ts","../src/cli/connect/codex.ts","../src/cli/connect/cursor.ts","../src/cli/connect/gemini-cli.ts","../src/cli/connect/hermes.ts","../src/cli/connect/kiro.ts","../src/cli/connect/openclaw.ts","../src/cli/connect/openhuman.ts","../src/cli/connect/pi.ts","../src/cli/connect/qwen.ts","../src/cli/connect/index.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n copyFileSync,\n renameSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport * as p from \"@clack/prompts\";\n\n// Env values use ${VAR:-default} expansion so the wired MCP entry\n// inherits AGENTMEMORY_URL / AGENTMEMORY_SECRET / AGENTMEMORY_TOOLS\n// from the user's shell, but never fails parse when the var is unset\n// (#510). Earlier `${VAR}` form caused Claude Code to silently drop the\n// server when no shell-level export existed — per the Claude Code MCP\n// docs, \"If a required environment variable is not set and has no\n// default value, Claude Code will fail to parse the config.\"\n//\n// Defaults match the documented runtime: localhost:3111 (no auth, all\n// tools). One wired entry now serves local AND remote (Kubernetes /\n// reverse-proxied) deployments without doctor-warning duplicates (#375)\n// AND fresh installs that haven't exported envs (#510).\nexport const AGENTMEMORY_MCP_BLOCK = {\n command: \"npx\",\n args: [\"-y\", \"@agentmemory/mcp\"],\n env: {\n AGENTMEMORY_URL: \"${AGENTMEMORY_URL:-http://localhost:3111}\",\n AGENTMEMORY_SECRET: \"${AGENTMEMORY_SECRET:-}\",\n AGENTMEMORY_TOOLS: \"${AGENTMEMORY_TOOLS:-all}\",\n },\n};\n\nexport function backupsDir(): string {\n return join(homedir(), \".agentmemory\", \"backups\");\n}\n\nexport function ensureBackupsDir(): string {\n const dir = backupsDir();\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nexport function timestampSlug(): string {\n return new Date().toISOString().replace(/[:.]/g, \"-\");\n}\n\nexport function backupFile(\n sourcePath: string,\n agent: string,\n ext = \"json\",\n): string {\n ensureBackupsDir();\n const stamp = timestampSlug();\n const target = join(backupsDir(), `${agent}-${stamp}.${ext}`);\n copyFileSync(sourcePath, target);\n return target;\n}\n\nexport function readJsonSafe<T = unknown>(path: string): T | null {\n if (!existsSync(path)) return null;\n try {\n return JSON.parse(readFileSync(path, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\nexport function writeJsonAtomic(path: string, value: unknown): void {\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;\n writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\");\n renameSync(tmp, path);\n}\n\nexport function logInstalled(label: string, target: string): void {\n p.log.success(`${label} → wired into ${target}`);\n}\n\nexport function logAlreadyWired(label: string, target: string): void {\n p.log.info(`${label} already wired in ${target} (use --force to re-install)`);\n}\n\nexport function logBackup(target: string): void {\n p.log.info(`Backup: ${target}`);\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\nimport {\n AGENTMEMORY_MCP_BLOCK,\n backupFile,\n logAlreadyWired,\n logBackup,\n logInstalled,\n readJsonSafe,\n writeJsonAtomic,\n} from \"./util.js\";\n\nexport type JsonMcpAdapterConfig = {\n name: string;\n displayName: string;\n detectDir: string;\n configPath: string;\n docs?: string;\n protocolNote?: string;\n};\n\ntype McpEntry = typeof AGENTMEMORY_MCP_BLOCK;\ntype McpConfig = {\n mcpServers?: Record<string, McpEntry>;\n [key: string]: unknown;\n};\n\nfunction entryMatches(entry: unknown): boolean {\n if (!entry || typeof entry !== \"object\") return false;\n const e = entry as Record<string, unknown>;\n if (e[\"command\"] !== \"npx\") return false;\n const args = Array.isArray(e[\"args\"]) ? (e[\"args\"] as string[]) : [];\n return args.includes(\"@agentmemory/mcp\");\n}\n\nexport function createJsonMcpAdapter(\n config: JsonMcpAdapterConfig,\n): ConnectAdapter {\n return {\n name: config.name,\n displayName: config.displayName,\n ...(config.docs !== undefined && { docs: config.docs }),\n ...(config.protocolNote !== undefined && {\n protocolNote: config.protocolNote,\n }),\n\n detect(): boolean {\n return existsSync(config.detectDir);\n },\n\n async install(opts: ConnectOptions): Promise<ConnectResult> {\n const existing = readJsonSafe<McpConfig>(config.configPath);\n const next: McpConfig = existing ? { ...existing } : {};\n const servers: Record<string, McpEntry> = {\n ...((next.mcpServers as Record<string, McpEntry>) ?? {}),\n };\n\n const alreadyHas = entryMatches(servers[\"agentmemory\"]);\n if (alreadyHas && !opts.force) {\n logAlreadyWired(config.displayName, config.configPath);\n return { kind: \"already-wired\", mutatedPath: config.configPath };\n }\n\n if (opts.dryRun) {\n p.log.info(\n `[dry-run] Would ${alreadyHas ? \"overwrite\" : \"add\"} mcpServers.agentmemory in ${config.configPath}`,\n );\n return { kind: \"installed\", mutatedPath: config.configPath };\n }\n\n let backupPath: string | undefined;\n if (existsSync(config.configPath)) {\n backupPath = backupFile(config.configPath, config.name);\n logBackup(backupPath);\n } else {\n mkdirSync(dirname(config.configPath), { recursive: true });\n }\n\n servers[\"agentmemory\"] = AGENTMEMORY_MCP_BLOCK;\n next.mcpServers = servers;\n writeJsonAtomic(config.configPath, next);\n\n const verify = readJsonSafe<McpConfig>(config.configPath);\n if (!entryMatches(verify?.mcpServers?.[\"agentmemory\"])) {\n p.log.error(\n `Verification failed: ${config.configPath} did not contain mcpServers.agentmemory after write.`,\n );\n return { kind: \"skipped\", reason: \"verification-failed\" };\n }\n\n logInstalled(config.displayName, config.configPath);\n return {\n kind: \"installed\",\n mutatedPath: config.configPath,\n ...(backupPath !== undefined && { backupPath }),\n };\n },\n };\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\n// Antigravity stores MCP config in mcp_config.json under its app\n// support directory. The schema follows the standard MCP envelope —\n// `{ mcpServers: { ... } }`. Path varies by platform:\n// macOS: ~/Library/Application Support/Antigravity/User/mcp_config.json\n// Linux: ~/.config/Antigravity/User/mcp_config.json\n// Windows: %APPDATA%/Antigravity/User/mcp_config.json\n// Connect is gated on win32 elsewhere; we cover macOS + Linux here.\nconst ANTIGRAVITY_DIR =\n platform() === \"darwin\"\n ? join(homedir(), \"Library\", \"Application Support\", \"Antigravity\", \"User\")\n : join(homedir(), \".config\", \"Antigravity\", \"User\");\n\nexport const adapter = createJsonMcpAdapter({\n name: \"antigravity\",\n displayName: \"Antigravity\",\n detectDir: ANTIGRAVITY_DIR,\n configPath: join(ANTIGRAVITY_DIR, \"mcp_config.json\"),\n docs: \"https://github.com/rohitg00/agentmemory#other-agents\",\n protocolNote:\n \"→ Using MCP via mcp_config.json. Antigravity replaces Gemini CLI (sunset 2026-06-18).\",\n});\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Workaround for openai/codex#16430 — Codex Desktop does not dispatch\n * plugin-local `hooks.json` even though both `CodexHooks` and `PluginHooks`\n * feature flags are stable + default-enabled in\n * `codex-rs/features/src/lib.rs`. Until upstream fixes plugin-scope\n * dispatch, the same hook commands can be mirrored into the global\n * `~/.codex/hooks.json`, which is loaded reliably.\n *\n * This module builds that mirror, with `${CLAUDE_PLUGIN_ROOT}` resolved to\n * the bundled `plugin/` directory so the user-scope file does not depend\n * on env-var expansion (Codex only injects `CLAUDE_PLUGIN_ROOT` for\n * plugin-scope hooks).\n *\n * Identification on re-install: every command we write contains the\n * resolved `<pluginRoot>/scripts/` prefix, so subsequent installs can\n * strip our entries and re-add cleanly without touching the user's other\n * hook entries.\n */\n\ntype HookHandler = { type: string; command: string };\ntype HookEntry = { matcher?: string; hooks: HookHandler[] };\nexport type HookManifest = { hooks: Record<string, HookEntry[]> };\n\n/**\n * Locate the bundled `plugin/` directory at runtime. Walks up from the\n * module's own location looking for `plugin/scripts/` + `plugin/hooks/`,\n * both shipped via the npm `files` field. Works for both `dist/cli.mjs`\n * (bundled) and `src/cli/connect/codex-hooks.ts` (dev) layouts.\n */\nexport function findPluginRoot(startUrl: string = import.meta.url): string {\n const here = dirname(fileURLToPath(startUrl));\n let dir = here;\n for (let i = 0; i < 12; i++) {\n if (\n existsSync(join(dir, \"plugin\", \"scripts\")) &&\n existsSync(join(dir, \"plugin\", \"hooks\"))\n ) {\n return resolve(join(dir, \"plugin\"));\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n throw new Error(\n `agentmemory: could not locate bundled plugin/ directory (searched up from ${here})`,\n );\n}\n\n/**\n * Build the merged hooks.json content.\n *\n * 1. Strip any entry from `existing` whose first hook command points\n * under `<pluginRoot>/scripts/`. This lets us re-install idempotently\n * without leaving stale references.\n * 2. Append fresh entries from the bundled Codex manifest with\n * `${CLAUDE_PLUGIN_ROOT}` rewritten to the absolute plugin path.\n * Matcher values from the bundled manifest are preserved so PreToolUse\n * event routing keeps working.\n */\nexport function buildMergedHooks(\n existing: HookManifest | null,\n pluginRoot: string,\n manifestFile = \"hooks.codex.json\",\n): HookManifest {\n const bundledManifestPath = join(pluginRoot, \"hooks\", manifestFile);\n const ours = JSON.parse(readFileSync(bundledManifestPath, \"utf-8\")) as HookManifest;\n const scriptsDir = join(pluginRoot, \"scripts\");\n\n const out: HookManifest = { hooks: {} };\n\n if (existing?.hooks) {\n for (const [event, entries] of Object.entries(existing.hooks)) {\n const kept = entries.filter((entry) => !isAgentmemoryEntry(entry, scriptsDir));\n if (kept.length > 0) out.hooks[event] = kept;\n }\n }\n\n for (const [event, entries] of Object.entries(ours.hooks)) {\n const resolvedEntries: HookEntry[] = entries.map((entry) => {\n const next: HookEntry = {\n hooks: entry.hooks.map((handler) => ({\n type: handler.type,\n command: handler.command.replace(/\\$\\{CLAUDE_PLUGIN_ROOT\\}/g, pluginRoot),\n })),\n };\n if (entry.matcher !== undefined) next.matcher = entry.matcher;\n return next;\n });\n out.hooks[event] = [...(out.hooks[event] ?? []), ...resolvedEntries];\n }\n\n return out;\n}\n\nfunction isAgentmemoryEntry(entry: HookEntry, scriptsDir: string): boolean {\n return entry.hooks.some((handler) => handler.command.includes(scriptsDir));\n}\n","import { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\nimport {\n AGENTMEMORY_MCP_BLOCK,\n backupFile,\n logAlreadyWired,\n logBackup,\n logInstalled,\n readJsonSafe,\n writeJsonAtomic,\n} from \"./util.js\";\nimport {\n buildMergedHooks,\n findPluginRoot,\n type HookManifest,\n} from \"./codex-hooks.js\";\n\nconst CLAUDE_DIR = join(homedir(), \".claude\");\nconst CLAUDE_JSON = join(homedir(), \".claude.json\");\nconst CLAUDE_SETTINGS = join(CLAUDE_DIR, \"settings.json\");\n\ntype ClaudeMcpEntry = typeof AGENTMEMORY_MCP_BLOCK;\ntype ClaudeConfig = {\n mcpServers?: Record<string, ClaudeMcpEntry>;\n [key: string]: unknown;\n};\n\nfunction entryMatches(entry: unknown): boolean {\n if (!entry || typeof entry !== \"object\") return false;\n const e = entry as Record<string, unknown>;\n if (e[\"command\"] !== \"npx\") return false;\n const args = Array.isArray(e[\"args\"]) ? (e[\"args\"] as string[]) : [];\n return args.includes(\"@agentmemory/mcp\");\n}\n\nexport const adapter: ConnectAdapter = {\n name: \"claude-code\",\n displayName: \"Claude Code\",\n docs: \"https://github.com/rohitg00/agentmemory#claude-code-one-block-paste-it\",\n protocolNote:\n \"→ Using MCP. Hooks are also available — see docs/claude-code.md.\",\n\n detect(): boolean {\n return existsSync(CLAUDE_DIR);\n },\n\n async install(opts: ConnectOptions): Promise<ConnectResult> {\n const existing = readJsonSafe<ClaudeConfig>(CLAUDE_JSON);\n const next: ClaudeConfig = existing ? { ...existing } : {};\n const servers: Record<string, ClaudeMcpEntry> = {\n ...((next.mcpServers as Record<string, ClaudeMcpEntry>) ?? {}),\n };\n\n const alreadyHas = entryMatches(servers[\"agentmemory\"]);\n if (alreadyHas && !opts.force) {\n logAlreadyWired(\"Claude Code\", CLAUDE_JSON);\n // --with-hooks is independent of MCP wiring (issue #508). Run the\n // hooks fallback even when MCP is already in place so users with a\n // healthy MCP setup can still pick up version-stable hook paths.\n if (opts.withHooks) {\n const hookResult = installClaudeHooks(opts);\n if (hookResult.kind === \"skipped\") {\n p.log.warn(\n `Claude Code hooks fallback skipped: ${hookResult.reason}.`,\n );\n }\n }\n return { kind: \"already-wired\", mutatedPath: CLAUDE_JSON };\n }\n\n if (opts.dryRun) {\n p.log.info(\n `[dry-run] Would ${alreadyHas ? \"overwrite\" : \"add\"} mcpServers.agentmemory in ${CLAUDE_JSON}`,\n );\n return { kind: \"installed\", mutatedPath: CLAUDE_JSON };\n }\n\n let backupPath: string | undefined;\n if (existsSync(CLAUDE_JSON)) {\n backupPath = backupFile(CLAUDE_JSON, \"claude-code\");\n logBackup(backupPath);\n } else {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n writeFileSync(CLAUDE_JSON, \"{}\\n\", \"utf-8\");\n }\n\n servers[\"agentmemory\"] = AGENTMEMORY_MCP_BLOCK;\n next.mcpServers = servers;\n writeJsonAtomic(CLAUDE_JSON, next);\n\n const verify = readJsonSafe<ClaudeConfig>(CLAUDE_JSON);\n if (!entryMatches(verify?.mcpServers?.[\"agentmemory\"])) {\n p.log.error(\n `Verification failed: ${CLAUDE_JSON} did not contain mcpServers.agentmemory after write.`,\n );\n return { kind: \"skipped\", reason: \"verification-failed\" };\n }\n\n logInstalled(\"Claude Code\", CLAUDE_JSON);\n p.log.info(\n \"Restart Claude Code (or run `/mcp` inside a session) to pick up the new server.\",\n );\n\n if (opts.withHooks) {\n const hookResult = installClaudeHooks(opts);\n if (hookResult.kind === \"skipped\") {\n p.log.warn(\n `Claude Code hooks fallback skipped: ${hookResult.reason}. MCP wiring still applied.`,\n );\n }\n }\n\n return { kind: \"installed\", mutatedPath: CLAUDE_JSON, backupPath };\n },\n};\n\n/**\n * Merge the bundled `plugin/hooks/hooks.json` into\n * `~/.claude/settings.json`'s top-level `hooks` field with absolute\n * script paths. Use this when agentmemory is NOT installed through\n * `/plugin marketplace add` (e.g. MCP standalone wiring), so the\n * hook scripts survive version bumps without `${CLAUDE_PLUGIN_ROOT}`\n * expansion (issue #508).\n *\n * Re-install strips entries whose command points under\n * `<pluginRoot>/scripts/`; unrelated user hook entries survive.\n */\nfunction installClaudeHooks(opts: ConnectOptions): ConnectResult {\n let pluginRoot: string;\n try {\n pluginRoot = findPluginRoot();\n } catch (err) {\n return {\n kind: \"skipped\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n type ClaudeSettings = { hooks?: HookManifest[\"hooks\"]; [key: string]: unknown };\n const existing = readJsonSafe<ClaudeSettings>(CLAUDE_SETTINGS) ?? {};\n const existingHooks: HookManifest | null = existing.hooks\n ? { hooks: existing.hooks }\n : null;\n const merged = buildMergedHooks(existingHooks, pluginRoot, \"hooks.json\");\n\n if (opts.dryRun) {\n p.log.info(\n `[dry-run] Would merge agentmemory hook entries into ${CLAUDE_SETTINGS} (${Object.keys(merged.hooks).length} event(s))`,\n );\n return { kind: \"installed\", mutatedPath: CLAUDE_SETTINGS };\n }\n\n let backupPath: string | undefined;\n if (existsSync(CLAUDE_SETTINGS)) {\n backupPath = backupFile(CLAUDE_SETTINGS, \"claude-settings\", \"json\");\n logBackup(backupPath);\n } else {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n }\n\n const next: ClaudeSettings = { ...existing, hooks: merged.hooks };\n writeJsonAtomic(CLAUDE_SETTINGS, next);\n\n logInstalled(\"Claude Code hooks (workaround for #508)\", CLAUDE_SETTINGS);\n p.log.info(\n \"User-scope hook entries reference absolute paths under the bundled plugin/ dir. Re-run `agentmemory connect claude-code --with-hooks` after upgrading agentmemory to refresh them.\",\n );\n\n return {\n kind: \"installed\",\n mutatedPath: CLAUDE_SETTINGS,\n ...(backupPath !== undefined && { backupPath }),\n };\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\nimport {\n backupFile,\n logAlreadyWired,\n logBackup,\n logInstalled,\n readJsonSafe,\n writeJsonAtomic,\n} from \"./util.js\";\nimport {\n buildMergedHooks,\n findPluginRoot,\n type HookManifest,\n} from \"./codex-hooks.js\";\n\nconst CODEX_DIR = join(homedir(), \".codex\");\nconst CODEX_TOML = join(CODEX_DIR, \"config.toml\");\nconst CODEX_HOOKS = join(CODEX_DIR, \"hooks.json\");\n\nconst TOML_BLOCK = `[mcp_servers.agentmemory]\ncommand = \"npx\"\nargs = [\"-y\", \"@agentmemory/mcp\"]\n\n[mcp_servers.agentmemory.env]\nAGENTMEMORY_URL = \"http://localhost:3111\"\n`;\n\nconst SECTION_HEADER = \"[mcp_servers.agentmemory]\";\n\nfunction isWiredText(toml: string): boolean {\n return toml.includes(SECTION_HEADER);\n}\n\nfunction stripExistingBlock(toml: string): string {\n const lines = toml.split(/\\r?\\n/);\n const out: string[] = [];\n let skipping = false;\n for (const line of lines) {\n const trimmed = line.trim();\n if (\n trimmed === SECTION_HEADER ||\n trimmed === \"[mcp_servers.agentmemory.env]\"\n ) {\n skipping = true;\n continue;\n }\n if (\n skipping &&\n trimmed.startsWith(\"[\") &&\n trimmed !== \"[mcp_servers.agentmemory.env]\"\n ) {\n skipping = false;\n }\n if (!skipping) out.push(line);\n }\n return out.join(\"\\n\").replace(/\\n{3,}$/, \"\\n\\n\").trimEnd() + \"\\n\";\n}\n\nexport const adapter: ConnectAdapter = {\n name: \"codex\",\n displayName: \"Codex CLI\",\n docs: \"https://github.com/rohitg00/agentmemory#codex-cli-codex-plugin-platform\",\n protocolNote:\n \"→ Using MCP. Hooks ship via the Codex plugin; on Codex Desktop, also pass --with-hooks to install the global hooks.json workaround for openai/codex#16430.\",\n\n detect(): boolean {\n return existsSync(CODEX_DIR);\n },\n\n async install(opts: ConnectOptions): Promise<ConnectResult> {\n const exists = existsSync(CODEX_TOML);\n const current = exists ? readFileSync(CODEX_TOML, \"utf-8\") : \"\";\n const wired = isWiredText(current);\n\n if (wired && !opts.force) {\n logAlreadyWired(\"Codex CLI\", CODEX_TOML);\n return { kind: \"already-wired\", mutatedPath: CODEX_TOML };\n }\n\n if (opts.dryRun) {\n p.log.info(\n `[dry-run] Would ${wired ? \"rewrite\" : \"append\"} [mcp_servers.agentmemory] in ${CODEX_TOML}`,\n );\n if (opts.withHooks) installCodexHooks(opts);\n return { kind: \"installed\", mutatedPath: CODEX_TOML };\n }\n\n let backupPath: string | undefined;\n if (exists) {\n backupPath = backupFile(CODEX_TOML, \"codex\", \"toml\");\n logBackup(backupPath);\n } else {\n mkdirSync(dirname(CODEX_TOML), { recursive: true });\n }\n\n const cleaned = wired ? stripExistingBlock(current) : current;\n const joiner = cleaned.length === 0 || cleaned.endsWith(\"\\n\") ? \"\" : \"\\n\";\n const next = `${cleaned}${joiner}${cleaned.length > 0 ? \"\\n\" : \"\"}${TOML_BLOCK}`;\n writeFileSync(CODEX_TOML, next, \"utf-8\");\n\n const verify = readFileSync(CODEX_TOML, \"utf-8\");\n if (!isWiredText(verify)) {\n p.log.error(\n `Verification failed: ${CODEX_TOML} did not contain ${SECTION_HEADER} after write.`,\n );\n return { kind: \"skipped\", reason: \"verification-failed\" };\n }\n\n logInstalled(\"Codex CLI\", CODEX_TOML);\n p.log.info(\n \"Codex picks up MCP servers on next launch. For the deeper plugin install, run: codex plugin marketplace add rohitg00/agentmemory && codex plugin add agentmemory@agentmemory\",\n );\n\n if (opts.withHooks) {\n const hookResult = installCodexHooks(opts);\n if (hookResult.kind === \"skipped\") {\n p.log.warn(\n `Codex hooks fallback skipped: ${hookResult.reason}. MCP wiring still applied.`,\n );\n }\n }\n\n return {\n kind: \"installed\",\n mutatedPath: CODEX_TOML,\n ...(backupPath !== undefined && { backupPath }),\n };\n },\n};\n\n/**\n * Install the global `~/.codex/hooks.json` fallback. See\n * `codex-hooks.ts` for context (openai/codex#16430). Returns a result\n * describing the side effect for the caller's summary; failures here do\n * not roll back the MCP wiring.\n */\nfunction installCodexHooks(opts: ConnectOptions): ConnectResult {\n let pluginRoot: string;\n try {\n pluginRoot = findPluginRoot();\n } catch (err) {\n return {\n kind: \"skipped\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n const existing = readJsonSafe<HookManifest>(CODEX_HOOKS);\n const merged = buildMergedHooks(existing, pluginRoot);\n\n if (opts.dryRun) {\n p.log.info(\n `[dry-run] Would ${existing ? \"merge\" : \"create\"} ${CODEX_HOOKS} with ${Object.keys(merged.hooks).length} event(s)`,\n );\n return { kind: \"installed\", mutatedPath: CODEX_HOOKS };\n }\n\n let backupPath: string | undefined;\n if (existsSync(CODEX_HOOKS)) {\n backupPath = backupFile(CODEX_HOOKS, \"codex-hooks\", \"json\");\n logBackup(backupPath);\n }\n\n writeJsonAtomic(CODEX_HOOKS, merged);\n\n logInstalled(\"Codex hooks (workaround for openai/codex#16430)\", CODEX_HOOKS);\n p.log.info(\n \"User-scope hooks reference absolute paths under the bundled plugin/ dir. Re-run `agentmemory connect codex --with-hooks` after upgrading agentmemory to refresh them.\",\n );\n\n return {\n kind: \"installed\",\n mutatedPath: CODEX_HOOKS,\n ...(backupPath !== undefined && { backupPath }),\n };\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\nexport const adapter = createJsonMcpAdapter({\n name: \"cursor\",\n displayName: \"Cursor\",\n detectDir: join(homedir(), \".cursor\"),\n configPath: join(homedir(), \".cursor\", \"mcp.json\"),\n docs: \"https://github.com/rohitg00/agentmemory#other-agents\",\n protocolNote:\n \"→ Using MCP (the only protocol Cursor speaks). Memory bridge runs at :3111 underneath.\",\n});\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\nexport const adapter = createJsonMcpAdapter({\n name: \"gemini-cli\",\n displayName: \"Gemini CLI\",\n detectDir: join(homedir(), \".gemini\"),\n configPath: join(homedir(), \".gemini\", \"settings.json\"),\n docs: \"https://github.com/rohitg00/agentmemory#other-agents\",\n protocolNote:\n \"→ Using MCP (the only protocol Gemini CLI speaks). Memory bridge runs at :3111 underneath.\",\n});\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\n\nconst HERMES_DIR = join(homedir(), \".hermes\");\nconst HERMES_CONFIG = join(HERMES_DIR, \"config.yaml\");\nconst DOCS = \"https://github.com/rohitg00/agentmemory/tree/main/integrations/hermes\";\n\nexport const adapter: ConnectAdapter = {\n name: \"hermes\",\n displayName: \"Hermes Agent\",\n docs: DOCS,\n protocolNote:\n \"→ Using MCP. Hooks are also available — see docs/hermes.md.\",\n\n detect(): boolean {\n return existsSync(HERMES_DIR);\n },\n\n async install(_opts: ConnectOptions): Promise<ConnectResult> {\n p.log.warn(\n \"Hermes uses YAML config. Automated merge isn't implemented yet — manual install required.\",\n );\n p.note(\n [\n `Add to ${HERMES_CONFIG}:`,\n \"\",\n \" mcp_servers:\",\n \" agentmemory:\",\n \" command: npx\",\n ' args: [\"-y\", \"@agentmemory/mcp\"]',\n \"\",\n \" memory:\",\n \" provider: agentmemory\",\n \"\",\n `Full guide: ${DOCS}`,\n ].join(\"\\n\"),\n \"Hermes manual install\",\n );\n return {\n kind: \"stub\",\n reason: \"yaml-merge-not-implemented\",\n };\n },\n};\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\n// Kiro stores user-level MCP servers in ~/.kiro/settings/mcp.json.\n// Schema follows the standard MCP envelope { mcpServers: { ... } }.\n// Source: kiro.dev/docs/cli/mcp\nexport const adapter = createJsonMcpAdapter({\n name: \"kiro\",\n displayName: \"Kiro\",\n detectDir: join(homedir(), \".kiro\"),\n configPath: join(homedir(), \".kiro\", \"settings\", \"mcp.json\"),\n docs: \"https://github.com/rohitg00/agentmemory#other-agents\",\n protocolNote:\n \"→ Using MCP via ~/.kiro/settings/mcp.json (user-level). Workspace overrides live in .kiro/settings/mcp.json.\",\n});\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\nexport const adapter = createJsonMcpAdapter({\n name: \"openclaw\",\n displayName: \"OpenClaw\",\n detectDir: join(homedir(), \".openclaw\"),\n configPath: join(homedir(), \".openclaw\", \"openclaw.json\"),\n docs: \"https://github.com/rohitg00/agentmemory/tree/main/integrations/openclaw\",\n protocolNote:\n \"→ Using MCP. Hooks are also available — see docs/openclaw.md.\",\n});\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\n\nconst OPENHUMAN_DIR = join(homedir(), \".openhuman\");\nconst DOCS = \"https://github.com/tinyhumansai/openhuman\";\n\nexport const adapter: ConnectAdapter = {\n name: \"openhuman\",\n displayName: \"OpenHuman\",\n docs: DOCS,\n protocolNote:\n \"→ Using native hooks (REST API at :3111). MCP not required.\",\n\n detect(): boolean {\n return existsSync(OPENHUMAN_DIR);\n },\n\n async install(_opts: ConnectOptions): Promise<ConnectResult> {\n p.log.warn(\n \"OpenHuman integration is not yet automated. No `integrations/openhuman/` folder exists in the agentmemory repo today.\",\n );\n p.note(\n [\n \"OpenHuman is a Memory-trait host. The expected wiring is the REST\",\n \"proxy at http://localhost:3111 plus an OpenHuman-side Memory trait\",\n \"impl. Once integrations/openhuman/ lands in agentmemory we'll wire\",\n \"this up automatically.\",\n \"\",\n `Tracking: ${DOCS}`,\n ].join(\"\\n\"),\n \"OpenHuman manual install\",\n );\n return {\n kind: \"stub\",\n reason: \"no-integration-folder-yet\",\n };\n },\n};\n","import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\n\nconst PI_DIR = join(homedir(), \".pi\");\nconst PI_EXT_DIR = join(PI_DIR, \"agent\", \"extensions\", \"agentmemory\");\nconst DOCS = \"https://github.com/rohitg00/agentmemory/tree/main/integrations/pi\";\n\nexport const adapter: ConnectAdapter = {\n name: \"pi\",\n displayName: \"pi\",\n docs: DOCS,\n protocolNote:\n \"→ Using native hooks (REST API at :3111). MCP not required.\",\n\n detect(): boolean {\n return existsSync(PI_DIR);\n },\n\n async install(_opts: ConnectOptions): Promise<ConnectResult> {\n p.log.warn(\n \"pi uses a TypeScript extension file. Automated copy + register isn't implemented yet — manual install required.\",\n );\n p.note(\n [\n \"Run these from the agentmemory repo root:\",\n \"\",\n ` mkdir -p ${PI_EXT_DIR}`,\n ` cp integrations/pi/index.ts ${PI_EXT_DIR}/index.ts`,\n ` cp integrations/pi/security.ts ${PI_EXT_DIR}/security.ts`,\n \"\",\n \"Then add to ~/.pi/agent/settings.json:\",\n ' { \"extensions\": [\"~/.pi/agent/extensions/agentmemory\"] }',\n \"\",\n `Full guide: ${DOCS}`,\n ].join(\"\\n\"),\n \"pi manual install\",\n );\n return {\n kind: \"stub\",\n reason: \"ts-extension-copy-not-implemented\",\n };\n },\n};\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createJsonMcpAdapter } from \"./json-mcp-adapter.js\";\n\n// Qwen Code stores its settings (mcpServers + hooks) in\n// ~/.qwen/settings.json. Schema for mcpServers matches Claude Code's\n// shape, so the shared JSON adapter handles the wiring.\n// Source: qwenlm.github.io/qwen-code-docs/en/users/features/mcp\nexport const adapter = createJsonMcpAdapter({\n name: \"qwen\",\n displayName: \"Qwen Code\",\n detectDir: join(homedir(), \".qwen\"),\n configPath: join(homedir(), \".qwen\", \"settings.json\"),\n docs: \"https://github.com/rohitg00/agentmemory#other-agents\",\n protocolNote:\n \"→ Using MCP via ~/.qwen/settings.json. Qwen Code's hook system can also be wired separately — see docs.\",\n});\n","import { platform } from \"node:os\";\nimport * as p from \"@clack/prompts\";\nimport type { ConnectAdapter, ConnectOptions, ConnectResult } from \"./types.js\";\nimport { adapter as antigravity } from \"./antigravity.js\";\nimport { adapter as claudeCode } from \"./claude-code.js\";\nimport { adapter as codex } from \"./codex.js\";\nimport { adapter as cursor } from \"./cursor.js\";\nimport { adapter as geminiCli } from \"./gemini-cli.js\";\nimport { adapter as hermes } from \"./hermes.js\";\nimport { adapter as kiro } from \"./kiro.js\";\nimport { adapter as openclaw } from \"./openclaw.js\";\nimport { adapter as openhuman } from \"./openhuman.js\";\nimport { adapter as pi } from \"./pi.js\";\nimport { adapter as qwen } from \"./qwen.js\";\n\nexport const ADAPTERS: readonly ConnectAdapter[] = [\n claudeCode,\n codex,\n cursor,\n geminiCli,\n qwen,\n antigravity,\n kiro,\n openclaw,\n hermes,\n pi,\n openhuman,\n];\n\nexport function resolveAdapter(name: string): ConnectAdapter | null {\n const lower = name.toLowerCase();\n return ADAPTERS.find((a) => a.name === lower) ?? null;\n}\n\nexport function knownAgents(): string[] {\n return ADAPTERS.map((a) => a.name);\n}\n\nfunction parseFlags(args: string[]): {\n dryRun: boolean;\n force: boolean;\n all: boolean;\n withHooks: boolean;\n positional: string[];\n} {\n const positional: string[] = [];\n let dryRun = false;\n let force = false;\n let all = false;\n let withHooks = false;\n for (const a of args) {\n if (a === \"--dry-run\") dryRun = true;\n else if (a === \"--force\") force = true;\n else if (a === \"--all\") all = true;\n else if (a === \"--with-hooks\") withHooks = true;\n else if (!a.startsWith(\"-\")) positional.push(a);\n }\n return { dryRun, force, all, withHooks, positional };\n}\n\nexport async function runAdapter(\n adapter: ConnectAdapter,\n opts: ConnectOptions,\n): Promise<ConnectResult> {\n if (!adapter.detect()) {\n p.log.warn(\n `${adapter.displayName}: not detected on this machine (skipping).${adapter.docs ? ` Docs: ${adapter.docs}` : \"\"}`,\n );\n return { kind: \"skipped\", reason: \"not-detected\" };\n }\n p.log.step(`Wiring ${adapter.displayName}…`);\n if (adapter.protocolNote) {\n p.log.message(adapter.protocolNote);\n }\n try {\n return await adapter.install(opts);\n } catch (err) {\n p.log.error(\n `${adapter.displayName}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return { kind: \"skipped\", reason: \"exception\" };\n }\n}\n\nexport async function runConnect(args: string[]): Promise<void> {\n if (platform() === \"win32\") {\n p.intro(\"agentmemory connect\");\n p.log.warn(\n \"Windows: automated `connect` is not supported yet. See https://github.com/rohitg00/agentmemory#other-agents for manual install steps.\",\n );\n p.outro(\"Windows: manual install required — see docs\");\n return;\n }\n\n const { dryRun, force, all, withHooks, positional } = parseFlags(args);\n const opts: ConnectOptions = { dryRun, force, withHooks };\n\n p.intro(\"agentmemory connect\");\n\n if (positional.length === 0 && !all) {\n const detected = ADAPTERS.filter((a) => a.detect());\n if (detected.length === 0) {\n p.log.error(\"No supported agents detected on this machine.\");\n p.outro(`Supported: ${knownAgents().join(\", \")}`);\n process.exit(1);\n }\n const picked = await p.multiselect<string>({\n message: \"Wire agentmemory into which agents?\",\n options: detected.map((a) => ({ value: a.name, label: a.displayName })),\n required: true,\n });\n if (p.isCancel(picked)) {\n p.cancel(\"Cancelled.\");\n return;\n }\n const results: { name: string; result: ConnectResult }[] = [];\n for (const name of picked as string[]) {\n const adapter = resolveAdapter(name);\n if (!adapter) continue;\n results.push({ name, result: await runAdapter(adapter, opts) });\n }\n summarize(results);\n return;\n }\n\n if (all) {\n const detected = ADAPTERS.filter((a) => a.detect());\n if (detected.length === 0) {\n p.log.error(\"No supported agents detected on this machine.\");\n process.exit(1);\n }\n const results: { name: string; result: ConnectResult }[] = [];\n for (const adapter of detected) {\n results.push({\n name: adapter.name,\n result: await runAdapter(adapter, opts),\n });\n }\n summarize(results);\n return;\n }\n\n const agentName = positional[0]!;\n const adapter = resolveAdapter(agentName);\n if (!adapter) {\n p.log.error(`Unknown agent: ${agentName}`);\n p.outro(`Supported: ${knownAgents().join(\", \")}`);\n process.exit(1);\n }\n\n const result = await runAdapter(adapter, opts);\n summarize([{ name: agentName, result }]);\n if (result.kind === \"skipped\" && (result as { reason: string }).reason !== \"not-detected\") {\n process.exit(1);\n }\n}\n\nfunction summarize(\n results: { name: string; result: ConnectResult }[],\n): void {\n const lines = results.map(({ name, result }) => {\n switch (result.kind) {\n case \"installed\":\n return ` ✓ ${name}${result.mutatedPath ? ` → ${result.mutatedPath}` : \"\"}`;\n case \"already-wired\":\n return ` ✓ ${name} (already wired)`;\n case \"stub\":\n return ` ⚠ ${name} (manual install required: ${result.reason})`;\n case \"skipped\":\n return ` ✗ ${name} (skipped: ${result.reason})`;\n }\n });\n p.note(lines.join(\"\\n\"), \"summary\");\n\n const stubs = results.filter((r) => r.result.kind === \"stub\");\n if (stubs.length > 0) {\n p.log.info(\n `${stubs.length} agent(s) require manual install — see docs links above.`,\n );\n }\n p.outro(\"Restart any wired agent (or open a new session) to pick up agentmemory.\");\n}\n"],"mappings":"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"}
@@ -23,7 +23,7 @@ async function main() {
23
23
  }
24
24
  if (isSdkChildContext(data)) return;
25
25
  const sessionId = data.session_id || "unknown";
26
- const { imageData, cleanOutput } = extractImageData(data.tool_output);
26
+ const { imageData, cleanOutput } = extractImageData(data.tool_response ?? data.tool_output);
27
27
  try {
28
28
  await fetch(`${REST_URL}/agentmemory/observe`, {
29
29
  method: "POST",
@@ -1 +1 @@
1
- {"version":3,"file":"post-tool-use.mjs","names":[],"sources":["../../src/hooks/post-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) return;\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n const { imageData, cleanOutput } = extractImageData(data.tool_output);\n\n try {\n await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"post_tool_use\",\n sessionId,\n project: data.cwd || process.cwd(),\n cwd: data.cwd || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n tool_name: data.tool_name,\n tool_input: data.tool_input,\n tool_output: truncate(cleanOutput, 8000),\n ...(imageData ? { image_data: imageData } : {}),\n },\n }),\n signal: AbortSignal.timeout(3000),\n });\n } catch {\n }\n}\n\nfunction isBase64Image(val: unknown): val is string {\n return typeof val === \"string\" && (\n val.startsWith(\"data:image/\") ||\n val.startsWith(\"iVBORw0KGgo\") ||\n val.startsWith(\"/9j/\")\n );\n}\n\nfunction extractImageData(output: unknown): { imageData: string | undefined; cleanOutput: unknown } {\n if (isBase64Image(output)) {\n return { imageData: output, cleanOutput: \"[image data extracted]\" };\n }\n\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n const obj = output as Record<string, unknown>;\n let imageData: string | undefined;\n const clean: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(obj)) {\n if (!imageData && isBase64Image(val)) {\n imageData = val;\n clean[key] = \"[image data extracted]\";\n } else {\n clean[key] = val;\n }\n }\n\n return { imageData, cleanOutput: clean };\n }\n\n return { imageData: undefined, cleanOutput: output };\n}\n\nfunction truncate(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n return value.slice(0, max) + \"\\n[...truncated]\";\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) return str.slice(0, max) + \"...[truncated]\";\n return value;\n }\n return value;\n}\n\nmain();\n"],"mappings":";;AAEA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAa,KAAK,cAAyB;CAEjD,MAAM,EAAE,WAAW,gBAAgB,iBAAiB,KAAK,YAAY;AAErE,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,uBAAuB;GAC7C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB,UAAU;IACV;IACA,SAAS,KAAK,OAAO,QAAQ,KAAK;IAClC,KAAK,KAAK,OAAO,QAAQ,KAAK;IAC9B,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,MAAM;KACJ,WAAW,KAAK;KAChB,YAAY,KAAK;KACjB,aAAa,SAAS,aAAa,IAAK;KACxC,GAAI,YAAY,EAAE,YAAY,WAAW,GAAG,EAAE;KAC/C;IACF,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;SACI;;AAIV,SAAS,cAAc,KAA6B;AAClD,QAAO,OAAO,QAAQ,aACpB,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,OAAO;;AAI1B,SAAS,iBAAiB,QAA0E;AAClG,KAAI,cAAc,OAAO,CACvB,QAAO;EAAE,WAAW;EAAQ,aAAa;EAA0B;AAGrE,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,OAAO,EAAE;EAC3E,MAAM,MAAM;EACZ,IAAI;EACJ,MAAM,QAAiC,EAAE;AAEzC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,IAAI,CAC1C,KAAI,CAAC,aAAa,cAAc,IAAI,EAAE;AACpC,eAAY;AACZ,SAAM,OAAO;QAEb,OAAM,OAAO;AAIjB,SAAO;GAAE;GAAW,aAAa;GAAO;;AAG1C,QAAO;EAAE,WAAW;EAAW,aAAa;EAAQ;;AAGtD,SAAS,SAAS,OAAgB,KAAsB;AACtD,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,IAC9C,QAAO,MAAM,MAAM,GAAG,IAAI,GAAG;AAE/B,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,MAAM,KAAK,UAAU,MAAM;AACjC,MAAI,IAAI,SAAS,IAAK,QAAO,IAAI,MAAM,GAAG,IAAI,GAAG;AACjD,SAAO;;AAET,QAAO;;AAGT,MAAM"}
1
+ {"version":3,"file":"post-tool-use.mjs","names":[],"sources":["../../src/hooks/post-tool-use.ts"],"sourcesContent":["#!/usr/bin/env node\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) return;\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n const { imageData, cleanOutput } = extractImageData(\n data.tool_response ?? data.tool_output,\n );\n\n try {\n await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"post_tool_use\",\n sessionId,\n project: data.cwd || process.cwd(),\n cwd: data.cwd || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n tool_name: data.tool_name,\n tool_input: data.tool_input,\n tool_output: truncate(cleanOutput, 8000),\n ...(imageData ? { image_data: imageData } : {}),\n },\n }),\n signal: AbortSignal.timeout(3000),\n });\n } catch {\n }\n}\n\nfunction isBase64Image(val: unknown): val is string {\n return typeof val === \"string\" && (\n val.startsWith(\"data:image/\") ||\n val.startsWith(\"iVBORw0KGgo\") ||\n val.startsWith(\"/9j/\")\n );\n}\n\nfunction extractImageData(output: unknown): { imageData: string | undefined; cleanOutput: unknown } {\n if (isBase64Image(output)) {\n return { imageData: output, cleanOutput: \"[image data extracted]\" };\n }\n\n if (typeof output === \"object\" && output !== null && !Array.isArray(output)) {\n const obj = output as Record<string, unknown>;\n let imageData: string | undefined;\n const clean: Record<string, unknown> = {};\n\n for (const [key, val] of Object.entries(obj)) {\n if (!imageData && isBase64Image(val)) {\n imageData = val;\n clean[key] = \"[image data extracted]\";\n } else {\n clean[key] = val;\n }\n }\n\n return { imageData, cleanOutput: clean };\n }\n\n return { imageData: undefined, cleanOutput: output };\n}\n\nfunction truncate(value: unknown, max: number): unknown {\n if (typeof value === \"string\" && value.length > max) {\n return value.slice(0, max) + \"\\n[...truncated]\";\n }\n if (typeof value === \"object\" && value !== null) {\n const str = JSON.stringify(value);\n if (str.length > max) return str.slice(0, max) + \"...[truncated]\";\n return value;\n }\n return value;\n}\n\nmain();\n"],"mappings":";;AAEA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,YAAa,KAAK,cAAyB;CAEjD,MAAM,EAAE,WAAW,gBAAgB,iBACjC,KAAK,iBAAiB,KAAK,YAC5B;AAED,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,uBAAuB;GAC7C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB,UAAU;IACV;IACA,SAAS,KAAK,OAAO,QAAQ,KAAK;IAClC,KAAK,KAAK,OAAO,QAAQ,KAAK;IAC9B,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,MAAM;KACJ,WAAW,KAAK;KAChB,YAAY,KAAK;KACjB,aAAa,SAAS,aAAa,IAAK;KACxC,GAAI,YAAY,EAAE,YAAY,WAAW,GAAG,EAAE;KAC/C;IACF,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;SACI;;AAIV,SAAS,cAAc,KAA6B;AAClD,QAAO,OAAO,QAAQ,aACpB,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,cAAc,IAC7B,IAAI,WAAW,OAAO;;AAI1B,SAAS,iBAAiB,QAA0E;AAClG,KAAI,cAAc,OAAO,CACvB,QAAO;EAAE,WAAW;EAAQ,aAAa;EAA0B;AAGrE,KAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,OAAO,EAAE;EAC3E,MAAM,MAAM;EACZ,IAAI;EACJ,MAAM,QAAiC,EAAE;AAEzC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,IAAI,CAC1C,KAAI,CAAC,aAAa,cAAc,IAAI,EAAE;AACpC,eAAY;AACZ,SAAM,OAAO;QAEb,OAAM,OAAO;AAIjB,SAAO;GAAE;GAAW,aAAa;GAAO;;AAG1C,QAAO;EAAE,WAAW;EAAW,aAAa;EAAQ;;AAGtD,SAAS,SAAS,OAAgB,KAAsB;AACtD,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,IAC9C,QAAO,MAAM,MAAM,GAAG,IAAI,GAAG;AAE/B,KAAI,OAAO,UAAU,YAAY,UAAU,MAAM;EAC/C,MAAM,MAAM,KAAK,UAAU,MAAM;AACjC,MAAI,IAAI,SAAS,IAAK,QAAO,IAAI,MAAM,GAAG,IAAI,GAAG;AACjD,SAAO;;AAET,QAAO;;AAGT,MAAM"}
@@ -31,6 +31,14 @@ async function main() {
31
31
  signal: AbortSignal.timeout(12e4)
32
32
  });
33
33
  } catch {}
34
+ try {
35
+ await fetch(`${REST_URL}/agentmemory/session/end`, {
36
+ method: "POST",
37
+ headers: authHeaders(),
38
+ body: JSON.stringify({ sessionId }),
39
+ signal: AbortSignal.timeout(5e3)
40
+ });
41
+ } catch {}
34
42
  }
35
43
  main();
36
44
 
@@ -1 +1 @@
1
- {"version":3,"file":"stop.mjs","names":[],"sources":["../../src/hooks/stop.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Inlined — see src/hooks/sdk-guard.ts for canonical version. Kept local\n// per-hook so tsdown does not emit a shared hashed chunk that would churn\n// the diff on every rebuild.\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) {\n // Do not summarize from inside a Claude Agent SDK child session;\n // would re-enter agent-sdk provider and loop (see sdk-guard.ts).\n return;\n }\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n await fetch(`${REST_URL}/agentmemory/summarize`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId }),\n signal: AbortSignal.timeout(120000), // Increased from 30s to 120s\n });\n } catch {\n // summarize is best-effort\n }\n}\n\nmain();"],"mappings":";;AAKA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAGzB;CAGF,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,yBAAyB;GAC/C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;GACnC,QAAQ,YAAY,QAAQ,KAAO;GACpC,CAAC;SACI;;AAKV,MAAM"}
1
+ {"version":3,"file":"stop.mjs","names":[],"sources":["../../src/hooks/stop.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Inlined — see src/hooks/sdk-guard.ts for canonical version. Kept local\n// per-hook so tsdown does not emit a shared hashed chunk that would churn\n// the diff on every rebuild.\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (isSdkChildContext(data)) {\n // Do not summarize from inside a Claude Agent SDK child session;\n // would re-enter agent-sdk provider and loop (see sdk-guard.ts).\n return;\n }\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n await fetch(`${REST_URL}/agentmemory/summarize`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId }),\n signal: AbortSignal.timeout(120000), // Increased from 30s to 120s\n });\n } catch {\n // summarize is best-effort\n }\n\n // Claude Code fires a separate `SessionEnd` hook that closes the\n // viewer session lifecycle. Codex does not have a SessionEnd event,\n // so the only signal we get when a Codex session ends is this Stop\n // hook (#493). Always best-effort POST /agentmemory/session/end here\n // so the viewer shows `completed` for Codex sessions; for Claude Code\n // this is a harmless idempotent second call (session-end.mjs runs on\n // SessionEnd and sets the same fields).\n try {\n await fetch(`${REST_URL}/agentmemory/session/end`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({ sessionId }),\n signal: AbortSignal.timeout(5000),\n });\n } catch {\n // session/end is best-effort\n }\n}\n\nmain();"],"mappings":";;AAKA,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,kBAAkB,KAAK,CAGzB;CAGF,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,yBAAyB;GAC/C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;GACnC,QAAQ,YAAY,QAAQ,KAAO;GACpC,CAAC;SACI;AAWR,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,2BAA2B;GACjD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC;GACnC,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;SACI;;AAKV,MAAM"}
@@ -1,4 +1,4 @@
1
- import { i as KV } from "./cli.mjs";
1
+ import { t as KV } from "./schema-BkALl7Z_.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join, resolve, sep } from "node:path";
4
4
  import { homedir } from "node:os";
@@ -113,4 +113,4 @@ async function decrementImageRef(kv, sdk, filePath) {
113
113
 
114
114
  //#endregion
115
115
  export { isManagedImagePath as a, decrementImageRef, getImageRefCount, getMaxBytes as i, incrementImageRef, IMAGES_DIR as n, saveImageToDisk as o, deleteImage as r, touchImage as s, withKeyedLock as t };
116
- //# sourceMappingURL=image-refs-R3tin9MR.mjs.map
116
+ //# sourceMappingURL=image-refs-CJS5B9Gq.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-refs-R3tin9MR.mjs","names":[],"sources":["../src/utils/image-store.ts","../src/state/keyed-mutex.ts","../src/functions/image-refs.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, writeFile, unlink, utimes, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\n\nexport const IMAGES_DIR = join(homedir(), \".agentmemory\", \"images\");\n\nconst DEFAULT_MAX_BYTES = 500 * 1024 * 1024;\n\nexport function getMaxBytes(): number {\n return Number(process.env.AGENTMEMORY_IMAGE_STORE_MAX_BYTES) || DEFAULT_MAX_BYTES;\n}\n\nexport function isManagedImagePath(filePath: string): boolean {\n const resolved = resolve(filePath);\n const normalizedImagesDir = resolve(IMAGES_DIR);\n return resolved.startsWith(normalizedImagesDir + sep) || resolved === normalizedImagesDir;\n}\n\nfunction contentHash(data: string): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nexport async function saveImageToDisk(base64Data: string): Promise<{ filePath: string; bytesWritten: number }> {\n if (!base64Data) return { filePath: \"\", bytesWritten: 0 };\n\n if (!existsSync(IMAGES_DIR)) {\n await mkdir(IMAGES_DIR, { recursive: true });\n }\n\n let cleanBase64 = base64Data;\n let ext = \"png\";\n\n if (base64Data.startsWith(\"data:image/\")) {\n const commaIdx = base64Data.indexOf(\",\");\n if (commaIdx !== -1) {\n const meta = base64Data.substring(0, commaIdx);\n if (meta.includes(\"jpeg\") || meta.includes(\"jpg\")) ext = \"jpg\";\n else if (meta.includes(\"webp\")) ext = \"webp\";\n else if (meta.includes(\"gif\")) ext = \"gif\";\n cleanBase64 = base64Data.substring(commaIdx + 1);\n }\n } else if (base64Data.startsWith(\"/9j/\")) {\n ext = \"jpg\";\n }\n\n const hash = contentHash(cleanBase64);\n const filePath = join(IMAGES_DIR, `${hash}.${ext}`);\n\n if (existsSync(filePath)) {\n return { filePath, bytesWritten: 0 };\n }\n\n const buffer = Buffer.from(cleanBase64, \"base64\");\n await writeFile(filePath, buffer);\n\n const s = await stat(filePath);\n\n return { filePath, bytesWritten: s.size };\n}\n\nexport async function deleteImage(filePath: string | undefined): Promise<{ deletedBytes: number }> {\n if (!filePath) return { deletedBytes: 0 };\n if (!isManagedImagePath(filePath)) return { deletedBytes: 0 };\n try {\n if (existsSync(filePath)) {\n const s = await stat(filePath);\n const size = s.size;\n await unlink(filePath);\n return { deletedBytes: size };\n }\n } catch (err) {\n console.error(\"[agentmemory] Failed to delete image context:\", err);\n }\n return { deletedBytes: 0 };\n}\n\n/** Touch an image file to update its mtime (marking it as recently used for LRU eviction) */\nexport async function touchImage(filePath: string): Promise<void> {\n if (!filePath || !isManagedImagePath(filePath)) return;\n try {\n if (existsSync(filePath)) {\n const now = new Date();\n await utimes(filePath, now, now);\n }\n } catch (err) {\n // Ignore touch errors silently\n }\n}\n","const locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const cleanup = next.then(\n () => {},\n () => {},\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n return next;\n}\n","import type { ISdk } from \"iii-sdk\";\nimport { KV } from \"../state/schema.js\";\nimport { StateKV } from \"../state/kv.js\";\nimport { deleteImage, touchImage } from \"../utils/image-store.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\n\nexport async function getImageRefCount(kv: StateKV, filePath: string): Promise<number> {\n const count = await kv.get<number>(KV.imageRefs, filePath);\n return count ? Number(count) : 0;\n}\n\nexport async function incrementImageRef(kv: StateKV, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n await kv.set(KV.imageRefs, filePath, current + 1);\n await touchImage(filePath);\n });\n}\n\nexport async function decrementImageRef(kv: StateKV, sdk: ISdk, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n if (current <= 1) {\n await kv.delete(KV.imageEmbeddings, filePath);\n await kv.delete(KV.imageRefs, filePath);\n const { deletedBytes } = await deleteImage(filePath);\n if (deletedBytes > 0) {\n sdk.triggerVoid(\"mem::disk-size-delta\", { deltaBytes: -deletedBytes });\n }\n } else {\n await kv.set(KV.imageRefs, filePath, current - 1);\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"image-refs-CJS5B9Gq.mjs","names":[],"sources":["../src/utils/image-store.ts","../src/state/keyed-mutex.ts","../src/functions/image-refs.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, writeFile, unlink, utimes, stat } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\n\nexport const IMAGES_DIR = join(homedir(), \".agentmemory\", \"images\");\n\nconst DEFAULT_MAX_BYTES = 500 * 1024 * 1024;\n\nexport function getMaxBytes(): number {\n return Number(process.env.AGENTMEMORY_IMAGE_STORE_MAX_BYTES) || DEFAULT_MAX_BYTES;\n}\n\nexport function isManagedImagePath(filePath: string): boolean {\n const resolved = resolve(filePath);\n const normalizedImagesDir = resolve(IMAGES_DIR);\n return resolved.startsWith(normalizedImagesDir + sep) || resolved === normalizedImagesDir;\n}\n\nfunction contentHash(data: string): string {\n return createHash(\"sha256\").update(data).digest(\"hex\");\n}\n\nexport async function saveImageToDisk(base64Data: string): Promise<{ filePath: string; bytesWritten: number }> {\n if (!base64Data) return { filePath: \"\", bytesWritten: 0 };\n\n if (!existsSync(IMAGES_DIR)) {\n await mkdir(IMAGES_DIR, { recursive: true });\n }\n\n let cleanBase64 = base64Data;\n let ext = \"png\";\n\n if (base64Data.startsWith(\"data:image/\")) {\n const commaIdx = base64Data.indexOf(\",\");\n if (commaIdx !== -1) {\n const meta = base64Data.substring(0, commaIdx);\n if (meta.includes(\"jpeg\") || meta.includes(\"jpg\")) ext = \"jpg\";\n else if (meta.includes(\"webp\")) ext = \"webp\";\n else if (meta.includes(\"gif\")) ext = \"gif\";\n cleanBase64 = base64Data.substring(commaIdx + 1);\n }\n } else if (base64Data.startsWith(\"/9j/\")) {\n ext = \"jpg\";\n }\n\n const hash = contentHash(cleanBase64);\n const filePath = join(IMAGES_DIR, `${hash}.${ext}`);\n\n if (existsSync(filePath)) {\n return { filePath, bytesWritten: 0 };\n }\n\n const buffer = Buffer.from(cleanBase64, \"base64\");\n await writeFile(filePath, buffer);\n\n const s = await stat(filePath);\n\n return { filePath, bytesWritten: s.size };\n}\n\nexport async function deleteImage(filePath: string | undefined): Promise<{ deletedBytes: number }> {\n if (!filePath) return { deletedBytes: 0 };\n if (!isManagedImagePath(filePath)) return { deletedBytes: 0 };\n try {\n if (existsSync(filePath)) {\n const s = await stat(filePath);\n const size = s.size;\n await unlink(filePath);\n return { deletedBytes: size };\n }\n } catch (err) {\n console.error(\"[agentmemory] Failed to delete image context:\", err);\n }\n return { deletedBytes: 0 };\n}\n\n/** Touch an image file to update its mtime (marking it as recently used for LRU eviction) */\nexport async function touchImage(filePath: string): Promise<void> {\n if (!filePath || !isManagedImagePath(filePath)) return;\n try {\n if (existsSync(filePath)) {\n const now = new Date();\n await utimes(filePath, now, now);\n }\n } catch (err) {\n // Ignore touch errors silently\n }\n}\n","const locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const cleanup = next.then(\n () => {},\n () => {},\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n return next;\n}\n","import type { ISdk } from \"iii-sdk\";\nimport { KV } from \"../state/schema.js\";\nimport { StateKV } from \"../state/kv.js\";\nimport { deleteImage, touchImage } from \"../utils/image-store.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\n\nexport async function getImageRefCount(kv: StateKV, filePath: string): Promise<number> {\n const count = await kv.get<number>(KV.imageRefs, filePath);\n return count ? Number(count) : 0;\n}\n\nexport async function incrementImageRef(kv: StateKV, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n await kv.set(KV.imageRefs, filePath, current + 1);\n await touchImage(filePath);\n });\n}\n\nexport async function decrementImageRef(kv: StateKV, sdk: ISdk, filePath: string): Promise<void> {\n return withKeyedLock(`imgRef:${filePath}`, async () => {\n const current = await getImageRefCount(kv, filePath);\n if (current <= 1) {\n await kv.delete(KV.imageEmbeddings, filePath);\n await kv.delete(KV.imageRefs, filePath);\n const { deletedBytes } = await deleteImage(filePath);\n if (deletedBytes > 0) {\n sdk.triggerVoid(\"mem::disk-size-delta\", { deltaBytes: -deletedBytes });\n }\n } else {\n await kv.set(KV.imageRefs, filePath, current - 1);\n }\n });\n}\n"],"mappings":";;;;;;;;AAMA,MAAa,aAAa,KAAK,SAAS,EAAE,gBAAgB,SAAS;AAEnE,MAAM,oBAAoB,MAAM,OAAO;AAEvC,SAAgB,cAAsB;AACpC,QAAO,OAAO,QAAQ,IAAI,kCAAkC,IAAI;;AAGlE,SAAgB,mBAAmB,UAA2B;CAC5D,MAAM,WAAW,QAAQ,SAAS;CAClC,MAAM,sBAAsB,QAAQ,WAAW;AAC/C,QAAO,SAAS,WAAW,sBAAsB,IAAI,IAAI,aAAa;;AAGxE,SAAS,YAAY,MAAsB;AACzC,QAAO,WAAW,SAAS,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM;;AAGxD,eAAsB,gBAAgB,YAAyE;AAC7G,KAAI,CAAC,WAAY,QAAO;EAAE,UAAU;EAAI,cAAc;EAAG;AAEzD,KAAI,CAAC,WAAW,WAAW,CACzB,OAAM,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;CAG9C,IAAI,cAAc;CAClB,IAAI,MAAM;AAEV,KAAI,WAAW,WAAW,cAAc,EAAE;EACvC,MAAM,WAAW,WAAW,QAAQ,IAAI;AACxC,MAAI,aAAa,IAAI;GACnB,MAAM,OAAO,WAAW,UAAU,GAAG,SAAS;AAC9C,OAAI,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,MAAM,CAAE,OAAM;YAChD,KAAK,SAAS,OAAO,CAAE,OAAM;YAC7B,KAAK,SAAS,MAAM,CAAE,OAAM;AACrC,iBAAc,WAAW,UAAU,WAAW,EAAE;;YAE1C,WAAW,WAAW,OAAO,CACrC,OAAM;CAIT,MAAM,WAAW,KAAK,YAAY,GADrB,YAAY,YAAY,CACK,GAAG,MAAM;AAEnD,KAAI,WAAW,SAAS,CACtB,QAAO;EAAE;EAAU,cAAc;EAAG;AAItC,OAAM,UAAU,UADD,OAAO,KAAK,aAAa,SAAS,CAChB;AAIjC,QAAO;EAAE;EAAU,eAFT,MAAM,KAAK,SAAS,EAEK;EAAM;;AAG3C,eAAsB,YAAY,UAAiE;AACjG,KAAI,CAAC,SAAU,QAAO,EAAE,cAAc,GAAG;AACzC,KAAI,CAAC,mBAAmB,SAAS,CAAE,QAAO,EAAE,cAAc,GAAG;AAC7D,KAAI;AACF,MAAI,WAAW,SAAS,EAAE;GAExB,MAAM,QADI,MAAM,KAAK,SAAS,EACf;AACf,SAAM,OAAO,SAAS;AACtB,UAAO,EAAE,cAAc,MAAM;;UAExB,KAAK;AACZ,UAAQ,MAAM,iDAAiD,IAAI;;AAErE,QAAO,EAAE,cAAc,GAAG;;;AAI5B,eAAsB,WAAW,UAAiC;AAChE,KAAI,CAAC,YAAY,CAAC,mBAAmB,SAAS,CAAE;AAChD,KAAI;AACF,MAAI,WAAW,SAAS,EAAE;GACxB,MAAM,sBAAM,IAAI,MAAM;AACtB,SAAM,OAAO,UAAU,KAAK,IAAI;;UAE3B,KAAK;;;;;ACtFhB,MAAM,wBAAQ,IAAI,KAA4B;AAE9C,SAAgB,cACd,KACA,IACY;CAEZ,MAAM,QADO,MAAM,IAAI,IAAI,IAAI,QAAQ,SAAS,EAC9B,KAAK,IAAI,GAAG;CAC9B,MAAM,UAAU,KAAK,WACb,UACA,GACP;AACD,OAAM,IAAI,KAAK,QAAQ;AACvB,SAAQ,WAAW;AACjB,MAAI,MAAM,IAAI,IAAI,KAAK,QAAS,OAAM,OAAO,IAAI;GACjD;AACF,QAAO;;;;;ACVT,eAAsB,iBAAiB,IAAa,UAAmC;CACrF,MAAM,QAAQ,MAAM,GAAG,IAAY,GAAG,WAAW,SAAS;AAC1D,QAAO,QAAQ,OAAO,MAAM,GAAG;;AAGjC,eAAsB,kBAAkB,IAAa,UAAiC;AACpF,QAAO,cAAc,UAAU,YAAY,YAAY;EACrD,MAAM,UAAU,MAAM,iBAAiB,IAAI,SAAS;AACpD,QAAM,GAAG,IAAI,GAAG,WAAW,UAAU,UAAU,EAAE;AACjD,QAAM,WAAW,SAAS;GAC1B;;AAGJ,eAAsB,kBAAkB,IAAa,KAAW,UAAiC;AAC/F,QAAO,cAAc,UAAU,YAAY,YAAY;EACrD,MAAM,UAAU,MAAM,iBAAiB,IAAI,SAAS;AACpD,MAAI,WAAW,GAAG;AAChB,SAAM,GAAG,OAAO,GAAG,iBAAiB,SAAS;AAC7C,SAAM,GAAG,OAAO,GAAG,WAAW,SAAS;GACvC,MAAM,EAAE,iBAAiB,MAAM,YAAY,SAAS;AACpD,OAAI,eAAe,EACjB,KAAI,YAAY,wBAAwB,EAAE,YAAY,CAAC,cAAc,CAAC;QAGxE,OAAM,GAAG,IAAI,GAAG,WAAW,UAAU,UAAU,EAAE;GAEnD"}
@@ -1,3 +1,3 @@
1
- import { a as isManagedImagePath, i as getMaxBytes, n as IMAGES_DIR, o as saveImageToDisk, r as deleteImage, s as touchImage } from "./image-refs-R3tin9MR.mjs";
1
+ import { a as isManagedImagePath, i as getMaxBytes, n as IMAGES_DIR, o as saveImageToDisk, r as deleteImage, s as touchImage } from "./image-refs-CJS5B9Gq.mjs";
2
2
 
3
3
  export { deleteImage, saveImageToDisk };