@agentmemory/agentmemory 0.9.21 → 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 (47) hide show
  1. package/README.md +150 -7
  2. package/dist/cli.d.mts +5 -1
  3. package/dist/cli.d.mts.map +1 -0
  4. package/dist/cli.mjs +103 -692
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/connect-BQQXpyDS.mjs +763 -0
  7. package/dist/connect-BQQXpyDS.mjs.map +1 -0
  8. package/dist/hooks/post-tool-use.mjs +1 -1
  9. package/dist/hooks/post-tool-use.mjs.map +1 -1
  10. package/dist/hooks/stop.mjs +8 -0
  11. package/dist/hooks/stop.mjs.map +1 -1
  12. package/dist/{image-refs-R3tin9MR.mjs → image-refs-CJS5B9Gq.mjs} +2 -2
  13. package/dist/{image-refs-R3tin9MR.mjs.map → image-refs-CJS5B9Gq.mjs.map} +1 -1
  14. package/dist/{image-store-DyrKZKqZ.mjs → image-store-CdE0amb1.mjs} +1 -1
  15. package/dist/index.mjs +450 -242
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/logger-xlVlvCWX.mjs +43 -0
  18. package/dist/logger-xlVlvCWX.mjs.map +1 -0
  19. package/dist/schema-BkALl7Z_.mjs +74 -0
  20. package/dist/schema-BkALl7Z_.mjs.map +1 -0
  21. package/dist/{src-D5arboxc.mjs → src-gpTAJuBy.mjs} +428 -243
  22. package/dist/src-gpTAJuBy.mjs.map +1 -0
  23. package/dist/{standalone-C7BgzzIN.mjs → standalone-C4i7ktpn.mjs} +18 -6
  24. package/dist/standalone-C4i7ktpn.mjs.map +1 -0
  25. package/dist/standalone.d.mts.map +1 -1
  26. package/dist/standalone.mjs +15 -4
  27. package/dist/standalone.mjs.map +1 -1
  28. package/dist/{tools-registry-CRTWUFw9.mjs → tools-registry-B7Y6nJsr.mjs} +36 -11
  29. package/dist/tools-registry-B7Y6nJsr.mjs.map +1 -0
  30. package/dist/version-DvQMNbEH.mjs +6 -0
  31. package/dist/version-DvQMNbEH.mjs.map +1 -0
  32. package/dist/viewer/index.html +77 -9
  33. package/package.json +6 -4
  34. package/plugin/.claude-plugin/plugin.json +1 -1
  35. package/plugin/.codex-plugin/plugin.json +1 -1
  36. package/plugin/.mcp.json +3 -2
  37. package/plugin/opencode/agentmemory-capture.ts +34 -9
  38. package/plugin/scripts/diagnostics.d.mts +17 -0
  39. package/plugin/scripts/diagnostics.d.mts.map +1 -0
  40. package/plugin/scripts/diagnostics.mjs.map +1 -0
  41. package/plugin/scripts/post-tool-use.mjs +1 -1
  42. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  43. package/plugin/scripts/stop.mjs +8 -0
  44. package/plugin/scripts/stop.mjs.map +1 -1
  45. package/dist/src-D5arboxc.mjs.map +0 -1
  46. package/dist/standalone-C7BgzzIN.mjs.map +0 -1
  47. package/dist/tools-registry-CRTWUFw9.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-registry-B7Y6nJsr.mjs","names":[],"sources":["../src/config.ts","../src/mcp/tools-registry.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type {\n AgentMemoryConfig,\n ProviderConfig,\n EmbeddingConfig,\n FallbackConfig,\n ClaudeBridgeConfig,\n TeamConfig,\n} from \"./types.js\";\n\nfunction safeParseInt(value: string | undefined, fallback: number): number {\n if (!value) return fallback;\n const parsed = parseInt(value, 10);\n return Number.isNaN(parsed) ? fallback : parsed;\n}\n\nconst DATA_DIR = join(homedir(), \".agentmemory\");\nconst ENV_FILE = join(DATA_DIR, \".env\");\n\nlet warnPremiumModelShown = false;\n\nfunction loadEnvFile(): Record<string, string> {\n if (!existsSync(ENV_FILE)) return {};\n const content = readFileSync(ENV_FILE, \"utf-8\");\n const vars: Record<string, string> = {};\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n const quoteChar = val[0] === '\"' || val[0] === \"'\" ? val[0] : \"\";\n if (quoteChar) {\n const closeIdx = val.indexOf(quoteChar, 1);\n if (closeIdx !== -1) val = val.slice(1, closeIdx);\n } else {\n const hashIdx = val.indexOf(\" #\");\n if (hashIdx !== -1) val = val.slice(0, hashIdx).trim();\n }\n vars[key] = val;\n }\n return vars;\n}\n\nfunction hasRealValue(v: string | undefined): v is string {\n return typeof v === \"string\" && v.trim().length > 0;\n}\n\nfunction detectProvider(env: Record<string, string>): ProviderConfig {\n const maxTokens = parseInt(env[\"MAX_TOKENS\"] || \"4096\", 10);\n\n // OpenAI-compatible: supports OpenAI, DeepSeek, SiliconFlow, Azure, vLLM, LM Studio\n if (hasRealValue(env[\"OPENAI_API_KEY\"]) && env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\") {\n return {\n provider: \"openai\",\n model: env[\"OPENAI_MODEL\"] || \"gpt-4o-mini\",\n maxTokens,\n baseURL: env[\"OPENAI_BASE_URL\"],\n };\n }\n\n // MiniMax: Anthropic-compatible API, requires raw fetch to avoid SDK stainless headers\n if (hasRealValue(env[\"MINIMAX_API_KEY\"])) {\n return {\n provider: \"minimax\",\n model: env[\"MINIMAX_MODEL\"] || \"MiniMax-M2.7\",\n maxTokens,\n };\n }\n\n if (hasRealValue(env[\"ANTHROPIC_API_KEY\"])) {\n return {\n provider: \"anthropic\",\n model: env[\"ANTHROPIC_MODEL\"] || \"claude-sonnet-4-20250514\",\n maxTokens,\n baseURL: env[\"ANTHROPIC_BASE_URL\"],\n };\n }\n if (hasRealValue(env[\"GEMINI_API_KEY\"]) || hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n if (!hasRealValue(env[\"GEMINI_API_KEY\"]) && hasRealValue(env[\"GOOGLE_API_KEY\"])) {\n process.stderr.write(\n \"[agentmemory] GOOGLE_API_KEY detected — treating as GEMINI_API_KEY. \" +\n \"Set GEMINI_API_KEY in ~/.agentmemory/.env to silence this warning.\\n\",\n );\n }\n return {\n provider: \"gemini\",\n model: env[\"GEMINI_MODEL\"] || \"gemini-2.5-flash\",\n maxTokens,\n };\n }\n if (hasRealValue(env[\"OPENROUTER_API_KEY\"])) {\n const model =\n env[\"OPENROUTER_MODEL\"] || \"anthropic/claude-sonnet-4-20250514\";\n // warn when the configured OpenRouter model is in the\n // premium tier and likely to burn money on background compression.\n // Captured workload data shows ~$5/35h on claude-sonnet-4 vs\n // ~$0.46/35h on deepseek-v4-pro for the same compression mix.\n // Heuristic match avoids hard-coding a pricing table.\n if (\n !warnPremiumModelShown &&\n /sonnet|opus|gpt-4o(?!.*mini)|gpt-4-turbo/i.test(model) &&\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] !== \"1\" &&\n env[\"AGENTMEMORY_SUPPRESS_COST_WARNING\"] !== \"true\"\n ) {\n warnPremiumModelShown = true;\n process.stderr.write(\n `[agentmemory] OPENROUTER_MODEL=${model} is in the premium tier. ` +\n `Background compression on this model can cost $5+/day under active use. ` +\n `Cheaper alternatives with comparable quality for memory compression: ` +\n `deepseek/deepseek-v4-pro, deepseek/deepseek-chat, qwen/qwen3-coder. ` +\n `See README \"Cost-aware model selection\" for the full table. ` +\n `Set AGENTMEMORY_SUPPRESS_COST_WARNING=1 to silence.\\n`,\n );\n }\n return {\n provider: \"openrouter\",\n model,\n maxTokens,\n };\n }\n\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n if (!allowAgentSdk) {\n process.stderr.write(\n \"[agentmemory] No LLM provider key found \" +\n \"(ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, MINIMAX_API_KEY, OPENAI_API_KEY). \" +\n \"LLM-backed compression and summarization are DISABLED — using no-op provider. \" +\n \"This is the safe default: the agent-sdk fallback used to spawn Claude Agent SDK \" +\n \"child sessions which inherit Claude Code's plugin hooks and cause infinite Stop-hook \" +\n \"recursion (#149 follow-up). To opt in to the agent-sdk fallback anyway, set both \" +\n \"AGENTMEMORY_AUTO_COMPRESS=true AND AGENTMEMORY_ALLOW_AGENT_SDK=true — but be aware \" +\n \"it will burn your Claude Pro allocation and may still recurse if you use it from \" +\n \"inside Claude Code itself.\\n\",\n );\n return {\n provider: \"noop\",\n model: \"noop\",\n maxTokens,\n };\n }\n\n process.stderr.write(\n \"[agentmemory] WARNING: agent-sdk fallback enabled via AGENTMEMORY_ALLOW_AGENT_SDK=true. \" +\n \"This spawns @anthropic-ai/claude-agent-sdk child sessions that can trigger the Stop-hook \" +\n \"recursion loop (#149 follow-up). A SDK-child env marker is set to block re-entry, \" +\n \"but prefer setting a real API key in ~/.agentmemory/.env instead.\\n\",\n );\n return {\n provider: \"agent-sdk\",\n model: \"claude-sonnet-4-20250514\",\n maxTokens,\n };\n}\n\nexport function loadConfig(): AgentMemoryConfig {\n const env = getMergedEnv();\n\n const provider = detectProvider(env);\n\n return {\n engineUrl: env[\"III_ENGINE_URL\"] || \"ws://localhost:49134\",\n restPort: parseInt(env[\"III_REST_PORT\"] || \"3111\", 10) || 3111,\n streamsPort: parseInt(env[\"III_STREAMS_PORT\"] || \"3112\", 10) || 3112,\n provider,\n tokenBudget: safeParseInt(env[\"TOKEN_BUDGET\"], 2000),\n maxObservationsPerSession: safeParseInt(env[\"MAX_OBS_PER_SESSION\"], 500),\n compressionModel: provider.model,\n dataDir: DATA_DIR,\n };\n}\n\nfunction getMergedEnv(\n overrides?: Record<string, string>,\n): Record<string, string> {\n const fileEnv = loadEnvFile();\n return { ...fileEnv, ...process.env, ...overrides } as Record<string, string>;\n}\n\nexport function getEnvVar(key: string): string | undefined {\n return getMergedEnv()[key];\n}\n\nexport function isDropStaleIndexEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_DROP_STALE_INDEX\"] === \"true\";\n}\n\nexport function detectLlmProviderKind(): \"llm\" | \"noop\" {\n const env = getMergedEnv();\n if (\n hasRealValue(env[\"ANTHROPIC_API_KEY\"]) ||\n hasRealValue(env[\"GEMINI_API_KEY\"]) ||\n hasRealValue(env[\"GOOGLE_API_KEY\"]) ||\n hasRealValue(env[\"OPENROUTER_API_KEY\"]) ||\n hasRealValue(env[\"MINIMAX_API_KEY\"]) ||\n (hasRealValue(env[\"OPENAI_API_KEY\"]) &&\n env[\"OPENAI_API_KEY_FOR_LLM\"] !== \"false\")\n ) {\n return \"llm\";\n }\n return \"noop\";\n}\n\nexport function loadEmbeddingConfig(): EmbeddingConfig {\n const env = getMergedEnv();\n let bm25Weight = parseFloat(env[\"BM25_WEIGHT\"] || \"0.4\");\n let vectorWeight = parseFloat(env[\"VECTOR_WEIGHT\"] || \"0.6\");\n bm25Weight =\n isNaN(bm25Weight) || bm25Weight < 0 ? 0.4 : Math.min(bm25Weight, 1);\n vectorWeight =\n isNaN(vectorWeight) || vectorWeight < 0 ? 0.6 : Math.min(vectorWeight, 1);\n return {\n provider: env[\"EMBEDDING_PROVIDER\"] || undefined,\n bm25Weight,\n vectorWeight,\n };\n}\n\nexport function detectEmbeddingProvider(\n env?: Record<string, string>,\n): string | null {\n const source = env ?? getMergedEnv();\n const forced = source[\"EMBEDDING_PROVIDER\"];\n if (forced) return forced;\n\n if (source[\"GEMINI_API_KEY\"]) return \"gemini\";\n if (source[\"OPENAI_API_KEY\"]) return \"openai\";\n if (source[\"VOYAGE_API_KEY\"]) return \"voyage\";\n if (source[\"COHERE_API_KEY\"]) return \"cohere\";\n if (source[\"OPENROUTER_API_KEY\"]) return \"openrouter\";\n return null;\n}\n\nexport function loadClaudeBridgeConfig(): ClaudeBridgeConfig {\n const env = getMergedEnv();\n const enabled = env[\"CLAUDE_MEMORY_BRIDGE\"] === \"true\";\n const projectPath = env[\"CLAUDE_PROJECT_PATH\"] || \"\";\n const lineBudget = safeParseInt(env[\"CLAUDE_MEMORY_LINE_BUDGET\"], 200);\n let memoryFilePath = \"\";\n if (enabled && projectPath) {\n // Claude Code stores MEMORY.md at\n // ~/.claude/projects/<slug>/MEMORY.md\n // where <slug> is the project path with `/` and `\\` swapped for `-`.\n // The leading `-` from an absolute POSIX path is preserved (Claude\n // Code keeps it; stripping it produced a slug Claude never reads).\n // There's also no `memory/` subdirectory — the file sits directly\n // under the slug dir.\n const safePath = projectPath.replace(/[/\\\\]/g, \"-\");\n memoryFilePath = join(\n homedir(),\n \".claude\",\n \"projects\",\n safePath,\n \"MEMORY.md\",\n );\n }\n return { enabled, projectPath, memoryFilePath, lineBudget };\n}\n\nexport function loadTeamConfig(): TeamConfig | null {\n const env = getMergedEnv();\n const teamId = env[\"TEAM_ID\"];\n const userId = env[\"USER_ID\"];\n if (!teamId || !userId) return null;\n const mode = env[\"TEAM_MODE\"] === \"shared\" ? \"shared\" : \"private\";\n return { teamId, userId, mode };\n}\n\n// optional AGENT_ID env for multi-agent memory isolation.\n// Returns null when unset so memory stays unscoped (legacy behavior).\n// Trimmed + length-capped to keep KV writes well-formed.\n//\n// Filtering is gated by AGENTMEMORY_AGENT_SCOPE:\n// \"shared\" (default) — tag everything, do not filter recall paths\n// \"isolated\" — tag everything AND filter recall paths\nexport function loadAgentScope(): {\n agentId: string;\n mode: \"shared\" | \"isolated\";\n} | null {\n const env = getMergedEnv();\n const raw = env[\"AGENT_ID\"];\n if (!raw) return null;\n const agentId = raw.trim().slice(0, 128);\n if (!agentId) return null;\n const mode = env[\"AGENTMEMORY_AGENT_SCOPE\"] === \"isolated\"\n ? \"isolated\"\n : \"shared\";\n return { agentId, mode };\n}\n\nexport function getAgentId(): string | undefined {\n return loadAgentScope()?.agentId;\n}\n\n// True only when AGENT_ID is set AND scope=isolated. Recall paths\n// consult this to decide whether to filter.\nexport function isAgentScopeIsolated(): boolean {\n return loadAgentScope()?.mode === \"isolated\";\n}\n\nexport function loadSnapshotConfig(): {\n enabled: boolean;\n interval: number;\n dir: string;\n} {\n const env = getMergedEnv();\n return {\n enabled: env[\"SNAPSHOT_ENABLED\"] === \"true\",\n interval: safeParseInt(env[\"SNAPSHOT_INTERVAL\"], 3600),\n dir: env[\"SNAPSHOT_DIR\"] || join(homedir(), \".agentmemory\", \"snapshots\"),\n };\n}\n\nexport function isGraphExtractionEnabled(): boolean {\n return getMergedEnv()[\"GRAPH_EXTRACTION_ENABLED\"] === \"true\";\n}\n\nexport function getGraphBatchSize(): number {\n return safeParseInt(getMergedEnv()[\"GRAPH_EXTRACTION_BATCH_SIZE\"], 10);\n}\n\nexport function isConsolidationEnabled(): boolean {\n return getMergedEnv()[\"CONSOLIDATION_ENABLED\"] === \"true\";\n}\n\n// Per-observation LLM compression is OFF by default as of 0.8.8 (see #138).\n// When disabled, observations are captured and indexed via a synthetic\n// (zero-LLM) compression path so recall/search still works. Users who want\n// richer LLM-generated summaries can set AGENTMEMORY_AUTO_COMPRESS=true in\n// ~/.agentmemory/.env — but should expect their Claude API token usage to\n// climb proportionally with session tool-use frequency.\nexport function isAutoCompressEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_AUTO_COMPRESS\"] === \"true\";\n}\n\n// Hook-level context injection into Claude Code's conversation is OFF by\n// default as of 0.8.10 (see #143). When disabled, pre-tool-use and\n// session-start hooks still POST observations for background capture, but\n// never write context to stdout — so Claude Code doesn't inject an extra\n// ~4000-char blob into every tool turn. 0.8.8 stopped the agentmemory-side\n// Claude calls (via ANTHROPIC_API_KEY); this stops the Claude Code-side\n// token burn where every tool call silently grew the model input window.\n// Users who want the in-conversation context injection explicitly opt in\n// with AGENTMEMORY_INJECT_CONTEXT=true and get a loud startup warning.\nexport function isContextInjectionEnabled(): boolean {\n return getMergedEnv()[\"AGENTMEMORY_INJECT_CONTEXT\"] === \"true\";\n}\n\nexport function getConsolidationDecayDays(): number {\n return safeParseInt(getMergedEnv()[\"CONSOLIDATION_DECAY_DAYS\"], 30);\n}\n\nexport function isStandaloneMcp(): boolean {\n return getMergedEnv()[\"STANDALONE_MCP\"] === \"true\";\n}\n\nexport function getStandalonePersistPath(): string {\n const env = getMergedEnv();\n return (\n env[\"STANDALONE_PERSIST_PATH\"] ||\n join(homedir(), \".agentmemory\", \"standalone.json\")\n );\n}\n\nconst VALID_PROVIDERS = new Set([\n \"anthropic\",\n \"gemini\",\n \"openrouter\",\n \"agent-sdk\",\n \"minimax\",\n \"openai\",\n]);\n\nexport function loadFallbackConfig(): FallbackConfig {\n const env = getMergedEnv();\n const raw = env[\"FALLBACK_PROVIDERS\"] || \"\";\n const allowAgentSdk = env[\"AGENTMEMORY_ALLOW_AGENT_SDK\"] === \"true\";\n const providers = raw\n .split(\",\")\n .map((p) => p.trim())\n .filter(\n (p): p is FallbackConfig[\"providers\"][number] =>\n Boolean(p) && VALID_PROVIDERS.has(p),\n )\n .filter((p) => {\n // Honor the same safety gate as detectProvider: agent-sdk is only\n // permitted as a fallback target when the user has explicitly opted\n // in. Without this filter, a user could set FALLBACK_PROVIDERS=agent-sdk\n // and re-introduce the Stop-hook recursion loop even though\n // detectProvider() returned the noop provider.\n if (p === \"agent-sdk\" && !allowAgentSdk) {\n process.stderr.write(\n \"[agentmemory] Ignoring FALLBACK_PROVIDERS entry 'agent-sdk' \" +\n \"(AGENTMEMORY_ALLOW_AGENT_SDK is not 'true'). The agent-sdk \" +\n \"fallback can spawn Claude Agent SDK child sessions that trigger \" +\n \"the Stop-hook recursion loop (#149 follow-up). Opt in explicitly \" +\n \"with AGENTMEMORY_ALLOW_AGENT_SDK=true if this is intentional.\\n\",\n );\n return false;\n }\n return true;\n });\n return { providers };\n}\n","export type McpToolDef = {\n name: string;\n description: string;\n inputSchema: {\n type: \"object\";\n properties: Record<string, { type: string; description: string }>;\n required?: string[];\n };\n};\n\nexport const CORE_TOOLS: McpToolDef[] = [\n {\n name: \"memory_recall\",\n description:\n \"Search past session observations for relevant context. Use when you need to recall what happened in previous sessions, find past decisions, or look up how a file was modified before.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: {\n type: \"string\",\n description: \"Search query (keywords, file names, concepts)\",\n },\n limit: {\n type: \"number\",\n description: \"Max results to return (default 10)\",\n },\n format: {\n type: \"string\",\n description: \"Result format: full, compact, or narrative (default full)\",\n },\n token_budget: {\n type: \"number\",\n description: \"Optional token budget to trim returned results\",\n },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_compress_file\",\n description:\n \"Compress a markdown file to reduce token usage while preserving headings, URLs, and code blocks. Creates a .original.md backup before writing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n filePath: {\n type: \"string\",\n description: \"Path to the markdown file to compress\",\n },\n },\n required: [\"filePath\"],\n },\n },\n {\n name: \"memory_save\",\n description:\n \"Explicitly save an important insight, decision, or pattern to long-term memory.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The insight or decision to remember\",\n },\n type: {\n type: \"string\",\n description:\n \"Memory type: pattern, preference, architecture, bug, workflow, or fact\",\n },\n concepts: {\n type: \"string\",\n description: \"Comma-separated key concepts\",\n },\n files: {\n type: \"string\",\n description: \"Comma-separated relevant file paths\",\n },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_file_history\",\n description: \"Get past observations about specific files.\",\n inputSchema: {\n type: \"object\",\n properties: {\n files: { type: \"string\", description: \"Comma-separated file paths\" },\n sessionId: {\n type: \"string\",\n description: \"Current session ID to exclude\",\n },\n },\n required: [\"files\"],\n },\n },\n {\n name: \"memory_patterns\",\n description: \"Detect recurring patterns across sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path to analyze\" },\n },\n },\n },\n {\n name: \"memory_sessions\",\n description:\n \"List recent sessions with their status and observation counts.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_smart_search\",\n description: \"Hybrid semantic+keyword search with progressive disclosure.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n expandIds: {\n type: \"string\",\n description: \"Comma-separated observation IDs to expand\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_vision_search\",\n description:\n \"Cross-modal image search via CLIP embeddings. Pass queryText to find screenshots matching a description, or queryImageBase64/queryImageRef to find similar images. Requires AGENTMEMORY_IMAGE_EMBEDDINGS=true.\",\n inputSchema: {\n type: \"object\",\n properties: {\n queryText: { type: \"string\", description: \"Text query (e.g. 'login form with error banner')\" },\n queryImageRef: { type: \"string\", description: \"Absolute path to a stored image to match against\" },\n queryImageBase64: { type: \"string\", description: \"Raw base64 image bytes or data URL\" },\n topK: { type: \"number\", description: \"Max results (default 10, max 50)\" },\n sessionId: { type: \"string\", description: \"Filter to a single session\" },\n },\n },\n },\n {\n name: \"memory_timeline\",\n description: \"Chronological observations around an anchor point.\",\n inputSchema: {\n type: \"object\",\n properties: {\n anchor: {\n type: \"string\",\n description: \"Anchor point: ISO date or keyword\",\n },\n project: { type: \"string\", description: \"Filter by project path\" },\n before: {\n type: \"number\",\n description: \"Observations before anchor (default 5)\",\n },\n after: {\n type: \"number\",\n description: \"Observations after anchor (default 5)\",\n },\n },\n required: [\"anchor\"],\n },\n },\n {\n name: \"memory_profile\",\n description: \"User/project profile with top concepts and file patterns.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Project path\" },\n refresh: {\n type: \"string\",\n description: \"Set to 'true' to force rebuild\",\n },\n },\n required: [\"project\"],\n },\n },\n {\n name: \"memory_export\",\n description: \"Export all memory data as JSON.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_relations\",\n description: \"Query the memory relationship graph.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryId: {\n type: \"string\",\n description: \"Memory ID to find relations for\",\n },\n maxHops: {\n type: \"number\",\n description: \"Max traversal depth (default 2)\",\n },\n minConfidence: {\n type: \"number\",\n description: \"Min confidence (0-1, default 0)\",\n },\n },\n required: [\"memoryId\"],\n },\n },\n {\n name: \"memory_commit_lookup\",\n description:\n \"Look up the agent session(s) that produced a specific git commit, given its SHA. Returns the commit metadata and linked sessions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sha: { type: \"string\", description: \"Full git commit SHA\" },\n },\n required: [\"sha\"],\n },\n },\n {\n name: \"memory_commits\",\n description:\n \"List recent commits linked to agent sessions, optionally filtered by branch or repo.\",\n inputSchema: {\n type: \"object\",\n properties: {\n branch: { type: \"string\", description: \"Filter by branch name\" },\n repo: { type: \"string\", description: \"Filter by remote URL\" },\n limit: { type: \"number\", description: \"Max results (default 100, max 500)\" },\n },\n },\n },\n];\n\nexport const V040_TOOLS: McpToolDef[] = [\n {\n name: \"memory_claude_bridge_sync\",\n description:\n \"Sync memory state to/from Claude Code's native MEMORY.md file.\",\n inputSchema: {\n type: \"object\",\n properties: {\n direction: {\n type: \"string\",\n description:\n \"'read' to import from MEMORY.md, 'write' to export to MEMORY.md\",\n },\n },\n required: [\"direction\"],\n },\n },\n {\n name: \"memory_graph_query\",\n description: \"Query the knowledge graph for entities and relationships.\",\n inputSchema: {\n type: \"object\",\n properties: {\n startNodeId: {\n type: \"string\",\n description: \"Starting node ID for traversal\",\n },\n nodeType: { type: \"string\", description: \"Filter by node type\" },\n maxDepth: {\n type: \"number\",\n description: \"Max BFS depth (default 3, max 5)\",\n },\n query: { type: \"string\", description: \"Search nodes by name\" },\n },\n },\n },\n {\n name: \"memory_consolidate\",\n description:\n \"Run the 4-tier memory consolidation pipeline (working -> episodic -> semantic -> procedural).\",\n inputSchema: {\n type: \"object\",\n properties: {\n tier: {\n type: \"string\",\n description: \"Target tier: episodic, semantic, or procedural\",\n },\n },\n },\n },\n {\n name: \"memory_team_share\",\n description: \"Share a memory or observation with team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n itemId: {\n type: \"string\",\n description: \"ID of memory or observation to share\",\n },\n itemType: {\n type: \"string\",\n description: \"Type: observation, memory, or pattern\",\n },\n },\n required: [\"itemId\", \"itemType\"],\n },\n },\n {\n name: \"memory_team_feed\",\n description: \"Get recent shared items from all team members.\",\n inputSchema: {\n type: \"object\",\n properties: {\n limit: { type: \"number\", description: \"Max items (default 20)\" },\n },\n },\n },\n {\n name: \"memory_audit\",\n description: \"View the audit trail of memory operations.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: { type: \"string\", description: \"Filter by operation type\" },\n limit: { type: \"number\", description: \"Max entries (default 50)\" },\n },\n },\n },\n {\n name: \"memory_governance_delete\",\n description: \"Delete specific memories with audit trail.\",\n inputSchema: {\n type: \"object\",\n properties: {\n memoryIds: {\n type: \"string\",\n description: \"Comma-separated memory IDs to delete\",\n },\n reason: { type: \"string\", description: \"Reason for deletion\" },\n },\n required: [\"memoryIds\"],\n },\n },\n {\n name: \"memory_snapshot_create\",\n description: \"Create a git-versioned snapshot of current memory state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Snapshot description\" },\n },\n },\n },\n];\n\nexport const V050_TOOLS: McpToolDef[] = [\n {\n name: \"memory_action_create\",\n description:\n \"Create an actionable work item with typed dependencies. Actions track what agents need to do and how work items relate to each other.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Action title\" },\n description: {\n type: \"string\",\n description: \"Detailed description of the work\",\n },\n priority: {\n type: \"number\",\n description: \"Priority 1-10 (10 highest)\",\n },\n project: { type: \"string\", description: \"Project path\" },\n tags: {\n type: \"string\",\n description: \"Comma-separated tags\",\n },\n parentId: {\n type: \"string\",\n description: \"Parent action ID for hierarchical actions\",\n },\n requires: {\n type: \"string\",\n description:\n \"Comma-separated action IDs that must complete before this\",\n },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_action_update\",\n description:\n \"Update an action's status, priority, or details. Set status to 'done' to complete it and unblock dependent actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID to update\" },\n status: {\n type: \"string\",\n description: \"New status: pending, active, done, blocked, cancelled\",\n },\n result: {\n type: \"string\",\n description: \"Outcome description (when completing)\",\n },\n priority: { type: \"number\", description: \"New priority 1-10\" },\n },\n required: [\"actionId\"],\n },\n },\n {\n name: \"memory_frontier\",\n description:\n \"Get all unblocked actions ranked by priority and urgency. Returns the frontier of actionable work with no unsatisfied dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: {\n type: \"string\",\n description: \"Agent ID to check lease conflicts\",\n },\n limit: { type: \"number\", description: \"Max results (default 20)\" },\n },\n },\n },\n {\n name: \"memory_next\",\n description:\n \"Get the single most important next action to work on. Combines dependency resolution, priority, and recency into a score.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n agentId: { type: \"string\", description: \"Current agent ID\" },\n },\n },\n },\n {\n name: \"memory_lease\",\n description:\n \"Acquire, release, or renew an exclusive lease on an action. Prevents multiple agents from working on the same thing.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionId: { type: \"string\", description: \"Action ID\" },\n agentId: { type: \"string\", description: \"Agent claiming the action\" },\n operation: {\n type: \"string\",\n description: \"acquire, release, or renew\",\n },\n result: {\n type: \"string\",\n description: \"Result when releasing (marks action done)\",\n },\n ttlMs: {\n type: \"number\",\n description: \"Lease duration in ms (default 10min, max 1hr)\",\n },\n },\n required: [\"actionId\", \"agentId\", \"operation\"],\n },\n },\n {\n name: \"memory_routine_run\",\n description:\n \"Instantiate a frozen workflow routine, creating actions for each step with proper dependencies.\",\n inputSchema: {\n type: \"object\",\n properties: {\n routineId: { type: \"string\", description: \"Routine template ID\" },\n project: { type: \"string\", description: \"Project context\" },\n initiatedBy: { type: \"string\", description: \"Agent starting the run\" },\n },\n required: [\"routineId\"],\n },\n },\n {\n name: \"memory_signal_send\",\n description:\n \"Send a message to another agent or broadcast. Supports threading, typed messages, and TTL expiration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n from: { type: \"string\", description: \"Sender agent ID\" },\n to: {\n type: \"string\",\n description: \"Recipient agent ID (omit for broadcast)\",\n },\n content: { type: \"string\", description: \"Message content\" },\n type: {\n type: \"string\",\n description: \"Message type: info, request, response, alert, handoff\",\n },\n replyTo: {\n type: \"string\",\n description: \"Signal ID to reply to (auto-threads)\",\n },\n },\n required: [\"from\", \"content\"],\n },\n },\n {\n name: \"memory_signal_read\",\n description:\n \"Read messages for an agent. Marks delivered messages as read.\",\n inputSchema: {\n type: \"object\",\n properties: {\n agentId: { type: \"string\", description: \"Agent to read messages for\" },\n unreadOnly: {\n type: \"string\",\n description: \"Set to 'true' for unread only\",\n },\n threadId: {\n type: \"string\",\n description: \"Filter by conversation thread\",\n },\n limit: { type: \"number\", description: \"Max messages (default 50)\" },\n },\n required: [\"agentId\"],\n },\n },\n {\n name: \"memory_checkpoint\",\n description:\n \"Create or resolve an external checkpoint (CI result, approval, deploy status) that gates action progress.\",\n inputSchema: {\n type: \"object\",\n properties: {\n operation: {\n type: \"string\",\n description: \"create, resolve, or list\",\n },\n name: { type: \"string\", description: \"Checkpoint name (for create)\" },\n checkpointId: {\n type: \"string\",\n description: \"Checkpoint ID (for resolve)\",\n },\n status: {\n type: \"string\",\n description: \"passed or failed (for resolve)\",\n },\n type: {\n type: \"string\",\n description: \"Checkpoint type: ci, approval, deploy, external, timer\",\n },\n linkedActionIds: {\n type: \"string\",\n description:\n \"Comma-separated action IDs this checkpoint gates (for create)\",\n },\n },\n required: [\"operation\"],\n },\n },\n {\n name: \"memory_mesh_sync\",\n description:\n \"Sync memories and actions with peer agentmemory instances for multi-agent collaboration.\",\n inputSchema: {\n type: \"object\",\n properties: {\n peerId: {\n type: \"string\",\n description: \"Specific peer ID (omit for all)\",\n },\n direction: {\n type: \"string\",\n description: \"push, pull, or both (default both)\",\n },\n },\n },\n },\n];\n\nexport const V051_TOOLS: McpToolDef[] = [\n {\n name: \"memory_sentinel_create\",\n description:\n \"Create an event-driven sentinel that watches for conditions (webhook, timer, threshold, pattern, approval) and auto-unblocks gated actions when triggered.\",\n inputSchema: {\n type: \"object\",\n properties: {\n name: { type: \"string\", description: \"Sentinel name\" },\n type: {\n type: \"string\",\n description: \"Type: webhook, timer, threshold, pattern, approval, custom\",\n },\n config: {\n type: \"string\",\n description: \"JSON config (timer: {durationMs}, threshold: {metric,operator,value}, pattern: {pattern}, webhook: {path})\",\n },\n linkedActionIds: {\n type: \"string\",\n description: \"Comma-separated action IDs to gate\",\n },\n expiresInMs: { type: \"number\", description: \"Auto-expire after ms\" },\n },\n required: [\"name\", \"type\"],\n },\n },\n {\n name: \"memory_sentinel_trigger\",\n description:\n \"Externally fire a sentinel, providing an optional result payload. Unblocks any gated actions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sentinelId: { type: \"string\", description: \"Sentinel ID to trigger\" },\n result: { type: \"string\", description: \"JSON result payload\" },\n },\n required: [\"sentinelId\"],\n },\n },\n {\n name: \"memory_sketch_create\",\n description:\n \"Create an ephemeral action graph for exploratory work. Auto-expires after TTL. Can be promoted to permanent actions or discarded.\",\n inputSchema: {\n type: \"object\",\n properties: {\n title: { type: \"string\", description: \"Sketch title\" },\n description: { type: \"string\", description: \"What this sketch explores\" },\n expiresInMs: { type: \"number\", description: \"TTL in ms (default 1 hour)\" },\n project: { type: \"string\", description: \"Project context\" },\n },\n required: [\"title\"],\n },\n },\n {\n name: \"memory_sketch_promote\",\n description:\n \"Promote a sketch's ephemeral actions to permanent actions. Makes the exploratory work official.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sketchId: { type: \"string\", description: \"Sketch ID to promote\" },\n project: { type: \"string\", description: \"Override project for promoted actions\" },\n },\n required: [\"sketchId\"],\n },\n },\n {\n name: \"memory_crystallize\",\n description:\n \"Compress completed action chains into compact crystal digests using LLM summarization. Extracts narrative, key outcomes, files affected, and lessons.\",\n inputSchema: {\n type: \"object\",\n properties: {\n actionIds: {\n type: \"string\",\n description: \"Comma-separated completed action IDs to crystallize\",\n },\n project: { type: \"string\", description: \"Project context\" },\n sessionId: { type: \"string\", description: \"Session context\" },\n },\n required: [\"actionIds\"],\n },\n },\n {\n name: \"memory_diagnose\",\n description:\n \"Run health checks across all subsystems (actions, leases, sentinels, sketches, signals, sessions, memories, mesh). Identifies stuck, orphaned, and inconsistent state.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to check (default all)\",\n },\n },\n },\n },\n {\n name: \"memory_heal\",\n description:\n \"Auto-fix all fixable issues found by diagnostics. Unblocks stuck actions, expires stale leases, cleans up orphaned data.\",\n inputSchema: {\n type: \"object\",\n properties: {\n categories: {\n type: \"string\",\n description: \"Comma-separated categories to heal (default all)\",\n },\n dryRun: {\n type: \"string\",\n description: \"Set to 'true' for dry run (report but don't fix)\",\n },\n },\n },\n },\n {\n name: \"memory_facet_tag\",\n description:\n \"Attach a structured tag (dimension:value) to an action, memory, or observation for multi-dimensional categorization.\",\n inputSchema: {\n type: \"object\",\n properties: {\n targetId: { type: \"string\", description: \"ID of the target to tag\" },\n targetType: {\n type: \"string\",\n description: \"Type: action, memory, or observation\",\n },\n dimension: { type: \"string\", description: \"Tag dimension (e.g., priority, team, status)\" },\n value: { type: \"string\", description: \"Tag value (e.g., urgent, backend, reviewed)\" },\n },\n required: [\"targetId\", \"targetType\", \"dimension\", \"value\"],\n },\n },\n {\n name: \"memory_facet_query\",\n description:\n \"Query targets by facet tags with AND/OR logic. Find all actions tagged priority:urgent AND team:backend.\",\n inputSchema: {\n type: \"object\",\n properties: {\n matchAll: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (AND logic)\",\n },\n matchAny: {\n type: \"string\",\n description: \"Comma-separated dimension:value pairs (OR logic)\",\n },\n targetType: {\n type: \"string\",\n description: \"Filter by type: action, memory, or observation\",\n },\n },\n },\n },\n];\n\nexport const V061_TOOLS: McpToolDef[] = [\n {\n name: \"memory_verify\",\n description:\n \"Verify a memory or observation by tracing its citation chain back to source observations and session context. Returns provenance information including confidence scores.\",\n inputSchema: {\n type: \"object\",\n properties: {\n id: {\n type: \"string\",\n description: \"Memory ID or observation ID to verify\",\n },\n },\n required: [\"id\"],\n },\n },\n];\n\nexport const V070_TOOLS: McpToolDef[] = [\n {\n name: \"memory_lesson_save\",\n description:\n \"Save a lesson learned from this session. Lessons have confidence scores that strengthen when reinforced and decay when not used. Duplicate content auto-strengthens the existing lesson.\",\n inputSchema: {\n type: \"object\",\n properties: {\n content: {\n type: \"string\",\n description: \"The lesson learned (what worked, what to avoid, when to use X approach)\",\n },\n context: {\n type: \"string\",\n description: \"When/where this lesson applies\",\n },\n confidence: {\n type: \"number\",\n description: \"Initial confidence 0.0-1.0 (default 0.5)\",\n },\n project: { type: \"string\", description: \"Project this lesson is about\" },\n tags: { type: \"string\", description: \"Comma-separated tags\" },\n },\n required: [\"content\"],\n },\n },\n {\n name: \"memory_lesson_recall\",\n description:\n \"Search lessons by query. Returns lessons sorted by confidence and recency. Use to check what the agent has learned before making decisions.\",\n inputSchema: {\n type: \"object\",\n properties: {\n query: { type: \"string\", description: \"Search query\" },\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0.1)\",\n },\n limit: { type: \"number\", description: \"Max results (default 10)\" },\n },\n required: [\"query\"],\n },\n },\n {\n name: \"memory_obsidian_export\",\n description:\n \"Export memories, lessons, and crystals as Obsidian-compatible Markdown files with YAML frontmatter and wikilinks for graph view.\",\n inputSchema: {\n type: \"object\",\n properties: {\n vaultDir: {\n type: \"string\",\n description: \"Output directory (default ~/.agentmemory/vault/)\",\n },\n types: {\n type: \"string\",\n description: \"Comma-separated types to export: memories,lessons,crystals,sessions (default all)\",\n },\n },\n },\n },\n];\n\nexport const V073_TOOLS: McpToolDef[] = [\n {\n name: \"memory_reflect\",\n description:\n \"Traverse the knowledge graph, group related memories by concept clusters, and synthesize higher-order insights via LLM. Returns new and reinforced insights.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n maxClusters: {\n type: \"number\",\n description: \"Max concept clusters to process (default 10, max 20)\",\n },\n },\n },\n },\n {\n name: \"memory_insight_list\",\n description:\n \"List synthesized insights — higher-order observations derived from patterns across memories, lessons, and crystals.\",\n inputSchema: {\n type: \"object\",\n properties: {\n project: { type: \"string\", description: \"Filter by project\" },\n minConfidence: {\n type: \"number\",\n description: \"Minimum confidence threshold (default 0)\",\n },\n limit: { type: \"number\", description: \"Max results (default 50)\" },\n },\n },\n },\n];\n\nexport const V010_SLOTS_TOOLS: McpToolDef[] = [\n {\n name: \"memory_slot_list\",\n description:\n \"List all memory slots (pinned + project + global). Slots are editable, size-limited memory units the agent can read and modify across sessions.\",\n inputSchema: { type: \"object\", properties: {} },\n },\n {\n name: \"memory_slot_get\",\n description: \"Read a single slot by label.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label (e.g. 'persona', 'pending_items')\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_create\",\n description: \"Create a new slot. Reject if a slot with the same label already exists.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label — lowercase, starts with letter, [a-z0-9_]\" },\n content: { type: \"string\", description: \"Initial content (default empty)\" },\n sizeLimit: { type: \"number\", description: \"Max chars (default 2000, hard cap 20000)\" },\n description: { type: \"string\", description: \"What this slot is for\" },\n pinned: { type: \"string\", description: \"'false' to exclude from context injection; default true\" },\n scope: { type: \"string\", description: \"'project' (default) or 'global' (shared across projects)\" },\n },\n required: [\"label\"],\n },\n },\n {\n name: \"memory_slot_append\",\n description:\n \"Append text to an existing slot. Fails with 413 if the append would exceed the slot's sizeLimit — agent must compact via memory_slot_replace first.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n text: { type: \"string\", description: \"Text to append\" },\n },\n required: [\"label\", \"text\"],\n },\n },\n {\n name: \"memory_slot_replace\",\n description: \"Replace slot content in place. Fails if content exceeds sizeLimit.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n content: { type: \"string\", description: \"New full content\" },\n },\n required: [\"label\", \"content\"],\n },\n },\n {\n name: \"memory_slot_delete\",\n description: \"Delete a slot. Seeded default slots can be deleted unless marked readOnly.\",\n inputSchema: {\n type: \"object\",\n properties: {\n label: { type: \"string\", description: \"Slot label\" },\n },\n required: [\"label\"],\n },\n },\n];\n\nconst ESSENTIAL_TOOLS = new Set([\n \"memory_save\",\n \"memory_recall\",\n \"memory_consolidate\",\n \"memory_smart_search\",\n \"memory_sessions\",\n \"memory_diagnose\",\n \"memory_lesson_save\",\n \"memory_reflect\",\n]);\n\nexport function getAllTools(): McpToolDef[] {\n return [\n ...CORE_TOOLS,\n ...V040_TOOLS,\n ...V050_TOOLS,\n ...V051_TOOLS,\n ...V061_TOOLS,\n ...V070_TOOLS,\n ...V073_TOOLS,\n ...V010_SLOTS_TOOLS,\n ];\n}\n\n// default switched from \"core\" (8 essential tools) to \"all\"\n// (full 51-tool surface). README and plugin manifests have always\n// advertised 51 tools \"in proxy mode\"; the old default left OpenCode /\n// Claude Code users seeing 8 with no indication the other 43 existed.\n// Users who want the lean essentials can still set AGENTMEMORY_TOOLS=core.\nexport function getVisibleTools(): McpToolDef[] {\n const mode = process.env[\"AGENTMEMORY_TOOLS\"] || \"all\";\n if (mode === \"core\") return getAllTools().filter((t) => ESSENTIAL_TOOLS.has(t.name));\n return getAllTools();\n}\n"],"mappings":"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CN,YAAY;IACV,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACD,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,cAAc;KACZ,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,QAAQ;GACpB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,UAAU;IACR,MAAM;IACN,aAAa;IACd,EACF;GACD,UAAU,CAAC,WAAW;GACvB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACD,MAAM;KACJ,MAAM;KACN,aACE;KACH;IACD,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,UAAU;GACtB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,OAAO;KAAE,MAAM;KAAU,aAAa;KAA8B;IACpE,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,QAAQ;GACpB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY,EACV,SAAS;IAAE,MAAM;IAAU,aAAa;IAA2B,EACpE;GACF;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GAAE,MAAM;GAAU,YAAY,EAAE;GAAE;EAChD;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,OAAO;KAAE,MAAM;KAAU,aAAa;KAAgB;IACtD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,OAAO;KAAE,MAAM;KAAU,aAAa;KAA4B;IACnE;GACD,UAAU,CAAC,QAAQ;GACpB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW;KAAE,MAAM;KAAU,aAAa;KAAoD;IAC9F,eAAe;KAAE,MAAM;KAAU,aAAa;KAAoD;IAClG,kBAAkB;KAAE,MAAM;KAAU,aAAa;KAAsC;IACvF,MAAM;KAAE,MAAM;KAAU,aAAa;KAAoC;IACzE,WAAW;KAAE,MAAM;KAAU,aAAa;KAA8B;IACzE;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,SAAS;KAAE,MAAM;KAAU,aAAa;KAA0B;IAClE,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,SAAS;GACrB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,SAAS;KAAE,MAAM;KAAU,aAAa;KAAgB;IACxD,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,UAAU;GACtB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GAAE,MAAM;GAAU,YAAY,EAAE;GAAE;EAChD;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,SAAS;KACP,MAAM;KACN,aAAa;KACd;IACD,eAAe;KACb,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,WAAW;GACvB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,KAAK;IAAE,MAAM;IAAU,aAAa;IAAuB,EAC5D;GACD,UAAU,CAAC,MAAM;GAClB;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,QAAQ;KAAE,MAAM;KAAU,aAAa;KAAyB;IAChE,MAAM;KAAE,MAAM;KAAU,aAAa;KAAwB;IAC7D,OAAO;KAAE,MAAM;KAAU,aAAa;KAAsC;IAC7E;GACF;EACF;CACF;AAED,MAAa,aAA2B;CACtC;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,WAAW;IACT,MAAM;IACN,aACE;IACH,EACF;GACD,UAAU,CAAC,YAAY;GACxB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,aAAa;KACX,MAAM;KACN,aAAa;KACd;IACD,UAAU;KAAE,MAAM;KAAU,aAAa;KAAuB;IAChE,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACD,OAAO;KAAE,MAAM;KAAU,aAAa;KAAwB;IAC/D;GACF;EACF;CACD;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY,EACV,MAAM;IACJ,MAAM;IACN,aAAa;IACd,EACF;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACF;GACD,UAAU,CAAC,UAAU,WAAW;GACjC;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY,EACV,OAAO;IAAE,MAAM;IAAU,aAAa;IAA0B,EACjE;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW;KAAE,MAAM;KAAU,aAAa;KAA4B;IACtE,OAAO;KAAE,MAAM;KAAU,aAAa;KAA4B;IACnE;GACF;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,QAAQ;KAAE,MAAM;KAAU,aAAa;KAAuB;IAC/D;GACD,UAAU,CAAC,YAAY;GACxB;EACF;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY,EACV,SAAS;IAAE,MAAM;IAAU,aAAa;IAAwB,EACjE;GACF;EACF;CACF;AAED,MAAa,aAA2B;CACtC;EACE,MAAM;EACN,aACE;EACF,aAAa;GACX,MAAM;GACN,YAAY;IACV,OAAO;KAAE,MAAM;KAAU,aAAa;KAAgB;IACtD,aAAa;KACX,MAAM;K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"}
