@agentmemory/agentmemory 0.9.31 → 0.9.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.env.example +18 -15
  2. package/AGENTS.md +4 -4
  3. package/README.md +92 -270
  4. package/dist/.env.example +18 -15
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +530 -75
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{compression-recovery-CnhLQC6N.mjs → compression-recovery-CZh63Jmz.mjs} +174 -89
  9. package/dist/compression-recovery-CZh63Jmz.mjs.map +1 -0
  10. package/dist/{connect-BSLIAMpw.mjs → connect-BX3njQ3Y.mjs} +28 -14
  11. package/dist/connect-BX3njQ3Y.mjs.map +1 -0
  12. package/dist/hooks/_capture.d.mts +3 -2
  13. package/dist/hooks/_capture.d.mts.map +1 -1
  14. package/dist/hooks/_capture.mjs +87 -8
  15. package/dist/hooks/_capture.mjs.map +1 -1
  16. package/dist/hooks/post-commit.mjs +58 -5
  17. package/dist/hooks/post-commit.mjs.map +1 -1
  18. package/dist/hooks/post-tool-failure.mjs +7 -2
  19. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  20. package/dist/hooks/post-tool-use.mjs +7 -2
  21. package/dist/hooks/post-tool-use.mjs.map +1 -1
  22. package/dist/hooks/pre-compact.mjs +67 -12
  23. package/dist/hooks/pre-compact.mjs.map +1 -1
  24. package/dist/hooks/pre-tool-use.mjs +58 -5
  25. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  26. package/dist/hooks/session-end.mjs +14 -3
  27. package/dist/hooks/session-end.mjs.map +1 -1
  28. package/dist/hooks/session-start.mjs +58 -5
  29. package/dist/hooks/session-start.mjs.map +1 -1
  30. package/dist/hooks/stop.mjs +2 -1
  31. package/dist/hooks/stop.mjs.map +1 -1
  32. package/dist/hooks/subagent-stop.mjs +15 -5
  33. package/dist/hooks/subagent-stop.mjs.map +1 -1
  34. package/dist/hooks/task-completed.mjs +8 -1
  35. package/dist/hooks/task-completed.mjs.map +1 -1
  36. package/dist/index.mjs +724 -200
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/integrations/dsh.mjs +31 -4
  39. package/dist/integrations/dsh.mjs.map +1 -1
  40. package/dist/{secret-store-D9GwO0xp.mjs → secret-store-iYXRcrO8.mjs} +197 -55
  41. package/dist/secret-store-iYXRcrO8.mjs.map +1 -0
  42. package/dist/{src-DaMtDzt_.mjs → src-C23nMpda.mjs} +359 -62
  43. package/dist/src-C23nMpda.mjs.map +1 -0
  44. package/dist/{standalone-BlqshyZ5.mjs → standalone-BYo5USDb.mjs} +169 -10
  45. package/dist/standalone-BYo5USDb.mjs.map +1 -0
  46. package/dist/standalone.d.mts.map +1 -1
  47. package/dist/standalone.mjs +240 -10
  48. package/dist/standalone.mjs.map +1 -1
  49. package/dist/viewer/index.html +49 -28
  50. package/integrations/hermes/README.md +11 -1
  51. package/integrations/hermes/__init__.py +1 -0
  52. package/integrations/hermes/plugin.yaml +1 -1
  53. package/integrations/omp/dist/index.mjs +31 -4
  54. package/integrations/omp/package.json +1 -1
  55. package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +1 -1
  56. package/integrations/omp/skills/agentmemory-architecture/SKILL.md +5 -1
  57. package/integrations/omp/skills/agentmemory-config/REFERENCE.md +2 -1
  58. package/integrations/omp/skills/agentmemory-config/SKILL.md +29 -9
  59. package/integrations/omp/skills/agentmemory-hooks/SKILL.md +17 -6
  60. package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  61. package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  62. package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  63. package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +1 -1
  64. package/integrations/openclaw/README.md +10 -0
  65. package/integrations/openclaw/openclaw.plugin.json +1 -1
  66. package/integrations/openclaw/package.json +1 -1
  67. package/integrations/openclaw/plugin.yaml +1 -1
  68. package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +1 -1
  69. package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +5 -1
  70. package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +2 -1
  71. package/integrations/openclaw/skills/agentmemory-config/SKILL.md +29 -9
  72. package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +17 -6
  73. package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  74. package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  75. package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  76. package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +1 -1
  77. package/integrations/pi/README.md +1 -0
  78. package/integrations/pi/dist/index.mjs +92 -11
  79. package/integrations/pi/index.ts +56 -6
  80. package/integrations/pi/package.json +1 -1
  81. package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +1 -1
  82. package/integrations/pi/skills/agentmemory-architecture/SKILL.md +5 -1
  83. package/integrations/pi/skills/agentmemory-config/REFERENCE.md +2 -1
  84. package/integrations/pi/skills/agentmemory-config/SKILL.md +29 -9
  85. package/integrations/pi/skills/agentmemory-hooks/SKILL.md +17 -6
  86. package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  87. package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  88. package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  89. package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +1 -1
  90. package/package.json +4 -3
  91. package/plugin/.claude-plugin/plugin.json +16 -3
  92. package/plugin/.codex-plugin/plugin.json +1 -1
  93. package/plugin/.devin-plugin/plugin.json +3 -4
  94. package/plugin/.mcp.json +5 -1
  95. package/plugin/README.md +71 -0
  96. package/plugin/hooks/hooks.json +24 -12
  97. package/plugin/opencode/plugin.json +1 -1
  98. package/plugin/plugin.json +1 -1
  99. package/plugin/scripts/_capture.d.mts +3 -2
  100. package/plugin/scripts/_capture.d.mts.map +1 -1
  101. package/plugin/scripts/_capture.mjs +87 -8
  102. package/plugin/scripts/_capture.mjs.map +1 -1
  103. package/plugin/scripts/plugin-bridge.mjs +25 -6
  104. package/plugin/scripts/post-tool-failure.mjs +7 -2
  105. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  106. package/plugin/scripts/post-tool-use.mjs +7 -2
  107. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  108. package/plugin/scripts/pre-compact.mjs +67 -12
  109. package/plugin/scripts/pre-compact.mjs.map +1 -1
  110. package/plugin/scripts/pre-tool-use.mjs +58 -5
  111. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  112. package/plugin/scripts/session-end.mjs +14 -3
  113. package/plugin/scripts/session-end.mjs.map +1 -1
  114. package/plugin/scripts/session-start.mjs +58 -5
  115. package/plugin/scripts/session-start.mjs.map +1 -1
  116. package/plugin/scripts/stop.mjs +2 -1
  117. package/plugin/scripts/stop.mjs.map +1 -1
  118. package/plugin/scripts/subagent-stop.mjs +15 -5
  119. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  120. package/plugin/scripts/task-completed.mjs +8 -1
  121. package/plugin/scripts/task-completed.mjs.map +1 -1
  122. package/plugin/skills/agentmemory-agents/REFERENCE.md +1 -1
  123. package/plugin/skills/agentmemory-architecture/SKILL.md +5 -1
  124. package/plugin/skills/agentmemory-config/REFERENCE.md +2 -1
  125. package/plugin/skills/agentmemory-config/SKILL.md +29 -9
  126. package/plugin/skills/agentmemory-hooks/SKILL.md +17 -6
  127. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  128. package/plugin/skills/agentmemory-mcp-tools/SKILL.md +10 -0
  129. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +2 -1
  130. package/plugin/skills/agentmemory-rest-api/SKILL.md +1 -1
  131. package/dist/compression-recovery-CnhLQC6N.mjs.map +0 -1
  132. package/dist/connect-BSLIAMpw.mjs.map +0 -1
  133. package/dist/secret-store-D9GwO0xp.mjs.map +0 -1
  134. package/dist/src-DaMtDzt_.mjs.map +0 -1
  135. package/dist/standalone-BlqshyZ5.mjs.map +0 -1
  136. package/plugin/scripts/post-commit.d.mts +0 -1
  137. package/plugin/scripts/post-commit.mjs +0 -204
  138. package/plugin/scripts/post-commit.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { a as KV, l as hasCjk, n as isKeyedLockHeld, o as fingerprintId, r as withKeyedLock, s as generateId, t as afterKeyedLock, u as segmentCjk } from "./keyed-mutex-DCUh7PG-.mjs";