@@ -0,0 +1,6 @@
1
+ //#region src/version.ts
2
+ const VERSION = "0.9.22";
3
+
4
+ //#endregion
5
+ export { VERSION as t };
6
+ //# sourceMappingURL=version-DvQMNbEH.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-DvQMNbEH.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["export const VERSION = \"0.9.22\";\n"],"mappings":";AAAA,MAAa,UAAU"}
@@ -164,6 +164,8 @@
164
164
 
165
165
  .tab-bar {
166
166
  display: flex;
167
+ height: 48px;
168
+ flex-shrink: 0;
167
169
  border-bottom: 1px solid var(--border-light);
168
170
  background: var(--bg);
169
171
  overflow-x: auto;
@@ -1265,7 +1267,7 @@
1265
1267
  var results = await Promise.all([
1266
1268
  apiGet('health'),
1267
1269
  apiGet('sessions'),
1268
- apiGet('memories?latest=true'),
1270
+ apiGet('memories?latest=true&limit=500'),
1269
1271
  apiGet('graph/stats'),
1270
1272
  apiGet('audit?limit=5'),
1271
1273
  apiGet('semantic'),
@@ -1579,7 +1581,13 @@
1579
1581
  }, 30000);
1580
1582
  }
1581
1583
 
1582
- var graphSim = { nodes: [], edges: [], running: false, canvas: null, ctx: null, raf: null, panX: 0, panY: 0, zoom: 1, dragNode: null, mouseX: 0, mouseY: 0 };
1584
+ var graphSim = { nodes: [], edges: [], running: false, canvas: null, ctx: null, raf: null, panX: 0, panY: 0, zoom: 1, dragNode: null, mouseX: 0, mouseY: 0, tickCount: 0, quietTicks: 0 };
1585
+ function wakeGraphSim() {
1586
+ graphSim.quietTicks = 0;
1587
+ if (graphSim.running && !graphSim.raf) {
1588
+ graphSim.raf = requestAnimationFrame(runSimulation);
1589
+ }
1590
+ }
1583
1591
 
1584
1592
  async function loadGraph() {
1585
1593
  var el = document.getElementById('view-graph');
@@ -1758,6 +1766,8 @@
1758
1766
  }
1759
1767
  lastMX = e.clientX;
1760
1768
  lastMY = e.clientY;
1769
+ // wake the simulation if it parked itself after settling
1770
+ wakeGraphSim();
1761
1771
  });
1762
1772
  canvas.addEventListener('mousemove', function(e) {
1763
1773
  var dx = e.clientX - lastMX;
@@ -1814,6 +1824,9 @@
1814
1824
  e.preventDefault();
1815
1825
  var factor = e.deltaY > 0 ? 0.9 : 1.1;
1816
1826
  graphSim.zoom = Math.max(0.1, Math.min(5, graphSim.zoom * factor));
1827
+ // zoom is visually meaningless if the rAF loop is parked —
1828
+ // wake the simulation so the next frame redraws at the new scale.
1829
+ wakeGraphSim();
1817
1830
  }, { passive: false });
1818
1831
  canvas.addEventListener('dblclick', function(e) {
1819
1832
  var c = canvasCoords(e);
@@ -1828,6 +1841,7 @@
1828
1841
  window.zoomGraph = function(dir) {
1829
1842
  var factor = dir > 0 ? 1.25 : 0.8;
1830
1843
  graphSim.zoom = Math.max(0.1, Math.min(5, graphSim.zoom * factor));
1844
+ wakeGraphSim();
1831
1845
  };
1832
1846
  window.recenterGraph = function() {
1833
1847
  graphSim.zoom = 1;
@@ -1837,6 +1851,7 @@
1837
1851
  graphSim.panX = cw / 2;
1838
1852
  graphSim.panY = ch / 2;
1839
1853
  }
1854
+ wakeGraphSim();
1840
1855
  };
1841
1856
 
1842
1857
  function selectGraphNode(simNode) {
@@ -1898,10 +1913,19 @@
1898
1913
  var nodes = graphSim.nodes;
1899
1914
  var edges = graphSim.edges;
1900
1915
  var nodeCount = nodes.length;
1901
- var damping = 0.9;
1902
- var repulsion = nodeCount > 100 ? 2000 : nodeCount > 50 ? 1200 : 800;
1916
+ graphSim.tickCount = (graphSim.tickCount || 0) + 1;
1917
+ // dense graphs (>1000 nodes) used to oscillate forever
1918
+ // because the per-node force pile-up exceeded what 0.9 damping
1919
+ // could bleed off each tick. Tick-decay tightens damping over
1920
+ // time so the layout actually settles; a per-node velocity cap
1921
+ // prevents any single node from being launched off-screen by an
1922
+ // accumulated kick before damping catches up.
1923
+ var coolBoost = Math.min(0.4, graphSim.tickCount / 1500);
1924
+ var damping = 0.9 - coolBoost;
1925
+ var repulsion = nodeCount > 1000 ? 3000 : nodeCount > 100 ? 2000 : nodeCount > 50 ? 1200 : 800;
1903
1926
  var attraction = nodeCount > 100 ? 0.002 : 0.005;
1904
- var centerGravity = nodeCount > 100 ? 0.005 : 0.01;
1927
+ var centerGravity = nodeCount > 1000 ? 0.012 : nodeCount > 100 ? 0.005 : 0.01;
1928
+ var velocityCap = nodeCount > 1000 ? 6 : nodeCount > 200 ? 12 : 24;
1905
1929
 
1906
1930
  var nodeMap = {};
1907
1931
  nodes.forEach(function(n) { nodeMap[n.id] = n; });
@@ -1921,8 +1945,14 @@
1921
1945
  }
1922
1946
  fx -= n.x * centerGravity;
1923
1947
  fy -= n.y * centerGravity;
1924
- n.vx = (n.vx + fx) * damping;
1925
- n.vy = (n.vy + fy) * damping;
1948
+ var nvx = (n.vx + fx) * damping;
1949
+ var nvy = (n.vy + fy) * damping;
1950
+ // Velocity cap (#563): keep any single node from being launched
1951
+ // off-screen by a one-tick force spike.
1952
+ if (nvx > velocityCap) nvx = velocityCap; else if (nvx < -velocityCap) nvx = -velocityCap;
1953
+ if (nvy > velocityCap) nvy = velocityCap; else if (nvy < -velocityCap) nvy = -velocityCap;
1954
+ n.vx = nvx;
1955
+ n.vy = nvy;
1926
1956
  }