2
- import { D as logger, Q as getVectorBackfillMax, R as getAgentId, _ as safeAudit, c as memoryToObservation, d as isSearchResultLayer, f as matchesSearchLayer, h as recordAudit, l as getSearchResultLayer, lt as isVectorBackfillAllEnabled, o as buildRecallResponse, st as isGraphExtractionEnabled, tt as isAgentScopeIsolated, u as isSearchLayer } from "./secret-store-D9GwO0xp.mjs";
2
+ import { H as getAgentId, _ as recordAudit, at as isAgentScopeIsolated, c as memoryToObservation, d as getSearchResultLayer, dt as isGraphExtractionEnabled, f as isSearchLayer, k as logger, l as observationEmbedText, m as matchesSearchLayer, nt as getVectorBackfillMax, o as buildRecallResponse, p as isSearchResultLayer, pt as isVectorBackfillAllEnabled, y as safeAudit } from "./secret-store-iYXRcrO8.mjs";
3
3
  import { i as isManagedImagePath } from "./image-store-BsN4vyjr.mjs";
4
4
  import { chmodSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, utimesSync, writeFileSync, writeSync } from "node:fs";
5
5
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -128,7 +128,29 @@ function resolveDataDir(options = {}) {
128
128
  }
129
129
  //#endregion
130
130
  //#region src/functions/privacy.ts