1927
1957
 
1928
1958
  edges.forEach(function(e) {
@@ -1939,13 +1969,28 @@
1939
1969
  if (graphSim.dragNode !== t) { t.vx -= fx; t.vy -= fy; }
1940
1970
  });
1941
1971
 
1972
+ var totalKineticEnergy = 0;
1942
1973
  nodes.forEach(function(n) {
1943
1974
  if (graphSim.dragNode === n) return;
1944
1975
  n.x += n.vx;
1945
1976
  n.y += n.vy;
1977
+ totalKineticEnergy += n.vx * n.vx + n.vy * n.vy;
1946
1978
  });
1947
1979
 
1980
+ // park the simulation when the layout is quiet to save CPU.
1981
+ // Pick up again when a drag/interaction wakes the loop.
1982
+ var rmsVelocity = nodes.length > 0 ? Math.sqrt(totalKineticEnergy / nodes.length) : 0;
1983
+ if (rmsVelocity < 0.05 && graphSim.tickCount > 60 && !graphSim.dragNode) {
1984
+ graphSim.quietTicks = (graphSim.quietTicks || 0) + 1;
1985
+ } else {
1986
+ graphSim.quietTicks = 0;
1987
+ }
1988
+
1948
1989
  renderGraph();
1990
+ if (graphSim.quietTicks > 30) {
1991
+ graphSim.raf = null;
1992
+ return;
1993
+ }
1949
1994
  graphSim.raf = requestAnimationFrame(runSimulation);
1950
1995
  }
1951
1996
 
@@ -2216,8 +2261,12 @@
2216
2261
  async function loadMemories() {
2217
2262
  var el = document.getElementById('view-memories');
2218
2263
  el.innerHTML = '<div class="loading">Loading memories...</div>';
2219
- var result = await apiGet('memories?latest=true');
2264
+ // cap at 2000 so the viewer remains responsive on large
2265
+ // corpora. Older endpoints returned the full unbounded list which
2266
+ // hit the iii invocation timeout and the UI fell through to 0.
2267
+ var result = await apiGet('memories?latest=true&limit=2000');
2220
2268
  state.memories.items = (result && result.memories) || [];
2269
+ state.memories.total = (result && typeof result.total === 'number') ? result.total : state.memories.items.length;
2221
2270
  state.memories.loaded = true;
2222
2271
  renderMemories();
2223
2272
  }
@@ -2230,7 +2279,26 @@
2230
2279
 
2231
2280
  var filtered = items.filter(function(m) {
2232
2281
  if (typeFilter && m.type !== typeFilter) return false;
2233
- if (search && !(m.title || '').toLowerCase().includes(search) && !(m.content || '').toLowerCase().includes(search)) return false;
2282
+ const normalizedSearch = (search || '')
2283
+ .normalize("NFKC")
2284
+ .toLowerCase();
2285
+
2286
+ const normalizedTitle = (m.title || '')
2287
+ .normalize("NFKC")
2288
+ .toLowerCase();
2289
+
2290
+ const normalizedContent = (m.content || '')
2291
+ .normalize("NFKC")
2292
+ .toLowerCase();
2293
+
2294
+ if (
2295
+ search &&
2296
+ !normalizedTitle.includes(normalizedSearch) &&
2297
+ !normalizedContent.includes(normalizedSearch)
2298
+ ) {
2299
+ return false;
2300
+ }
2301
+
2234
2302
  return true;
2235
2303
  });