131
- const PRIVATE_TAG_RE = /<private>[\s\S]*?<\/private>/gi;
131
+ const PRIVATE_TAG_TOKEN_RE = /<private(?=\s|>)[^>]*>|<\/private\s*>/gi;
132
+ function redactPrivateBlocks(input) {
133
+ const tokens = new RegExp(PRIVATE_TAG_TOKEN_RE.source, PRIVATE_TAG_TOKEN_RE.flags);
134
+ let out = "";
135
+ let cursor = 0;
136
+ let depth = 0;
137
+ for (let match = tokens.exec(input); match; match = tokens.exec(input)) {
138
+ const closing = match[0].startsWith("</");
139
+ if (depth === 0) {
140
+ if (closing) continue;
141
+ out += input.slice(cursor, match.index);
142
+ depth = 1;
143
+ } else if (closing) {
144
+ depth -= 1;
145
+ if (depth === 0) {
146
+ out += "[REDACTED]";
147
+ cursor = match.index + match[0].length;
148
+ }
149
+ } else depth += 1;
150
+ }
151
+ if (depth > 0) return `${out}[REDACTED]`;
152
+ return out + input.slice(cursor);
153
+ }
132
154
  const SECRET_PATTERN_SOURCES = [
133
155
  /(?:api[_-]?key|secret|token|password|credential|auth)[\s]*[=:]\s*["']?[A-Za-z0-9_\-/.+]{20,}["']?/gi,
134
156
  /Bearer\s+[A-Za-z0-9._\-+/=]{20,}/gi,
@@ -148,7 +170,7 @@ const SECRET_PATTERN_SOURCES = [
148
170
  const PRIVATE_KEY_BLOCK_RE = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g;
149
171
  const URL_CREDENTIALS_RE = /\b([a-z][a-z0-9+.-]*:\/\/)[^\s/?#@:"'<>]*:[^\s/?#@"'<>]+@/gi;
150
172
  function stripPrivateData(input) {
151
- let result = input.replace(PRIVATE_TAG_RE, "[REDACTED]");
173
+ let result = redactPrivateBlocks(input);
152
174
  result = result.replace(new RegExp(PRIVATE_KEY_BLOCK_RE.source, PRIVATE_KEY_BLOCK_RE.flags), "[REDACTED_SECRET]");
153
175
  result = result.replace(new RegExp(URL_CREDENTIALS_RE.source, URL_CREDENTIALS_RE.flags), "$1[REDACTED_SECRET]@");
154
176
  for (const source of SECRET_PATTERN_SOURCES) {
@@ -2686,7 +2708,7 @@ const FUNCTION_FIXES = {
2686
2708
  "mem::compress": "Compression calls your LLM provider. Check the provider key and model, or set AGENTMEMORY_AUTO_COMPRESS=false to use zero-LLM compression.",
2687
2709
  "mem::graph-extract": "Graph extraction calls your LLM provider. Check the provider key and model, or set GRAPH_EXTRACTION_ENABLED=false."
2688
2710
  };
2689
- const LLM_KEY_FIX = "Add one LLM provider key (for example ANTHROPIC_API_KEY or OPENAI_API_KEY) to ~/.agentmemory/.env and restart.";
2711
+ const LLM_KEY_FIX = "Set AGENTMEMORY_LLM_PROVIDER and that provider's key (for example AGENTMEMORY_LLM_PROVIDER=anthropic and ANTHROPIC_API_KEY) in ~/.agentmemory/.env and restart.";
2690
2712
  const OFF_WITHOUT_LLM = [
2691
2713
  "session summaries",
2692
2714
  "consolidation into semantic facts and procedures",
@@ -2795,8 +2817,17 @@ function evaluateStatus(input) {
2795
2817
  level: "info",
2796
2818
  code: "no-llm-provider",
2797
2819
  message: "No LLM provider is configured, so summaries, consolidation and graph extraction stay off. Search still works.",
2798
- fix: LLM_KEY_FIX
2820
+ fix: input.providerHint || "Set AGENTMEMORY_LLM_PROVIDER and that provider's key (for example AGENTMEMORY_LLM_PROVIDER=anthropic and ANTHROPIC_API_KEY) in ~/.agentmemory/.env and restart."
2799
2821
  });
2822
+ if (input.embeddingHint) {
2823
+ const keyMissing = input.embeddingHint.startsWith("EMBEDDING_PROVIDER=");
2824
+ problems.push({
2825
+ level: "info",
2826
+ code: keyMissing ? "embedding-provider-key-missing" : "embedding-provider-not-chosen",
2827
+ message: keyMissing ? "EMBEDDING_PROVIDER names a cloud provider whose key is missing, so search is keyword and graph only." : "A cloud embedding key is present, but no embedding provider is chosen, so search is keyword and graph only.",
2828
+ fix: input.embeddingHint
2829
+ });
2830
+ }
2800
2831
  const noLlm = input.provider === "noop";
2801
2832
  const functions = markLlmFunctions(input.functionMetrics, input.provider).map((m) => ({
2802
2833
  ...m,
@@ -2977,7 +3008,9 @@ function evaluateStatus(input) {
2977
3008
  provider: {
2978
3009
  llm: input.provider,
2979
3010
  route: input.provider === "noop" ? null : input.llmRoute ?? null,
3011
+ hint: input.providerHint ?? null,
2980
3012
  embeddings: input.embeddingProvider,
3013
+ embeddingHint: input.embeddingHint ?? null,
2981
3014
  circuitBreaker: input.circuitBreaker,
2982
3015
  offWithoutLlm: noLlm ? OFF_WITHOUT_LLM : []
2983
3016
  },
@@ -3302,7 +3335,7 @@ Rules:
3302
3335
  - Be concise but preserve ALL technically relevant details
3303
3336
  - File paths must be exact
3304
3337
  - Importance: 1-3 for routine reads, 4-6 for edits/commands, 7-9 for architectural decisions, 10 for breaking changes
3305
- - Concepts should be reusable search terms (e.g., "React hooks", "SQL migration", "auth middleware")
3338
+ - Concepts should be reusable search terms (e.g., "React hooks", "SQL migration", "auth middleware"); keep them in the user's language: when the source content is not English, include at least one concept in that original language, and when the observation mixes languages include both the original-language and English terms so search works in either
3306
3339
  - Strip any secrets, tokens, or credentials from the output`;
3307
3340
  function buildCompressionPrompt(observation) {
3308
3341
  const parts = [`Timestamp: ${observation.timestamp}`, `Hook: ${observation.hookType}`];
@@ -3684,6 +3717,9 @@ function getSynonyms(stemmedTerm) {
3684
3717
  }
3685
3718
  //#endregion
3686
3719
  //#region src/state/search-index.ts
3720
+ const USER_PROMPT_TERM_CAP = 2e3;
3721
+ const ASSISTANT_RESPONSE_TERM_CAP = 1e3;
3722
+ const TOOL_OUTPUT_TERM_CAP = 1e3;
3687
3723
  var SearchIndex = class SearchIndex {
3688
3724
  entries = /* @__PURE__ */ new Map();
3689
3725
  invertedIndex = /* @__PURE__ */ new Map();
@@ -3942,6 +3978,9 @@ var SearchIndex = class SearchIndex {
3942
3978
  ...obs.files,
3943
3979
  obs.type
3944
3980
  ];
3981
+ if (obs.source?.userPrompt) parts.push(obs.source.userPrompt.slice(0, USER_PROMPT_TERM_CAP));
3982
+ if (obs.source?.assistantResponse) parts.push(obs.source.assistantResponse.slice(0, ASSISTANT_RESPONSE_TERM_CAP));
3983
+ if (obs.source?.toolName === "conversation" && typeof obs.source.toolOutput === "string") parts.push(obs.source.toolOutput.slice(0, TOOL_OUTPUT_TERM_CAP));
3945
3984
  return this.tokenize(parts.join(" ").toLowerCase());
3946
3985
  }
3947
3986
  tokenize(text) {
@@ -4449,7 +4488,11 @@ async function indexRecords(observations, memories) {
4449
4488
  id: memory.id,
4450
4489
  revision,
4451
4490
  sessionId: memory.sessionIds?.[0] ?? "memory",
4452
- text: memory.title + " " + memory.content,
4491
+ text: observationEmbedText({
4492
+ title: memory.title,
4493
+ narrative: memory.content,
4494
+ concepts: memory.concepts
4495
+ }),
4453
4496
  context: {
4454
4497
  kind: "memory",
4455
4498
  logId: memory.id
@@ -4463,7 +4506,7 @@ async function indexRecords(observations, memories) {
4463
4506
  id: obs.id,
4464
4507
  revision,
4465
4508
  sessionId: obs.sessionId,
4466
- text: obs.title + " " + obs.narrative,
4509
+ text: observationEmbedText(obs),
4467
4510
  context: {
4468
4511
  kind: "observation",
4469
4512
  logId: obs.id
@@ -4496,21 +4539,36 @@ async function findUnindexedObservations(kv) {
4496
4539
  missing
4497
4540
  };
4498
4541
  }
4499
- const SESSIONS_LIST_RETRY_ATTEMPTS = 3;
4500
- const SESSIONS_LIST_RETRY_BASE_MS = 250;
4542
+ const LIST_RETRY_ATTEMPTS = 8;
4543
+ const LIST_RETRY_BASE_MS = 2500;
4544
+ const LIST_RETRY_BUDGET_MS = 7e4;
4501
4545
  function delay(ms) {
4502
4546
  return new Promise((resolve) => setTimeout(resolve, ms));
4503
4547
  }
4504
- async function listSessionsWithRetry(kv) {
4548
+ function isStateNotReadyError(err) {
4549
+ const code = err?.code;
4550
+ if (code === "function_not_found" || code === "invocation_timeout" || code === "timeout" || code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE") return true;
4551
+ const message = err instanceof Error ? err.message : String(err);
4552
+ return /function_not_found|Function state::\w+ not found|timed out|timeout|ETIMEDOUT|ECONNRESET|ECONNREFUSED|EPIPE|socket hang up|connection (?:closed|reset)/i.test(message);
4553
+ }
4554
+ async function listWithRetry(kv, scope, label, deadline) {
4505
4555
  let lastErr;
4506
- for (let attempt = 0; attempt < SESSIONS_LIST_RETRY_ATTEMPTS; attempt++) try {
4507
- return await kv.list(KV.sessions);
4508
- } catch (err) {
4509
- lastErr = err;
4510
- if (attempt < SESSIONS_LIST_RETRY_ATTEMPTS - 1) await delay(SESSIONS_LIST_RETRY_BASE_MS * (attempt + 1));
4556
+ let attempts = 0;
4557
+ while (attempts < LIST_RETRY_ATTEMPTS) {
4558
+ attempts++;
4559
+ try {
4560
+ return await kv.list(scope);
4561
+ } catch (err) {
4562
+ lastErr = err;
4563
+ if (!isStateNotReadyError(err)) break;
4564
+ const remaining = deadline - performance.now();
4565
+ const wait = LIST_RETRY_BASE_MS * attempts;
4566
+ if (attempts >= LIST_RETRY_ATTEMPTS || wait >= remaining) break;
4567
+ await delay(wait);
4568
+ }
4511
4569
  }
4512
- logger.warn("rebuildKeywordIndex: failed to load sessions after retries", {
4513
- attempts: SESSIONS_LIST_RETRY_ATTEMPTS,
4570
+ logger.warn(`${label}: failed to load`, {
4571
+ attempts,
4514
4572
  error: lastErr instanceof Error ? lastErr.message : String(lastErr)
4515
4573
  });
4516
4574
  return null;
@@ -4560,20 +4618,24 @@ async function runKeywordRebuild(kv, vectorBackfillSince) {
4560
4618
  };
4561
4619
  let documents = 0;
4562
4620
  let memoriesLoaded = false;
4563
- try {
4564
- const memories = await kv.list(KV.memories);
4621
+ const listDeadline = performance.now() + LIST_RETRY_BUDGET_MS;
4622
+ const memories = await listWithRetry(kv, KV.memories, "rebuildKeywordIndex: memories", listDeadline);
4623
+ const sessions = await listWithRetry(kv, KV.sessions, "rebuildKeywordIndex: sessions", listDeadline);
4624
+ if (memories === null) bm25RebuildIncomplete = true;
4625
+ else {
4565
4626
  memoriesLoaded = true;
4566
4627
  for (const memory of memories) {
4567
4628
  if (memory.isLatest === false) continue;
4568
4629
  if (!memory.title || !memory.content) continue;
4569
4630
  idx.add(memoryToObservation(memory), "memory");
4570
- consider(memory.id, memory.sessionIds?.[0] ?? "memory", memory.title + " " + memory.content, memory.createdAt, "memory");
4631
+ consider(memory.id, memory.sessionIds?.[0] ?? "memory", observationEmbedText({
4632
+ title: memory.title,
4633
+ narrative: memory.content,
4634
+ concepts: memory.concepts
4635
+ }), memory.createdAt, "memory");
4571
4636
  documents++;
4572
4637
  }
4573
- } catch (err) {
4574
- logger.warn("rebuildKeywordIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
4575
4638
  }
4576
- const sessions = await listSessionsWithRetry(kv);
4577
4639
  if (sessions === null) {
4578
4640
  bm25RebuildIncomplete = true;
4579
4641
  if (memoriesLoaded) memoryIndexReady = true;
@@ -4597,7 +4659,7 @@ async function runKeywordRebuild(kv, vectorBackfillSince) {
4597
4659
  for (const obs of results.flat()) {
4598
4660
  if (!obs.title || !obs.narrative) continue;
4599
4661
  idx.add(obs);
4600
- consider(obs.id, obs.sessionId, obs.title + " " + obs.narrative, obs.timestamp, "observation");
4662
+ consider(obs.id, obs.sessionId, observationEmbedText(obs), obs.timestamp, "observation");
4601
4663
  documents++;
4602
4664
  }
4603
4665
  }
@@ -4704,6 +4766,7 @@ function registerSearchFunction(sdk, kv) {
4704
4766
  if (data.targetLayer !== void 0 && !isSearchLayer(data.targetLayer)) throw new Error("mem::search: targetLayer must be one of 'all', 'memory', or 'observation'");
4705
4767
  const targetLayer = data.targetLayer ?? "all";
4706
4768
  const MAX_LIMIT = 100;
4769
+ const MAX_CANDIDATES = 1e3;
4707
4770
  let effectiveLimit = 20;
4708
4771
  if (data.limit !== void 0) {
4709
4772
  if (!Number.isInteger(data.limit) || data.limit < 1) throw new Error("mem::search: limit must be a positive integer");
@@ -4741,7 +4804,7 @@ function registerSearchFunction(sdk, kv) {
4741
4804
  await rebuildPromise;
4742
4805
  }
4743
4806
  const filtering = !!(projectFilter || cwdFilter || filterAgentId);
4744
- const fetchLimit = filtering ? Math.max(effectiveLimit * 10, 100) : effectiveLimit;
4807
+ let fetchLimit = filtering ? Math.max(effectiveLimit * 10, 100) : effectiveLimit;
4745
4808
  let results;
4746
4809
  const authorizeGraph = async (record, sessionId, read) => {
4747
4810
  if (filterAgentId !== void 0 && record.agentId !== filterAgentId) return false;
@@ -4755,25 +4818,12 @@ function registerSearchFunction(sdk, kv) {
4755
4818
  }
4756
4819
  return true;
4757
4820
  };
4758
- if (hybridRanker) try {
4759
- results = (await hybridRanker(query, fetchLimit, targetLayer, authorizeGraph)).map((r) => ({
4760
- obsId: r.observation.id,
4761
- sessionId: r.sessionId,
4762
- score: r.combinedScore,
4763
- observation: r.observation,
4764
- layer: r.layer
4765
- }));
4766
- } catch (err) {
4767
- logger.warn("hybrid ranking failed, falling back to keyword search", { error: err instanceof Error ? err.message : String(err) });
4768
- results = idx.search(query, fetchLimit, targetLayer);
4769
- }
4770
- else results = idx.search(query, fetchLimit, targetLayer);
4771
4821
  const sessionCache = /* @__PURE__ */ new Map();
4772
4822
  const loadSession = async (sessionId) => {
4773
4823
  if (sessionCache.has(sessionId)) return sessionCache.get(sessionId);
4774
- const s = await kv.get(KV.sessions, sessionId);
4775
- sessionCache.set(sessionId, s ?? null);
4776
- return s ?? null;
4824
+ const pending = kv.get(KV.sessions, sessionId).then((session) => session ?? null);
4825
+ sessionCache.set(sessionId, pending);
4826
+ return pending;
4777
4827
  };
4778
4828
  const memoryProjectCache = /* @__PURE__ */ new Map();
4779
4829
  const loadMemoryProject = async (obsId) => {
@@ -4782,53 +4832,88 @@ function registerSearchFunction(sdk, kv) {
4782
4832
  memoryProjectCache.set(obsId, proj);
4783
4833
  return proj;
4784
4834
  };
4785
- const earlyCap = filterAgentId ? fetchLimit : effectiveLimit;
4786
- const candidates = [];
4787
- for (const r of results) {
4788
- if (candidates.length >= earlyCap) break;
4789
- const layer = r.layer ?? idx.layerOf(r.obsId) ?? getSearchResultLayer(r.obsId, r.sessionId);
4790
- if (!matchesSearchLayer(r.obsId, r.sessionId, targetLayer, layer)) continue;
4791
- if (filtering) {
4792
- const memoryProject = projectFilter && layer === "memory" ? await loadMemoryProject(r.obsId) : null;
4793
- if (projectFilter && memoryProject !== null && memoryProject !== projectFilter) continue;
4794
- const session = await loadSession(r.sessionId);
4795
- if (session) {
4796
- if (projectFilter && memoryProject === null && session.project !== projectFilter) continue;
4797
- if (cwdFilter && session.cwd !== cwdFilter) continue;
4835
+ const observationCache = /* @__PURE__ */ new Map();
4836
+ const hybridRequest = {};
4837
+ let enriched = [];
4838
+ let useHybridRanker = hybridRanker !== null;
4839
+ while (true) {
4840
+ hybridRequest.candidateCount = void 0;
4841
+ if (useHybridRanker && hybridRanker) try {
4842
+ results = (await hybridRanker(query, fetchLimit, targetLayer, authorizeGraph, hybridRequest)).map((r) => ({
4843
+ obsId: r.observation.id,
4844
+ sessionId: r.sessionId,
4845
+ score: r.combinedScore,
4846
+ observation: r.observation,
4847
+ layer: r.layer
4848
+ }));
4849
+ } catch (err) {
4850
+ hybridRequest.candidateCount = void 0;
4851
+ logger.warn("hybrid ranking failed", { error: err instanceof Error ? err.message : String(err) });
4852
+ if (enriched.length > 0) break;
4853
+ useHybridRanker = false;
4854
+ results = idx.search(query, fetchLimit, targetLayer);
4855
+ }
4856
+ else results = idx.search(query, fetchLimit, targetLayer);
4857
+ enriched = [];
4858
+ let batchSize = Math.min(10, effectiveLimit);
4859
+ for (let offset = 0; offset < Math.min(results.length, fetchLimit) && enriched.length < effectiveLimit;) {
4860
+ const batch = results.slice(offset, Math.min(offset + batchSize, fetchLimit));
4861
+ offset += batch.length;
4862
+ const obsResults = await Promise.all(batch.map(async (r) => {
4863
+ const layer = r.layer ?? idx.layerOf(r.obsId) ?? getSearchResultLayer(r.obsId, r.sessionId);
4864
+ if (!matchesSearchLayer(r.obsId, r.sessionId, targetLayer, layer)) return null;
4865
+ if (filtering) {
4866
+ const memoryProject = projectFilter && layer === "memory" ? await loadMemoryProject(r.obsId) : null;
4867
+ if (projectFilter && memoryProject !== null && memoryProject !== projectFilter) return null;
4868
+ const session = projectFilter || cwdFilter ? await loadSession(r.sessionId) : null;
4869
+ if (projectFilter && memoryProject === null && !session) return null;
4870
+ if (cwdFilter && layer === "observation" && !session) return null;
4871
+ if (session) {
4872
+ if (projectFilter && memoryProject === null && session.project !== projectFilter) return null;
4873
+ if (cwdFilter && session.cwd !== cwdFilter) return null;
4874
+ }
4875
+ }
4876
+ const key = JSON.stringify([r.obsId, r.sessionId]);
4877
+ let pending = observationCache.get(key);
4878
+ if (!pending) {
4879
+ pending = (async () => {
4880
+ if (r.observation) return {
4881
+ observation: r.observation,
4882
+ layer: r.layer ?? idx.layerOf(r.obsId) ?? getSearchResultLayer(r.obsId, r.sessionId)
4883
+ };
4884
+ const obs = await kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
4885
+ if (obs) return {
4886
+ observation: obs,
4887
+ layer: "observation"
4888
+ };
4889
+ const mem = await kv.get(KV.memories, r.obsId).catch(() => null);
4890
+ return mem ? {
4891
+ observation: memoryToObservation(mem),
4892
+ layer: "memory"
4893
+ } : null;
4894
+ })();
4895
+ observationCache.set(key, pending);
4896
+ }
4897
+ return pending;
4898
+ }));
4899
+ for (let i = 0; i < batch.length; i++) {
4900
+ const resolved = obsResults[i];
4901
+ if (!resolved) continue;
4902
+ const obs = resolved.observation;
4903
+ if (!matchesSearchLayer(obs.id, obs.sessionId, targetLayer, resolved.layer)) continue;
4904
+ if (filterAgentId !== void 0 && obs.agentId !== filterAgentId) continue;
4905
+ if (enriched.length >= effectiveLimit) break;
4906
+ enriched.push({
4907
+ observation: withoutObservationSource(obs),
4908
+ layer: resolved.layer,
4909
+ score: batch[i].score,
4910
+ sessionId: batch[i].sessionId
4911
+ });
4798
4912
  }
4913
+ batchSize = Math.min(10, Math.max(effectiveLimit - enriched.length, batchSize * 2));
4799
4914
  }
4800
- candidates.push(r);
4801
- }
4802
- const obsResults = await Promise.all(candidates.map(async (r) => {
4803
- if (r.observation) return {
4804
- observation: r.observation,
4805
- layer: r.layer ?? idx.layerOf(r.obsId) ?? getSearchResultLayer(r.obsId, r.sessionId)
4806
- };
4807
- const obs = await kv.get(KV.observations(r.sessionId), r.obsId).catch(() => null);
4808
- if (obs) return {
4809
- observation: obs,
4810
- layer: "observation"
4811
- };
4812
- const mem = await kv.get(KV.memories, r.obsId).catch(() => null);
4813
- return mem ? {
4814
- observation: memoryToObservation(mem),
4815
- layer: "memory"
4816
- } : null;
4817
- }));
4818
- const enriched = [];
4819
- for (let i = 0; i < candidates.length; i++) {
4820
- const resolved = obsResults[i];
4821
- if (!resolved) continue;
4822
- const obs = resolved.observation;
4823
- if (!matchesSearchLayer(obs.id, obs.sessionId, targetLayer, resolved.layer)) continue;
4824
- if (filterAgentId !== void 0 && obs.agentId !== filterAgentId) continue;
4825
- if (enriched.length >= effectiveLimit) break;
4826
- enriched.push({
4827
- observation: withoutObservationSource(obs),
4828
- layer: resolved.layer,
4829
- score: candidates[i].score,
4830
- sessionId: candidates[i].sessionId
4831
- });
4915
+ if (!filtering || enriched.length >= effectiveLimit || (hybridRequest.candidateCount ?? results.length) < fetchLimit || fetchLimit >= MAX_CANDIDATES) break;
4916
+ fetchLimit = Math.min(fetchLimit * 2, MAX_CANDIDATES);
4832
4917
  }
4833
4918
  const response = buildRecallResponse(enriched, format, tokenBudget);
4834
4919
  recordAccessBatch(kv, enriched.slice(0, response.results.length).map((result) => result.observation.id));
@@ -5579,7 +5664,7 @@ function registerCompressFunction(sdk, kv, provider, metricsStore) {
5579
5664
  retryable: false
5580
5665
  };
5581
5666
  const committedRevision = observationRevision(compressed);
5582
- await bounded(() => vectorIndexAddGuarded(observationId, sessionId, compressed.title + " " + compressed.narrative, {
5667
+ await bounded(() => vectorIndexAddGuarded(observationId, sessionId, observationEmbedText(compressed), {
5583
5668
  kind: "observation",
5584
5669
  logId: observationId
5585
5670
  }, (embedding, isCurrent) => withKeyedLock(`obs:${sessionId}`, async () => {
@@ -5909,4 +5994,4 @@ function registerCompressionRecovery(sdk, kv) {
5909
5994
  //#endregion
5910
5995
  export { normalizeAccessLog as $, resolveDataDir as $t, getVectorBackfillState as A, withGraphSnapshotUnknownMutation as At, scheduleIndexSave as B, resolveGraphScope as Bt, findUnindexedObservations as C, initializeGraphIndex as Ct, getKeywordRebuildEpoch as D, recoverGraphIndex as Dt, getIndexPersistenceStatus as E, reconcileGraphIndexRecord as Et, isMemoryIndexReady as F, isLegacyGraphState as Ft, setVectorBackfillState as G, retainSent as Gt, setHybridRanker as H, drainSpool as Ht, markKeywordRebuildPending as I, notifyGraphGenerationChange as It, vectorIndexRemove as J, spoolSummary as Jt, setVectorIndex as K, spoolDir as Kt, rankMemoryIds as L, pinGraphState as Lt, indexRecords as M, noteGraphSnapshotWrite as Mt, isBm25RebuildIncomplete as N, withoutGraphRecovery as Nt, getPendingVectorBackfillCount as O, withGraphIndexMutation as Ot, isKeywordRebuildInProgress as P, getGraphGeneration as Pt, emptyAccessLog as Q, withWriteScrubbing as Qt, rebuildKeywordIndex as R, pinGraphStateGeneration as Rt, backfillVectorBacklog as S, graphEdgeLookupKey as St, getEmbeddingProvider as T, normalizeGraphIndexText as Tt, setIndexPersistence as U, parseSentMark as Ut, setEmbeddingProvider as V, setGraphGenerationListener as Vt, setPendingVectorBackfillCount as W, reconcileSent as Wt, withoutObservationSource as X, scrubRecord as Xt, rawFromObservationSource as Y, registerPrivacyFunction as Yt, deleteAccessLog as Z, stripPrivateData as Zt, getLiveEventsStats as _, GRAPH_INDEX_MAX_ID_BYTES as _t, markCompressionDispatchFailed as a, runtimeMetadataPath as an, markLlmFunctions as at, validateOutput as b, finishGraphIndexBuild as bt, isValidEventId as c, singleFlight as ct, budgetLiveObservationSource as d, runGraphCompactOnBoot as dt, SHUTDOWN_FLUSH_TIMEOUT_MS as en, recordAccessBatch as et, SAFE_PAYLOAD_BYTES as f, setGraphCompactBootDisabled as ft, emitLiveEvent as g, registerGraphFunction as gt, payloadByteLength as h, persistGraphDelta as ht, compressionRaw as i, settleWithin as in, evaluateStatus as it, getVectorIndex as j, ensureGraphConsistency as jt, getSearchIndex as k, withGraphSnapshotIndexBarrier as kt, buildSyntheticCompression as l, GRAPH_COMPACT_BOOT_DELAY_MS as lt, oversizedPayloadError as m, boundSources as mt, compressionInFlight as n, registerStartupCleanup as nn, CURL_AUTH_HEADER as nt, needsCompression as o, prefersHtml as ot, checkPayloadFrameSize as p, COMPACT_SCOPES as pt, vectorIndexAddGuarded as q, spoolPolicy as qt, registerCompressFunction as r, runStartupCleanup as rn, UNINDEXED_SCAN_REUSE_MS as rt, pendingCompression as s, renderStatusHtml as st, parseCompressionScope as t, SHUTDOWN_HARD_EXIT_MS as tn, SearchIndex as tt, budgetImportedObservationSources as u, getGraphCompactBootStatus as ut, liveEvents as v, GraphIndexError as vt, flushIndexSave as w, mutateIndexedGraphRecord as wt, SummaryOutputSchema as x, getGraphIndexStatus as xt, scoreSummary as y, createGraphIndexReader as yt, registerSearchFunction as z, replaceGraphGeneration as zt };
5911
5996
 
5912
- //# sourceMappingURL=compression-recovery-CnhLQC6N.mjs.map
5997
+ //# sourceMappingURL=compression-recovery-CZh63Jmz.mjs.map