2236
2304
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentmemory/agentmemory",
3
- "version": "0.9.21",
3
+ "version": "0.9.22",
4
4
  "description": "Persistent memory for AI coding agents, powered by iii-engine's three primitives",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -25,7 +25,9 @@
25
25
  "test:watch": "vitest --exclude test/integration.test.ts",
26
26
  "test:integration": "vitest run test/integration.test.ts",
27
27
  "test:all": "vitest run",
28
- "bench:load": "node --import tsx benchmark/load-100k.ts"
28
+ "bench:load": "node --import tsx benchmark/load-100k.ts",
29
+ "eval:longmemeval": "tsx eval/runner/longmemeval.ts",
30
+ "eval:coding-life": "tsx eval/runner/coding-life.ts"
29
31
  },
30
32
  "keywords": [
31
33
  "ai",
@@ -57,10 +59,10 @@
57
59
  },
58
60
  "dependencies": {
59
61
  "@anthropic-ai/claude-agent-sdk": "^0.3.142",
60
- "@anthropic-ai/sdk": "^0.39.0",
62
+ "@anthropic-ai/sdk": "^0.93.0",
61
63
  "@clack/prompts": "^1.2.0",
62
64
  "dotenv": "^17.4.2",
63
- "iii-sdk": "^0.11.2",
65
+ "iii-sdk": "0.11.2",
64
66
  "zod": "^4.0.0"
65
67
  },
66
68
  "optionalDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmemory",
3
- "version": "0.9.21",
3
+ "version": "0.9.22",
4
4
  "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 51 MCP tools, 4 skills, real-time viewer.",
5
5
  "author": {
6
6
  "name": "Rohit Ghumare",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmemory",
3
- "version": "0.9.21",
3
+ "version": "0.9.22",
4
4
  "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 6 hooks, 51 MCP tools, 4 skills, real-time viewer.",
5
5
  "author": {
6
6
  "name": "Rohit Ghumare",
package/plugin/.mcp.json CHANGED
@@ -4,8 +4,9 @@
4
4
  "command": "npx",
5
5
  "args": ["-y", "@agentmemory/mcp"],
6
6
  "env": {
7
- "AGENTMEMORY_URL": "${AGENTMEMORY_URL}",
8
- "AGENTMEMORY_SECRET": "${AGENTMEMORY_SECRET}"
7
+ "AGENTMEMORY_URL": "${AGENTMEMORY_URL:-http://localhost:3111}",
8
+ "AGENTMEMORY_SECRET": "${AGENTMEMORY_SECRET:-}",
9
+ "AGENTMEMORY_TOOLS": "${AGENTMEMORY_TOOLS:-all}"
9
10
  }
10
11
  }
11
12
  }
@@ -64,6 +64,12 @@ const stashedFiles = new Map<string, Set<string>>();
64
64
  const seenSubtaskIds = new Map<string, Set<string>>();
65
65
  const seenToolCallIds = new Map<string, Set<string>>();
66
66
  const contextInjectedSessions = new Set<string>();
67
+ // cache the context returned by POST /session/start so the chat
68
+ // system-transform hook can inject it without a second /context fetch.
69
+ // Auto-injection now happens at session.created (immediately) AND at
70
+ // the first prompt_submit (fallback for older OpenCode builds that
71
+ // don't implement experimental.chat.system.transform).
72
+ const startContextCache = new Map<string, string>();
67
73
 
68
74
  function stashFor(sid: string): Set<string> {
69
75
  let s = stashedFiles.get(sid);
@@ -178,16 +184,26 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
178
184
  seenSubtaskIds.delete(activeSessionId);
179
185
  seenToolCallIds.delete(activeSessionId);
180
186
  contextInjectedSessions.delete(activeSessionId);
181
- await post("/session/start", {
182
- sessionId: activeSessionId,
187
+ // Snapshot the session id locally — `activeSessionId` is mutable
188
+ // and another `session.created` event during the await could
189
+ // rebind it, causing context to be cached against the wrong key.
190
+ const sessionId = activeSessionId;
191
+ const startResult = await postJson("/session/start", {
192
+ sessionId,
183
193
  title: info?.title ?? null,
184
194
  parentID: info?.parentID ?? null,
185
195
  version: info?.version ?? null,
186
196
  project: projectPath,
187
197
  cwd: projectPath,
188
198
  });
189
- if (pendingConfig && activeSessionId) {
190
- await observe(activeSessionId, "config_loaded", pendingConfig);
199
+ // cache the context returned at session/start so the
200
+ // chat.system.transform hook injects it without a second fetch.
201
+ const startCtx = (startResult as any)?.context;
202
+ if (typeof startCtx === "string" && startCtx.length > 0) {
203
+ startContextCache.set(sessionId, startCtx);
204
+ }
205
+ if (pendingConfig) {
206
+ await observe(sessionId, "config_loaded", pendingConfig);
191
207
  pendingConfig = null;
192
208
  }
193
209
  }
@@ -257,6 +273,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
257
273
  post("/consolidate-pipeline", { tier: "all", force: true }, 30000);
258
274
  if (sid === activeSessionId) activeSessionId = null;
259
275
  stashedFiles.delete(sid);
276
+ startContextCache.delete(sid);
260
277
  seenSubtaskIds.delete(sid);
261
278
  seenToolCallIds.delete(sid);
262
279
  contextInjectedSessions.delete(sid);
@@ -588,11 +605,19 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
588
605
  if (!contextInjectedSessions.has(sid)) {
589
606
  if (!Array.isArray(output.system)) return;
590
607
  output.system.push(AGENTMEMORY_INSTRUCTIONS);
591
- const result = await postJson("/context", {
592
- sessionId: sid,
593
- project: projectPath,
594
- });
595
- const ctx = (result as any)?.context;
608
+ // prefer the context already fetched at session.created;
609
+ // fall back to a fresh /context call if the cache missed (e.g.
610
+ // session resumed across plugin reloads).
611
+ let ctx = startContextCache.get(sid);
612
+ if (typeof ctx !== "string" || ctx.length === 0) {
613
+ const result = await postJson("/context", {
614
+ sessionId: sid,
615
+ project: projectPath,
616
+ });
617
+ ctx = (result as any)?.context;
618
+ } else {
619
+ startContextCache.delete(sid);
620
+ }
596
621
  if (typeof ctx === "string" && ctx.length > 0) {
597
622
  output.system.push(ctx);
598
623
  }
@@ -0,0 +1,17 @@
1
+ import { ISdk } from "iii-sdk";
2
+
3
+ //#region src/state/kv.d.ts
4
+ declare class StateKV {
5
+ private sdk;
6
+ constructor(sdk: ISdk);
7
+ get<T = unknown>(scope: string, key: string): Promise<T | null>;
8
+ set<T = unknown>(scope: string, key: string, data: T): Promise<T>;
9
+ delete(scope: string, key: string): Promise<void>;
10
+ list<T = unknown>(scope: string): Promise<T[]>;
11
+ }
12
+ //#endregion
13
+ //#region src/functions/diagnostics.d.ts
14
+ declare function registerDiagnosticsFunction(sdk: ISdk, kv: StateKV): void;
15
+ //#endregion
16
+ export { registerDiagnosticsFunction };
17
+ //# sourceMappingURL=diagnostics.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.mts","names":[],"sources":["../../src/state/kv.ts","../../src/functions/diagnostics.ts"],"mappings":";;;cAEa,OAAA;EAAA,QACS,GAAA;cAAA,GAAA,EAAK,IAAA;EAEnB,GAAA,aAAA,CAAiB,KAAA,UAAe,GAAA,WAAc,OAAA,CAAQ,CAAA;EAOtD,GAAA,aAAA,CAAiB,KAAA,UAAe,GAAA,UAAa,IAAA,EAAM,CAAA,GAAI,OAAA,CAAQ,CAAA;EAO/D,MAAA,CAAO,KAAA,UAAe,GAAA,WAAc,OAAA;EAOpC,IAAA,aAAA,CAAkB,KAAA,WAAgB,OAAA,CAAQ,CAAA;AAAA;;;iBC0BlC,2BAAA,CAA4B,GAAA,EAAK,IAAA,EAAM,EAAA,EAAI,OAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.mjs","names":[],"sources":["../../src/state/schema.ts","../../src/state/keyed-mutex.ts","../../src/functions/diagnostics.ts"],"sourcesContent":["export const KV = {\n sessions: \"mem:sessions\",\n observations: (sessionId: string) => `mem:obs:${sessionId}`,\n memories: \"mem:memories\",\n summaries: \"mem:summaries\",\n config: \"mem:config\",\n metrics: \"mem:metrics\",\n health: \"mem:health\",\n embeddings: (obsId: string) => `mem:emb:${obsId}`,\n bm25Index: \"mem:index:bm25\",\n relations: \"mem:relations\",\n profiles: \"mem:profiles\",\n claudeBridge: \"mem:claude-bridge\",\n graphNodes: \"mem:graph:nodes\",\n graphEdges: \"mem:graph:edges\",\n semantic: \"mem:semantic\",\n procedural: \"mem:procedural\",\n teamShared: (teamId: string) => `mem:team:${teamId}:shared`,\n teamUsers: (teamId: string, userId: string) =>\n `mem:team:${teamId}:users:${userId}`,\n teamProfile: (teamId: string) => `mem:team:${teamId}:profile`,\n audit: \"mem:audit\",\n actions: \"mem:actions\",\n actionEdges: \"mem:action-edges\",\n leases: \"mem:leases\",\n routines: \"mem:routines\",\n routineRuns: \"mem:routine-runs\",\n signals: \"mem:signals\",\n checkpoints: \"mem:checkpoints\",\n mesh: \"mem:mesh\",\n sketches: \"mem:sketches\",\n facets: \"mem:facets\",\n sentinels: \"mem:sentinels\",\n crystals: \"mem:crystals\",\n} as const;\n\nexport const STREAM = {\n name: \"mem-live\",\n group: (sessionId: string) => sessionId,\n viewerGroup: \"viewer\",\n} as const;\n\nexport function generateId(prefix: string): string {\n const ts = Date.now().toString(36);\n const rand = crypto.randomUUID().replace(/-/g, \"\").slice(0, 12);\n return `${prefix}_${ts}_${rand}`;\n}\n\nexport function fingerprintId(prefix: string, content: string): string {\n const crypto = require(\"node:crypto\") as typeof import(\"node:crypto\");\n const hash = crypto.createHash(\"sha256\").update(content).digest(\"hex\");\n return `${prefix}_${hash.slice(0, 16)}`;\n}\n\nexport function jaccardSimilarity(a: string, b: string): number {\n const setA = new Set(a.split(/\\s+/).filter((t) => t.length > 2));\n const setB = new Set(b.split(/\\s+/).filter((t) => t.length > 2));\n if (setA.size === 0 && setB.size === 0) return 1;\n if (setA.size === 0 || setB.size === 0) return 0;\n let intersection = 0;\n for (const word of setA) {\n if (setB.has(word)) intersection++;\n }\n return intersection / (setA.size + setB.size - intersection);\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 type { StateKV } from \"../state/kv.js\";\nimport { KV } from \"../state/schema.js\";\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\nimport type {\n Action,\n ActionEdge,\n Lease,\n Checkpoint,\n Signal,\n Sketch,\n MeshPeer,\n Session,\n Memory,\n} from \"../types.js\";\n\ninterface Sentinel {\n id: string;\n name: string;\n type: \"webhook\" | \"timer\" | \"threshold\" | \"pattern\" | \"approval\" | \"custom\";\n status: \"watching\" | \"triggered\" | \"cancelled\" | \"expired\";\n config: Record<string, unknown>;\n result?: unknown;\n createdAt: string;\n triggeredAt?: string;\n expiresAt?: string;\n linkedActionIds: string[];\n escalatedAt?: string;\n}\n\ninterface DiagnosticCheck {\n name: string;\n category: string;\n status: \"pass\" | \"warn\" | \"fail\";\n message: string;\n fixable: boolean;\n}\n\nconst ALL_CATEGORIES = [\n \"actions\",\n \"leases\",\n \"sentinels\",\n \"sketches\",\n \"signals\",\n \"sessions\",\n \"memories\",\n \"mesh\",\n];\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\nexport function registerDiagnosticsFunction(sdk: ISdk, kv: StateKV): void {\n sdk.registerFunction(\n { id: \"mem::diagnose\" },\n async (data: { categories?: string[] }) => {\n const categories = data.categories && data.categories.length > 0\n ? data.categories.filter((c) => ALL_CATEGORIES.includes(c))\n : ALL_CATEGORIES;\n\n const checks: DiagnosticCheck[] = [];\n const now = Date.now();\n\n if (categories.includes(\"actions\")) {\n const actions = await kv.list<Action>(KV.actions);\n const allEdges = await kv.list<ActionEdge>(KV.actionEdges);\n const leases = await kv.list<Lease>(KV.leases);\n const actionMap = new Map(actions.map((a) => [a.id, a]));\n\n for (const action of actions) {\n if (action.status === \"active\") {\n const hasActiveLease = leases.some(\n (l) =>\n l.actionId === action.id &&\n l.status === \"active\" &&\n new Date(l.expiresAt).getTime() > now,\n );\n if (!hasActiveLease) {\n checks.push({\n name: `active-no-lease:${action.id}`,\n category: \"actions\",\n status: \"warn\",\n message: `Action \"${action.title}\" is active but has no active lease`,\n fixable: false,\n });\n }\n }\n\n if (action.status === \"blocked\") {\n const deps = allEdges.filter(\n (e) => e.sourceActionId === action.id && e.type === \"requires\",\n );\n if (deps.length > 0) {\n const allDone = deps.every((d) => {\n const target = actionMap.get(d.targetActionId);\n return target && target.status === \"done\";\n });\n if (allDone) {\n checks.push({\n name: `blocked-deps-done:${action.id}`,\n category: \"actions\",\n status: \"fail\",\n message: `Action \"${action.title}\" is blocked but all dependencies are done`,\n fixable: true,\n });\n }\n }\n }\n\n if (action.status === \"pending\") {\n const deps = allEdges.filter(\n (e) => e.sourceActionId === action.id && e.type === \"requires\",\n );\n if (deps.length > 0) {\n const hasUnsatisfied = deps.some((d) => {\n const target = actionMap.get(d.targetActionId);\n return !target || target.status !== \"done\";\n });\n if (hasUnsatisfied) {\n checks.push({\n name: `pending-unsatisfied-deps:${action.id}`,\n category: \"actions\",\n status: \"fail\",\n message: `Action \"${action.title}\" is pending but has unsatisfied dependencies`,\n fixable: true,\n });\n }\n }\n }\n }\n\n if (\n !checks.some((c) => c.category === \"actions\" && c.status !== \"pass\")\n ) {\n checks.push({\n name: \"actions-ok\",\n category: \"actions\",\n status: \"pass\",\n message: `All ${actions.length} actions are consistent`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"leases\")) {\n const leases = await kv.list<Lease>(KV.leases);\n const actions = await kv.list<Action>(KV.actions);\n const actionIds = new Set(actions.map((a) => a.id));\n let leaseIssues = 0;\n\n for (const lease of leases) {\n if (\n lease.status === \"active\" &&\n new Date(lease.expiresAt).getTime() <= now\n ) {\n checks.push({\n name: `expired-lease:${lease.id}`,\n category: \"leases\",\n status: \"fail\",\n message: `Lease ${lease.id} for action ${lease.actionId} expired at ${lease.expiresAt}`,\n fixable: true,\n });\n leaseIssues++;\n }\n\n if (!actionIds.has(lease.actionId)) {\n checks.push({\n name: `orphaned-lease:${lease.id}`,\n category: \"leases\",\n status: \"fail\",\n message: `Lease ${lease.id} references non-existent action ${lease.actionId}`,\n fixable: true,\n });\n leaseIssues++;\n }\n }\n\n if (leaseIssues === 0) {\n checks.push({\n name: \"leases-ok\",\n category: \"leases\",\n status: \"pass\",\n message: `All ${leases.length} leases are healthy`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"sentinels\")) {\n const sentinels = await kv.list<Sentinel>(KV.sentinels);\n const actions = await kv.list<Action>(KV.actions);\n const actionIds = new Set(actions.map((a) => a.id));\n let sentinelIssues = 0;\n\n for (const sentinel of sentinels) {\n if (\n sentinel.status === \"watching\" &&\n sentinel.expiresAt &&\n new Date(sentinel.expiresAt).getTime() <= now\n ) {\n checks.push({\n name: `expired-sentinel:${sentinel.id}`,\n category: \"sentinels\",\n status: \"fail\",\n message: `Sentinel \"${sentinel.name}\" expired at ${sentinel.expiresAt}`,\n fixable: true,\n });\n sentinelIssues++;\n }\n\n for (const actionId of sentinel.linkedActionIds) {\n if (!actionIds.has(actionId)) {\n checks.push({\n name: `sentinel-missing-action:${sentinel.id}:${actionId}`,\n category: \"sentinels\",\n status: \"warn\",\n message: `Sentinel \"${sentinel.name}\" references non-existent action ${actionId}`,\n fixable: false,\n });\n sentinelIssues++;\n }\n }\n }\n\n if (sentinelIssues === 0) {\n checks.push({\n name: \"sentinels-ok\",\n category: \"sentinels\",\n status: \"pass\",\n message: `All ${sentinels.length} sentinels are healthy`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"sketches\")) {\n const sketches = await kv.list<Sketch>(KV.sketches);\n let sketchIssues = 0;\n\n for (const sketch of sketches) {\n if (\n sketch.status === \"active\" &&\n new Date(sketch.expiresAt).getTime() <= now\n ) {\n checks.push({\n name: `expired-sketch:${sketch.id}`,\n category: \"sketches\",\n status: \"fail\",\n message: `Sketch \"${sketch.title}\" expired at ${sketch.expiresAt}`,\n fixable: true,\n });\n sketchIssues++;\n }\n }\n\n if (sketchIssues === 0) {\n checks.push({\n name: \"sketches-ok\",\n category: \"sketches\",\n status: \"pass\",\n message: `All ${sketches.length} sketches are healthy`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"signals\")) {\n const signals = await kv.list<Signal>(KV.signals);\n let signalIssues = 0;\n\n for (const signal of signals) {\n if (\n signal.expiresAt &&\n new Date(signal.expiresAt).getTime() <= now\n ) {\n checks.push({\n name: `expired-signal:${signal.id}`,\n category: \"signals\",\n status: \"fail\",\n message: `Signal from \"${signal.from}\" expired at ${signal.expiresAt}`,\n fixable: true,\n });\n signalIssues++;\n }\n }\n\n if (signalIssues === 0) {\n checks.push({\n name: \"signals-ok\",\n category: \"signals\",\n status: \"pass\",\n message: `All ${signals.length} signals are healthy`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"sessions\")) {\n const sessions = await kv.list<Session>(KV.sessions);\n let sessionIssues = 0;\n\n for (const session of sessions) {\n if (\n session.status === \"active\" &&\n now - new Date(session.startedAt).getTime() > TWENTY_FOUR_HOURS_MS\n ) {\n checks.push({\n name: `abandoned-session:${session.id}`,\n category: \"sessions\",\n status: \"warn\",\n message: `Session ${session.id} has been active for over 24 hours`,\n fixable: false,\n });\n sessionIssues++;\n }\n }\n\n if (sessionIssues === 0) {\n checks.push({\n name: \"sessions-ok\",\n category: \"sessions\",\n status: \"pass\",\n message: `All ${sessions.length} sessions are healthy`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"memories\")) {\n const memories = await kv.list<Memory>(KV.memories);\n const memoryIds = new Set(memories.map((m) => m.id));\n const supersededBy = new Map<string, string>();\n let memoryIssues = 0;\n\n for (const memory of memories) {\n if (memory.supersedes && memory.supersedes.length > 0) {\n for (const sid of memory.supersedes) {\n if (!memoryIds.has(sid)) {\n checks.push({\n name: `memory-missing-supersedes:${memory.id}:${sid}`,\n category: \"memories\",\n status: \"warn\",\n message: `Memory \"${memory.title}\" supersedes non-existent memory ${sid}`,\n fixable: false,\n });\n memoryIssues++;\n }\n supersededBy.set(sid, memory.id);\n }\n }\n }\n\n for (const memory of memories) {\n if (memory.isLatest && supersededBy.has(memory.id)) {\n checks.push({\n name: `memory-stale-latest:${memory.id}`,\n category: \"memories\",\n status: \"fail\",\n message: `Memory \"${memory.title}\" has isLatest=true but is superseded by ${supersededBy.get(memory.id)}`,\n fixable: true,\n });\n memoryIssues++;\n }\n }\n\n if (memoryIssues === 0) {\n checks.push({\n name: \"memories-ok\",\n category: \"memories\",\n status: \"pass\",\n message: `All ${memories.length} memories are consistent`,\n fixable: false,\n });\n }\n }\n\n if (categories.includes(\"mesh\")) {\n const peers = await kv.list<MeshPeer>(KV.mesh);\n let meshIssues = 0;\n\n for (const peer of peers) {\n if (\n peer.lastSyncAt &&\n now - new Date(peer.lastSyncAt).getTime() > ONE_HOUR_MS\n ) {\n checks.push({\n name: `stale-peer:${peer.id}`,\n category: \"mesh\",\n status: \"warn\",\n message: `Peer \"${peer.name}\" last synced over 1 hour ago`,\n fixable: false,\n });\n meshIssues++;\n }\n\n if (peer.status === \"error\") {\n checks.push({\n name: `error-peer:${peer.id}`,\n category: \"mesh\",\n status: \"warn\",\n message: `Peer \"${peer.name}\" is in error state`,\n fixable: false,\n });\n meshIssues++;\n }\n }\n\n if (meshIssues === 0) {\n checks.push({\n name: \"mesh-ok\",\n category: \"mesh\",\n status: \"pass\",\n message: `All ${peers.length} mesh peers are healthy`,\n fixable: false,\n });\n }\n }\n\n const summary = {\n pass: checks.filter((c) => c.status === \"pass\").length,\n warn: checks.filter((c) => c.status === \"warn\").length,\n fail: checks.filter((c) => c.status === \"fail\").length,\n fixable: checks.filter((c) => c.fixable).length,\n };\n\n return { success: true, checks, summary };\n },\n );\n\n sdk.registerFunction(\n { id: \"mem::heal\" },\n async (data: { categories?: string[]; dryRun?: boolean }) => {\n const dryRun = data.dryRun ?? false;\n const categories = data.categories && data.categories.length > 0\n ? data.categories.filter((c) => ALL_CATEGORIES.includes(c))\n : ALL_CATEGORIES;\n\n let fixed = 0;\n let skipped = 0;\n const details: string[] = [];\n const now = Date.now();\n\n if (categories.includes(\"actions\")) {\n const actions = await kv.list<Action>(KV.actions);\n const allEdges = await kv.list<ActionEdge>(KV.actionEdges);\n const actionMap = new Map(actions.map((a) => [a.id, a]));\n\n for (const action of actions) {\n if (action.status === \"blocked\") {\n const deps = allEdges.filter(\n (e) => e.sourceActionId === action.id && e.type === \"requires\",\n );\n if (deps.length > 0) {\n const allDone = deps.every((d) => {\n const target = actionMap.get(d.targetActionId);\n return target && target.status === \"done\";\n });\n if (allDone) {\n if (dryRun) {\n details.push(\n `[dry-run] Would unblock action \"${action.title}\" (${action.id})`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:action:${action.id}`,\n async () => {\n const fresh = await kv.get<Action>(KV.actions, action.id);\n if (!fresh || fresh.status !== \"blocked\") return false;\n const freshEdges = await kv.list<ActionEdge>(KV.actionEdges);\n const freshDeps = freshEdges.filter(\n (e) =>\n e.sourceActionId === fresh.id && e.type === \"requires\",\n );\n const freshActions = await kv.list<Action>(KV.actions);\n const freshMap = new Map(\n freshActions.map((a) => [a.id, a]),\n );\n const stillAllDone = freshDeps.every((d) => {\n const target = freshMap.get(d.targetActionId);\n return target && target.status === \"done\";\n });\n if (!stillAllDone) return false;\n fresh.status = \"pending\";\n fresh.updatedAt = new Date().toISOString();\n await kv.set(KV.actions, fresh.id, fresh);\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Unblocked action \"${action.title}\" (${action.id})`,\n );\n fixed++;\n } else {\n skipped++;\n }\n }\n }\n }\n\n if (action.status === \"pending\") {\n const deps = allEdges.filter(\n (e) => e.sourceActionId === action.id && e.type === \"requires\",\n );\n if (deps.length > 0) {\n const hasUnsatisfied = deps.some((d) => {\n const target = actionMap.get(d.targetActionId);\n return !target || target.status !== \"done\";\n });\n if (hasUnsatisfied) {\n if (dryRun) {\n details.push(\n `[dry-run] Would block action \"${action.title}\" (${action.id})`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:action:${action.id}`,\n async () => {\n const fresh = await kv.get<Action>(KV.actions, action.id);\n if (!fresh || fresh.status !== \"pending\") return false;\n const freshEdges = await kv.list<ActionEdge>(KV.actionEdges);\n const freshDeps = freshEdges.filter(\n (e) =>\n e.sourceActionId === fresh.id && e.type === \"requires\",\n );\n const freshActions = await kv.list<Action>(KV.actions);\n const freshMap = new Map(\n freshActions.map((a) => [a.id, a]),\n );\n const stillUnsatisfied = freshDeps.some((d) => {\n const target = freshMap.get(d.targetActionId);\n return !target || target.status !== \"done\";\n });\n if (!stillUnsatisfied) return false;\n fresh.status = \"blocked\";\n fresh.updatedAt = new Date().toISOString();\n await kv.set(KV.actions, fresh.id, fresh);\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Blocked action \"${action.title}\" (${action.id})`,\n );\n fixed++;\n } else {\n skipped++;\n }\n }\n }\n }\n }\n }\n\n if (categories.includes(\"leases\")) {\n const leases = await kv.list<Lease>(KV.leases);\n const actions = await kv.list<Action>(KV.actions);\n const actionIds = new Set(actions.map((a) => a.id));\n\n for (const lease of leases) {\n if (\n lease.status === \"active\" &&\n new Date(lease.expiresAt).getTime() <= now\n ) {\n if (dryRun) {\n details.push(\n `[dry-run] Would expire lease ${lease.id} for action ${lease.actionId}`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:lease:${lease.actionId}`,\n async () => {\n const fresh = await kv.get<Lease>(KV.leases, lease.id);\n if (\n !fresh ||\n fresh.status !== \"active\" ||\n new Date(fresh.expiresAt).getTime() > Date.now()\n ) {\n return false;\n }\n fresh.status = \"expired\";\n await kv.set(KV.leases, fresh.id, fresh);\n\n const action = await kv.get<Action>(KV.actions, fresh.actionId);\n if (\n action &&\n action.status === \"active\" &&\n action.assignedTo === fresh.agentId\n ) {\n action.status = \"pending\";\n action.assignedTo = undefined;\n action.updatedAt = new Date().toISOString();\n await kv.set(KV.actions, action.id, action);\n }\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Expired lease ${lease.id} for action ${lease.actionId}`,\n );\n fixed++;\n } else {\n skipped++;\n }\n continue;\n }\n\n if (!actionIds.has(lease.actionId)) {\n if (dryRun) {\n details.push(\n `[dry-run] Would delete orphaned lease ${lease.id}`,\n );\n fixed++;\n continue;\n }\n await kv.delete(KV.leases, lease.id);\n details.push(`Deleted orphaned lease ${lease.id}`);\n fixed++;\n }\n }\n }\n\n if (categories.includes(\"sentinels\")) {\n const sentinels = await kv.list<Sentinel>(KV.sentinels);\n\n for (const sentinel of sentinels) {\n if (\n sentinel.status === \"watching\" &&\n sentinel.expiresAt &&\n new Date(sentinel.expiresAt).getTime() <= now\n ) {\n if (dryRun) {\n details.push(\n `[dry-run] Would expire sentinel \"${sentinel.name}\" (${sentinel.id})`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:sentinel:${sentinel.id}`,\n async () => {\n const fresh = await kv.get<Sentinel>(\n KV.sentinels,\n sentinel.id,\n );\n if (!fresh || fresh.status !== \"watching\") return false;\n if (\n !fresh.expiresAt ||\n new Date(fresh.expiresAt).getTime() > Date.now()\n ) {\n return false;\n }\n fresh.status = \"expired\";\n await kv.set(KV.sentinels, fresh.id, fresh);\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Expired sentinel \"${sentinel.name}\" (${sentinel.id})`,\n );\n fixed++;\n } else {\n skipped++;\n }\n }\n }\n }\n\n if (categories.includes(\"sketches\")) {\n const sketches = await kv.list<Sketch>(KV.sketches);\n\n for (const sketch of sketches) {\n if (\n sketch.status === \"active\" &&\n new Date(sketch.expiresAt).getTime() <= now\n ) {\n if (dryRun) {\n details.push(\n `[dry-run] Would discard expired sketch \"${sketch.title}\" (${sketch.id})`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:sketch:${sketch.id}`,\n async () => {\n const fresh = await kv.get<Sketch>(KV.sketches, sketch.id);\n if (\n !fresh ||\n fresh.status !== \"active\" ||\n new Date(fresh.expiresAt).getTime() > Date.now()\n ) {\n return false;\n }\n\n const allEdges = await kv.list<ActionEdge>(KV.actionEdges);\n const actionIdSet = new Set(fresh.actionIds);\n for (const edge of allEdges) {\n if (\n actionIdSet.has(edge.sourceActionId) ||\n actionIdSet.has(edge.targetActionId)\n ) {\n await kv.delete(KV.actionEdges, edge.id);\n }\n }\n for (const actionId of fresh.actionIds) {\n await kv.delete(KV.actions, actionId);\n }\n\n fresh.status = \"discarded\";\n fresh.discardedAt = new Date().toISOString();\n await kv.set(KV.sketches, fresh.id, fresh);\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Discarded expired sketch \"${sketch.title}\" (${sketch.id})`,\n );\n fixed++;\n } else {\n skipped++;\n }\n }\n }\n }\n\n if (categories.includes(\"signals\")) {\n const signals = await kv.list<Signal>(KV.signals);\n\n for (const signal of signals) {\n if (\n signal.expiresAt &&\n new Date(signal.expiresAt).getTime() <= now\n ) {\n if (dryRun) {\n details.push(\n `[dry-run] Would delete expired signal ${signal.id}`,\n );\n fixed++;\n continue;\n }\n await kv.delete(KV.signals, signal.id);\n details.push(`Deleted expired signal ${signal.id}`);\n fixed++;\n }\n }\n }\n\n if (categories.includes(\"memories\")) {\n const memories = await kv.list<Memory>(KV.memories);\n const supersededBy = new Map<string, string>();\n\n for (const memory of memories) {\n if (memory.supersedes && memory.supersedes.length > 0) {\n for (const sid of memory.supersedes) {\n supersededBy.set(sid, memory.id);\n }\n }\n }\n\n for (const memory of memories) {\n if (memory.isLatest && supersededBy.has(memory.id)) {\n if (dryRun) {\n details.push(\n `[dry-run] Would set isLatest=false on memory \"${memory.title}\" (${memory.id})`,\n );\n fixed++;\n continue;\n }\n const didFix = await withKeyedLock(\n `mem:memory:${memory.id}`,\n async () => {\n const fresh = await kv.get<Memory>(KV.memories, memory.id);\n if (!fresh || !fresh.isLatest) return false;\n fresh.isLatest = false;\n fresh.updatedAt = new Date().toISOString();\n await kv.set(KV.memories, fresh.id, fresh);\n return true;\n },\n );\n if (didFix) {\n details.push(\n `Set isLatest=false on memory \"${memory.title}\" (${memory.id})`,\n );\n fixed++;\n } else {\n skipped++;\n }\n }\n }\n }\n\n return { success: true, fixed, skipped, details };\n },\n );\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",