@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,6 +1,6 @@
1
- import { a as KV, c as jaccardSimilarity, i as withKeyedLocks, n as isKeyedLockHeld, o as fingerprintId, r as withKeyedLock, s as generateId, t as afterKeyedLock } from "./keyed-mutex-DCUh7PG-.mjs";
2
- import { $ as getVectorBucketSize, B as getConsolidationCooldownMs, C as III_PINNED_VERSION, D as logger, E as bootWarn, F as describeLlmRoute, G as getFollowupWindowSeconds, H as getConsolidationIntervalMs, I as detectEmbeddingProvider, J as getSessionSweepStaleHours, K as getIndexSaveIntervalMs, L as detectLlmProviderKind, N as runtimeConfigPath, P as MAX_IMPORT_OBS_PER_SESSION, Q as getVectorBackfillMax, R as getAgentId, S as getVisibleTools, T as bootLog, U as getEnvVar, V as getConsolidationDecayDays, W as getFinalizeIdleMs, X as getStateBackend, _ as safeAudit, a as secretFilePath, at as isDropStaleIndexEnabled, c as memoryToObservation, ct as isSessionSweepEnabled, dt as loadConfig, et as hydrateProcessEnvFromFile, f as matchesSearchLayer, ft as loadEmbeddingConfig, g as runAuditRetentionSweep, h as recordAudit, ht as loadTeamConfig, it as isContextInjectionEnabled, j as engineStateConfigPaths, k as captureDurableAfterMs, l as getSearchResultLayer, lt as isVectorBackfillAllEnabled, m as queryAudit, mt as loadSnapshotConfig, n as ensureServerSecret, nt as isAutoCompressEnabled, ot as isGraphCompactOnBootEnabled, p as AUDIT_MIGRATION_STATE_KEY, pt as loadFallbackConfig, r as explicitSecret, rt as isConsolidationEnabled, s as lessonToObservation, st as isGraphExtractionEnabled, tt as isAgentScopeIsolated, u as isSearchLayer, ut as loadClaudeBridgeConfig, v as setAuditRecordedListener, w as VERSION, x as getAllTools, y as startAuditMigration, z as getAuditRetentionMonths } from "./secret-store-D9GwO0xp.mjs";
3
- import { $ as normalizeAccessLog, A as getVectorBackfillState, At as withGraphSnapshotUnknownMutation, B as scheduleIndexSave, Bt as resolveGraphScope, C as findUnindexedObservations, Ct as initializeGraphIndex, D as getKeywordRebuildEpoch, Dt as recoverGraphIndex, E as getIndexPersistenceStatus, Et as reconcileGraphIndexRecord, F as isMemoryIndexReady, Ft as isLegacyGraphState, G as setVectorBackfillState, Gt as retainSent, H as setHybridRanker, Ht as drainSpool, I as markKeywordRebuildPending, It as notifyGraphGenerationChange, J as vectorIndexRemove, Jt as spoolSummary, K as setVectorIndex, Kt as spoolDir, L as rankMemoryIds, Lt as pinGraphState, M as indexRecords, Mt as noteGraphSnapshotWrite, N as isBm25RebuildIncomplete, Nt as withoutGraphRecovery, O as getPendingVectorBackfillCount, Ot as withGraphIndexMutation, P as isKeywordRebuildInProgress, Pt as getGraphGeneration, Q as emptyAccessLog, Qt as withWriteScrubbing, R as rebuildKeywordIndex, Rt as pinGraphStateGeneration, S as backfillVectorBacklog, St as graphEdgeLookupKey, T as getEmbeddingProvider, Tt as normalizeGraphIndexText, U as setIndexPersistence, V as setEmbeddingProvider, Vt as setGraphGenerationListener, W as setPendingVectorBackfillCount, Wt as reconcileSent, X as withoutObservationSource, Xt as scrubRecord, Y as rawFromObservationSource, Yt as registerPrivacyFunction, Z as deleteAccessLog, Zt as stripPrivateData, _ as getLiveEventsStats, a as markCompressionDispatchFailed, an as runtimeMetadataPath, at as markLlmFunctions, b as validateOutput, bt as finishGraphIndexBuild, c as isValidEventId, ct as singleFlight, d as budgetLiveObservationSource, dt as runGraphCompactOnBoot, en as SHUTDOWN_FLUSH_TIMEOUT_MS, et as recordAccessBatch, f as SAFE_PAYLOAD_BYTES, ft as setGraphCompactBootDisabled, g as emitLiveEvent, gt as registerGraphFunction, h as payloadByteLength, ht as persistGraphDelta, i as compressionRaw, in as settleWithin, it as evaluateStatus, j as getVectorIndex, jt as ensureGraphConsistency, k as getSearchIndex, kt as withGraphSnapshotIndexBarrier, l as buildSyntheticCompression, lt as GRAPH_COMPACT_BOOT_DELAY_MS, m as oversizedPayloadError, mt as boundSources, n as compressionInFlight, o as needsCompression, ot as prefersHtml, p as checkPayloadFrameSize, pt as COMPACT_SCOPES, q as vectorIndexAddGuarded, qt as spoolPolicy, r as registerCompressFunction, rn as runStartupCleanup, rt as UNINDEXED_SCAN_REUSE_MS, s as pendingCompression, st as renderStatusHtml, t as parseCompressionScope, tn as SHUTDOWN_HARD_EXIT_MS, tt as SearchIndex, u as budgetImportedObservationSources, ut as getGraphCompactBootStatus, v as liveEvents, vt as GraphIndexError, w as flushIndexSave, wt as mutateIndexedGraphRecord, x as SummaryOutputSchema, xt as getGraphIndexStatus, y as scoreSummary, yt as createGraphIndexReader, z as registerSearchFunction, zt as replaceGraphGeneration } from "./compression-recovery-CnhLQC6N.mjs";
1
+ import { a as KV, c as jaccardSimilarity, i as withKeyedLocks, 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 { B as detectLlmProviderKind, C as getAllTools, D as bootLog, E as VERSION, F as runtimeConfigPath, G as getConsolidationDecayDays, H as getAgentId, J as getFinalizeIdleMs, K as getConsolidationIntervalMs, L as MAX_IMPORT_OBS_PER_SESSION, N as engineStateConfigPaths, O as bootWarn, Q as getSessionSweepStaleHours, R as describeLlmRoute, T as III_PINNED_VERSION, U as getAuditRetentionMonths, V as embeddingOptInHint, W as getConsolidationCooldownMs, X as getIndexSaveIntervalMs, Y as getFollowupWindowSeconds, _ as recordAudit, _t as loadEmbeddingConfig, a as secretFilePath, at as isAgentScopeIsolated, b as setAuditRecordedListener, bt as loadTeamConfig, c as memoryToObservation, ct as isContextInjectionEnabled, d as getSearchResultLayer, dt as isGraphExtractionEnabled, et as getStateBackend, f as isSearchLayer, ft as isSessionSweepEnabled, g as queryAudit, gt as loadConfig, h as AUDIT_MIGRATION_STATE_KEY, ht as loadClaudeBridgeConfig, it as hydrateProcessEnvFromFile, j as captureDurableAfterMs, k as logger, l as observationEmbedText, lt as isDropStaleIndexEnabled, m as matchesSearchLayer, mt as llmOptInHint, n as ensureServerSecret, nt as getVectorBackfillMax, ot as isAutoCompressEnabled, pt as isVectorBackfillAllEnabled, q as getEnvVar, r as explicitSecret, rt as getVectorBucketSize, s as lessonToObservation, st as isConsolidationEnabled, u as supersededAt, ut as isGraphCompactOnBootEnabled, v as runAuditRetentionSweep, vt as loadFallbackConfig, w as getVisibleTools, x as startAuditMigration, y as safeAudit, yt as loadSnapshotConfig, z as detectEmbeddingProvider } from "./secret-store-iYXRcrO8.mjs";
3
+ import { $ as normalizeAccessLog, A as getVectorBackfillState, At as withGraphSnapshotUnknownMutation, B as scheduleIndexSave, Bt as resolveGraphScope, C as findUnindexedObservations, Ct as initializeGraphIndex, D as getKeywordRebuildEpoch, Dt as recoverGraphIndex, E as getIndexPersistenceStatus, Et as reconcileGraphIndexRecord, F as isMemoryIndexReady, Ft as isLegacyGraphState, G as setVectorBackfillState, Gt as retainSent, H as setHybridRanker, Ht as drainSpool, I as markKeywordRebuildPending, It as notifyGraphGenerationChange, J as vectorIndexRemove, Jt as spoolSummary, K as setVectorIndex, Kt as spoolDir, L as rankMemoryIds, Lt as pinGraphState, M as indexRecords, Mt as noteGraphSnapshotWrite, N as isBm25RebuildIncomplete, Nt as withoutGraphRecovery, O as getPendingVectorBackfillCount, Ot as withGraphIndexMutation, P as isKeywordRebuildInProgress, Pt as getGraphGeneration, Q as emptyAccessLog, Qt as withWriteScrubbing, R as rebuildKeywordIndex, Rt as pinGraphStateGeneration, S as backfillVectorBacklog, St as graphEdgeLookupKey, T as getEmbeddingProvider, Tt as normalizeGraphIndexText, U as setIndexPersistence, V as setEmbeddingProvider, Vt as setGraphGenerationListener, W as setPendingVectorBackfillCount, Wt as reconcileSent, X as withoutObservationSource, Xt as scrubRecord, Y as rawFromObservationSource, Yt as registerPrivacyFunction, Z as deleteAccessLog, Zt as stripPrivateData, _ as getLiveEventsStats, a as markCompressionDispatchFailed, an as runtimeMetadataPath, at as markLlmFunctions, b as validateOutput, bt as finishGraphIndexBuild, c as isValidEventId, ct as singleFlight, d as budgetLiveObservationSource, dt as runGraphCompactOnBoot, en as SHUTDOWN_FLUSH_TIMEOUT_MS, et as recordAccessBatch, f as SAFE_PAYLOAD_BYTES, ft as setGraphCompactBootDisabled, g as emitLiveEvent, gt as registerGraphFunction, h as payloadByteLength, ht as persistGraphDelta, i as compressionRaw, in as settleWithin, it as evaluateStatus, j as getVectorIndex, jt as ensureGraphConsistency, k as getSearchIndex, kt as withGraphSnapshotIndexBarrier, l as buildSyntheticCompression, lt as GRAPH_COMPACT_BOOT_DELAY_MS, m as oversizedPayloadError, mt as boundSources, n as compressionInFlight, o as needsCompression, ot as prefersHtml, p as checkPayloadFrameSize, pt as COMPACT_SCOPES, q as vectorIndexAddGuarded, qt as spoolPolicy, r as registerCompressFunction, rn as runStartupCleanup, rt as UNINDEXED_SCAN_REUSE_MS, s as pendingCompression, st as renderStatusHtml, t as parseCompressionScope, tn as SHUTDOWN_HARD_EXIT_MS, tt as SearchIndex, u as budgetImportedObservationSources, ut as getGraphCompactBootStatus, v as liveEvents, vt as GraphIndexError, w as flushIndexSave, wt as mutateIndexedGraphRecord, x as SummaryOutputSchema, xt as getGraphIndexStatus, y as scoreSummary, yt as createGraphIndexReader, z as registerSearchFunction, zt as replaceGraphGeneration } from "./compression-recovery-CZh63Jmz.mjs";
4
4
  import { a as saveImageToDisk, i as isManagedImagePath, n as deleteImage, r as getMaxBytes, t as IMAGES_DIR } from "./image-store-BsN4vyjr.mjs";
5
5
  import { a as pageAfterCursor, c as sortByKeyDesc, i as matchesText, n as parseSearchExpansionIds, o as pageByOffset, r as encodeCursor, s as parseListQuery, t as parseSessionArguments } from "./session-arguments-zxsbbd8y.mjs";
6
6
  import { execFile, execFileSync, execSync } from "node:child_process";
@@ -408,9 +408,11 @@ var OpenAIProvider = class {
408
408
  throw new Error(`OpenAI API error (${response.status}): ${text}`);
409
409
  }
410
410
  const data = await response.json();
411
- const message = data.choices?.[0]?.message;
411
+ const choice = data.choices?.[0];
412
+ const message = choice?.message;
412
413
  const content = message?.content;
413
- if (content) return content;
414
+ if (content?.trim()) return content;
415
+ if (choice?.finish_reason === "length") throw new Error(`OpenAI response hit max_tokens (${this.maxTokens}) before any content. Raise MAX_TOKENS or lower OPENAI_REASONING_EFFORT.`);
414
416
  const reasoning = message?.reasoning ?? message?.reasoning_content;
415
417
  if (reasoning) return reasoning;
416
418
  throw new Error(`OpenAI returned unexpected response: ${JSON.stringify(data).slice(0, 200)}`);
@@ -1218,8 +1220,84 @@ var StateKV = class {
1218
1220
  if (this.backend !== "redis" || !Array.isArray(values)) return values;
1219
1221
  return orderLikeInsertion(values);
1220
1222
  }
1223
+ async listGroups() {
1224
+ const groups = (await this.sdk.trigger({
1225
+ function_id: "state::list_groups",
1226
+ payload: {}
1227
+ }))?.groups;
1228
+ if (!Array.isArray(groups) || !groups.every((group) => typeof group === "string")) throw new Error("state::list_groups returned an unexpected shape");
1229
+ return groups;
1230
+ }
1221
1231
  };
1222
1232
  //#endregion
1233
+ //#region src/state/state-readiness.ts
1234
+ const STATE_READY_TIMEOUT_MS = 12e4;
1235
+ const STATE_READY_INTERVAL_MS = 100;
1236
+ const PROBE_TIMEOUT_MS = 1e4;
1237
+ const READINESS_PROBE_KEY = "__agentmemory_state_readiness__";
1238
+ function errorMessage$3(error) {
1239
+ return error instanceof Error ? error.message : String(error);
1240
+ }
1241
+ function isRetryableProbeError(error) {
1242
+ const code = error?.code;
1243
+ if (code === "function_not_found" || code === "invocation_timeout" || code === "timeout") return true;
1244
+ return /Function state::(?:get|list) not found|timed out|timeout|ETIMEDOUT/i.test(errorMessage$3(error));
1245
+ }
1246
+ async function waitForStateReady(sdk, options = {}) {
1247
+ const scope = options.scope ?? KV.config;
1248
+ const timeoutMs = options.timeoutMs ?? STATE_READY_TIMEOUT_MS;
1249
+ const intervalMs = options.intervalMs ?? STATE_READY_INTERVAL_MS;
1250
+ const now = options.now ?? (() => performance.now());
1251
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1252
+ const startedAt = now();
1253
+ let attempts = 0;
1254
+ let lastError;
1255
+ const probeTimeoutMs = () => Math.min(PROBE_TIMEOUT_MS, Math.max(1, timeoutMs - (now() - startedAt)));
1256
+ for (;;) {
1257
+ attempts += 1;
1258
+ try {
1259
+ await sdk.trigger({
1260
+ function_id: "state::list",
1261
+ payload: { scope },
1262
+ timeoutMs: probeTimeoutMs()
1263
+ });
1264
+ await sdk.trigger({
1265
+ function_id: "state::get",
1266
+ payload: {
1267
+ scope,
1268
+ key: READINESS_PROBE_KEY
1269
+ },
1270
+ timeoutMs: probeTimeoutMs()
1271
+ });
1272
+ return {
1273
+ ready: true,
1274
+ waitedMs: Math.round(now() - startedAt),
1275
+ attempts
1276
+ };
1277
+ } catch (error) {
1278
+ lastError = errorMessage$3(error);
1279
+ if (!isRetryableProbeError(error)) return {
1280
+ ready: false,
1281
+ waitedMs: Math.round(now() - startedAt),
1282
+ attempts,
1283
+ error: lastError
1284
+ };
1285
+ if (attempts === 1) options.onWaiting?.(lastError);
1286
+ }
1287
+ const elapsed = now() - startedAt;
1288
+ if (elapsed >= timeoutMs) return {
1289
+ ready: false,
1290
+ waitedMs: Math.round(elapsed),
1291
+ attempts,
1292
+ error: lastError
1293
+ };
1294
+ await sleep(Math.min(intervalMs, timeoutMs - elapsed));
1295
+ }
1296
+ }
1297
+ function assertStateReady(readiness) {
1298
+ if (!readiness.ready) throw new Error(`State worker did not become ready within ${readiness.waitedMs}ms: ${readiness.error ?? "timeout"}`);
1299
+ }
1300
+ //#endregion
1223
1301
  //#region src/state/vector-index.ts
1224
1302
  function float32ToBase64(arr) {
1225
1303
  return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).toString("base64");
@@ -1982,10 +2060,12 @@ function registerQueryExpansionFunction(sdk, provider) {
1982
2060
  }
1983
2061
  });
1984
2062
  }
2063
+ const CJK_RUN = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
1985
2064
  function extractEntitiesFromQuery(query) {
1986
2065
  const entities = [];
1987
2066
  const quoted = query.match(/"([^"]+)"/g);
1988
2067
  if (quoted) for (const q of quoted) entities.push(q.replace(/"/g, ""));
2068
+ for (const run of query.matchAll(CJK_RUN)) for (const seg of segmentCjk(run[0])) if (seg.trim().length >= 2) entities.push(seg.trim());
1989
2069
  const capitalized = query.match(/\b[A-Z][a-zA-Z0-9_.-]+\b/g);
1990
2070
  if (capitalized) {
1991
2071
  const stopWords = new Set([
@@ -2128,8 +2208,8 @@ var HybridSearch = class {
2128
2208
  this.rerankEnabled = rerankEnabled;
2129
2209
  this.graphRetrieval = new GraphRetrieval(kv, (id) => this.bm25.layerOf(id), (id) => this.bm25.sessionOf(id));
2130
2210
  }
2131
- async search(query, limit = 20, targetLayer = "all", authorizeGraph) {
2132
- return this.tripleStreamSearch(query, limit, targetLayer, this.graphRetrieval.createQuery(authorizeGraph));
2211
+ async search(query, limit = 20, targetLayer = "all", authorizeGraph, request) {
2212
+ return this.tripleStreamSearch(query, limit, targetLayer, this.graphRetrieval.createQuery(authorizeGraph), void 0, true, request);
2133
2213
  }
2134
2214
  async searchWithExpansion(query, limit, expansion, targetLayer = "all", authorizeGraph) {
2135
2215
  const allQueries = [
@@ -2149,13 +2229,25 @@ var HybridSearch = class {
2149
2229
  const combined = Array.from(merged.values()).sort((a, b) => b.combinedScore - a.combinedScore || (a.observation.id < b.observation.id ? -1 : a.observation.id > b.observation.id ? 1 : 0));
2150
2230
  return this.rerankResults(query, combined, limit);
2151
2231
  }
2152
- async tripleStreamSearch(query, limit, targetLayer, graphQuery, entityHints, applyReranking = true) {
2232
+ async tripleStreamSearch(query, limit, targetLayer, graphQuery, entityHints, applyReranking = true, request) {
2153
2233
  const retrievalLimit = this.rerankEnabled ? Math.max(limit, 20) : limit;
2154
2234
  const bm25Results = this.bm25.search(query, retrievalLimit * 2, targetLayer);
2155
2235
  let vectorResults = [];
2156
2236
  let queryEmbedding = null;
2157
2237
  if (this.vector && this.embeddingProvider && this.vector.size > 0) try {
2158
- queryEmbedding = await this.embeddingProvider.embed(query);
2238
+ if (request) {
2239
+ if (!request.embedding || request.embedding.query !== query) request.embedding = {
2240
+ query,
2241
+ value: this.embeddingProvider.embed(query)
2242
+ };
2243
+ const embedding = request.embedding;
2244
+ try {
2245
+ queryEmbedding = await embedding.value;
2246
+ } catch (error) {
2247
+ if (request.embedding === embedding) request.embedding = void 0;
2248
+ throw error;
2249
+ }
2250
+ } else queryEmbedding = await this.embeddingProvider.embed(query);
2159
2251
  vectorResults = this.vector.search(queryEmbedding, retrievalLimit * 2, targetLayer, (id) => this.bm25.layerOf(id));
2160
2252
  } catch {}
2161
2253
  const entities = entityHints && entityHints.length > 0 ? entityHints : extractEntitiesFromQuery(query);
@@ -2230,6 +2322,7 @@ var HybridSearch = class {
2230
2322
  graphContext: s.graphContext,
2231
2323
  combinedScore
2232
2324
  }));
2325
+ if (request) request.candidateCount = combined.length;
2233
2326
  const retrievalDepth = Math.max(limit, 20);
2234
2327
  const diversified = this.diversifyBySession(combined, retrievalDepth);
2235
2328
  const enriched = await this.enrichResults(diversified, retrievalDepth);
@@ -2333,6 +2426,10 @@ const PENDING_LOG_SAVE_THRESHOLD = 500;
2333
2426
  const EARLY_SAVE_MIN_GAP_MS = 5e3;
2334
2427
  const BACKFILL_MARKER_KEY = "vectors:backfill";
2335
2428
  const MEMORY_ID_PREFIX = "mem_";
2429
+ const LEGACY_SHARD_SCOPE_PREFIXES = [`${KV.bm25Index}:bm25:`, `${KV.bm25Index}:vectors:`];
2430
+ const LEGACY_SHARD_KEY = "data";
2431
+ const LEGACY_SHARD_SUFFIX = /^((?:[0-9a-z]+-[0-9a-f]{32})|(?:idx_[0-9a-z]+_[0-9a-f]{12})):\d{5}$/;
2432
+ const ORPHAN_SWEEP_KEY = "legacy:sweep";
2336
2433
  function auditIndexPersistEnabled() {
2337
2434
  const raw = process.env.AGENTMEMORY_AUDIT_INDEX_PERSIST;
2338
2435
  if (!raw) return false;
@@ -2367,6 +2464,11 @@ function isValidShardDescriptor(shard) {
2367
2464
  const candidate = shard;
2368
2465
  return typeof candidate.scope === "string" && candidate.scope.length > 0 && typeof candidate.key === "string" && candidate.key.length > 0 && Number.isInteger(candidate.chars) && candidate.chars >= 0;
2369
2466
  }
2467
+ function legacyShardGeneration(scope) {
2468
+ const prefix = LEGACY_SHARD_SCOPE_PREFIXES.find((candidate) => scope.startsWith(candidate));
2469
+ if (!prefix) return void 0;
2470
+ return LEGACY_SHARD_SUFFIX.exec(scope.slice(prefix.length))?.[1] ?? null;
2471
+ }
2370
2472
  function isPersistedVector(row) {
2371
2473
  if (!row || typeof row !== "object") return false;
2372
2474
  const candidate = row;
@@ -2737,7 +2839,7 @@ var IndexPersistence = class {
2737
2839
  }
2738
2840
  async load() {
2739
2841
  if (!this.vector) {
2740
- await this.removeLegacyBm25Snapshot();
2842
+ await this.finishLegacyCleanup();
2741
2843
  return {
2742
2844
  vector: null,
2743
2845
  state: "none",
@@ -2765,7 +2867,7 @@ var IndexPersistence = class {
2765
2867
  this.hasVectorCheckpoint = loaded.size > 0;
2766
2868
  this.checkpointBackfillSince = meta.backfillSince;
2767
2869
  await this.removeLegacyVectorSnapshotIfPresent();
2768
- await this.removeLegacyBm25Snapshot();
2870
+ await this.finishLegacyCleanup();
2769
2871
  const expectedCount = Number.isInteger(meta.count) ? meta.count : void 0;
2770
2872
  this.vectorCountShortfall = expectedCount !== void 0 && loaded.size < expectedCount ? {
2771
2873
  expected: expectedCount,
@@ -2951,7 +3053,7 @@ var IndexPersistence = class {
2951
3053
  savedAt: null
2952
3054
  };
2953
3055
  if (!(manifest.value != null || typeof legacyKey.value === "string" && legacyKey.value.length > 0)) {
2954
- await this.removeLegacyBm25Snapshot();
3056
+ await this.finishLegacyCleanup();
2955
3057
  return {
2956
3058
  vector: null,
2957
3059
  state: "none",
@@ -2984,7 +3086,7 @@ var IndexPersistence = class {
2984
3086
  };
2985
3087
  }
2986
3088
  await this.removeLegacyVectorSnapshot(manifest.value);
2987
- await this.removeLegacyBm25Snapshot();
3089
+ await this.finishLegacyCleanup();
2988
3090
  await this.auditIndexPersistence("migrate", [statePath(KV.bm25Index, VECTOR_META_KEY)], {
2989
3091
  vectors: legacy.size,
2990
3092
  buckets: this.bucketCountInUse()
@@ -3024,6 +3126,71 @@ var IndexPersistence = class {
3024
3126
  if (manifest != null) await this.deleteKey(KV.bm25Index, LEGACY_BM25_MANIFEST_KEY, "legacy_bm25_cleanup");
3025
3127
  if (legacy != null) await this.deleteKey(KV.bm25Index, LEGACY_BM25_KEY, "legacy_bm25_cleanup");
3026
3128
  }
3129
+ async finishLegacyCleanup() {
3130
+ await this.removeLegacyBm25Snapshot();
3131
+ await this.sweepOrphanedGenerations();
3132
+ }
3133
+ async sweepOrphanedGenerations() {
3134
+ try {
3135
+ if (await this.kv.get(KV.bm25Index, ORPHAN_SWEEP_KEY) != null) return;
3136
+ const candidates = /* @__PURE__ */ new Map();
3137
+ for (const scope of await this.kv.listGroups()) {
3138
+ const generation = legacyShardGeneration(scope);
3139
+ if (generation === void 0) continue;
3140
+ if (generation === null) {
3141
+ logger.warn("index persistence: orphan sweep skipped, unrecognized legacy shard scope", { scope });
3142
+ return;
3143
+ }
3144
+ candidates.set(scope, generation);
3145
+ }
3146
+ const named = await this.namedLegacyGenerations();
3147
+ if (!named) return;
3148
+ const orphans = [...candidates].filter(([, generation]) => !named.has(generation));
3149
+ const failures = await inBatches(orphans, WRITE_CONCURRENCY, async ([scope]) => {
3150
+ if (!await this.deleteKey(scope, LEGACY_SHARD_KEY, "orphan_generation_sweep")) throw new Error(scope);
3151
+ });
3152
+ const summary = {
3153
+ generations: new Set(orphans.map(([, generation]) => generation)).size,
3154
+ shards: orphans.length,
3155
+ failed: failures.length
3156
+ };
3157
+ if (failures.length > 0) {
3158
+ logger.warn("index persistence: orphan sweep left shards behind; the next boot retries", summary);
3159
+ return;
3160
+ }
3161
+ if (candidates.size > orphans.length) {
3162
+ if (orphans.length > 0) logger.info("index persistence: swept orphaned legacy index generations", summary);
3163
+ return;
3164
+ }
3165
+ await this.kv.set(KV.bm25Index, ORPHAN_SWEEP_KEY, {
3166
+ v: 1,
3167
+ at: new Date(this.now()).toISOString(),
3168
+ ...summary
3169
+ });
3170
+ await this.auditIndexPersistence("sweep", [statePath(KV.bm25Index, ORPHAN_SWEEP_KEY)], summary);
3171
+ logger.info("index persistence: swept orphaned legacy index generations", summary);
3172
+ } catch (err) {
3173
+ logger.warn("index persistence: orphan sweep failed; the next boot retries", { message: errorMessage$2(err) });
3174
+ }
3175
+ }
3176
+ async namedLegacyGenerations() {
3177
+ const named = /* @__PURE__ */ new Set();
3178
+ for (const [key, label] of [[LEGACY_BM25_MANIFEST_KEY, "bm25"], [LEGACY_VECTOR_MANIFEST_KEY, "vector"]]) {
3179
+ const read = await this.readIndexValue(KV.bm25Index, key, label, "manifest");
3180
+ if (!read.ok) return null;
3181
+ const manifest = read.value;
3182
+ if (manifest == null) continue;
3183
+ const generations = typeof manifest === "object" && Array.isArray(manifest.shards) ? manifest.shards.filter((shard) => typeof shard?.scope === "string" && shard.scope.length > 0).map((shard) => legacyShardGeneration(shard.scope)) : [];
3184
+ if (typeof manifest === "object" && typeof manifest.generation === "string") generations.push(manifest.generation);
3185
+ const usable = generations.filter((generation) => !!generation);
3186
+ for (const generation of usable) named.add(generation);
3187
+ if (usable.length === 0) {
3188
+ logger.warn("index persistence: orphan sweep skipped, a legacy manifest names no generation", { key });
3189
+ return null;
3190
+ }
3191
+ }
3192
+ return named;
3193
+ }
3027
3194
  logFailure(err) {
3028
3195
  const now = this.now();
3029
3196
  if (now - this.lastFailureLogAt < FAILURE_LOG_THROTTLE_MS) return;
@@ -3058,9 +3225,12 @@ var IndexPersistence = class {
3058
3225
  result,
3059
3226
  error
3060
3227
  });
3228
+ return error === void 0;
3061
3229
  }
3062
3230
  async deleteShards(shards, reason) {
3063
- await inBatches(shards, WRITE_CONCURRENCY, (shard) => this.deleteKey(shard.scope, shard.key, reason));
3231
+ await inBatches(shards, WRITE_CONCURRENCY, async (shard) => {
3232
+ await this.deleteKey(shard.scope, shard.key, reason);
3233
+ });
3064
3234
  }
3065
3235
  async loadShardedData(legacyKey, manifestKey, label) {
3066
3236
  const manifest = await this.readIndexValue(KV.bm25Index, manifestKey, label, "manifest");
@@ -3618,7 +3788,7 @@ async function restoreIndexEntries(obs) {
3618
3788
  scheduleIndexSave();
3619
3789
  }
3620
3790
  const vectors = getVectorIndex();
3621
- if (vectors && !vectors.has(obs.id)) await vectorIndexAddGuarded(obs.id, obs.sessionId, obs.title + " " + (obs.narrative || ""), {
3791
+ if (vectors && !vectors.has(obs.id)) await vectorIndexAddGuarded(obs.id, obs.sessionId, observationEmbedText(obs), {
3622
3792
  kind: "synthetic",
3623
3793
  logId: obs.id
3624
3794
  });
@@ -3840,7 +4010,7 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
3840
4010
  }
3841
4011
  getSearchIndex().add(synthetic);
3842
4012
  scheduleIndexSave();
3843
- await vectorIndexAddGuarded(synthetic.id, synthetic.sessionId, synthetic.title + " " + (synthetic.narrative || ""), {
4013
+ await vectorIndexAddGuarded(synthetic.id, synthetic.sessionId, observationEmbedText(synthetic), {
3844
4014
  kind: "synthetic",
3845
4015
  logId: synthetic.id
3846
4016
  });
@@ -5650,7 +5820,7 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
5650
5820
  return {
5651
5821
  success: false,
5652
5822
  error: "no_provider",
5653
- reason: "No LLM provider key set; Summarize is a no-op. Set ANTHROPIC_API_KEY (or GEMINI/OPENROUTER/MINIMAX) in ~/.agentmemory/.env to enable."
5823
+ reason: "No LLM provider chosen; Summarize is a no-op. Set AGENTMEMORY_LLM_PROVIDER and that provider's key in ~/.agentmemory/.env to enable."
5654
5824
  };
5655
5825
  }
5656
5826
  const boundedProvider = {
@@ -6261,6 +6431,7 @@ function registerConsolidateFunction(sdk, kv, provider) {
6261
6431
  const existingMatch = existingMemories.find((m) => m.title.toLowerCase() === parsed.title.toLowerCase() && (!scopedProject || !m.project || m.project === scopedProject));
6262
6432
  if (existingMatch) {
6263
6433
  existingMatch.isLatest = false;
6434
+ existingMatch.updatedAt = supersededAt(existingMatch.updatedAt, now);
6264
6435
  await kv.set(KV.memories, existingMatch.id, existingMatch);
6265
6436
  await recordAudit(kv, "evolve", "mem::consolidate", [existingMatch.id], {
6266
6437
  action: "mark_non_latest",
@@ -6459,25 +6630,52 @@ function registerPatternsFunction(sdk, kv) {
6459
6630
  }
6460
6631
  //#endregion
6461
6632
  //#region src/functions/facets.ts
6462
- async function deleteFacetsForTargets(kv, targetIds) {
6633
+ async function targetLiveness(kv, id) {
6634
+ let observation = false;
6635
+ if (id.startsWith("obs_")) {
6636
+ const sessionId = await lookupObservationSession(kv, id);
6637
+ observation = !!sessionId && !!await kv.get(KV.observations(sessionId), id);
6638
+ }
6639
+ if (id.startsWith("lsn_") || id.startsWith("lesson_")) {
6640
+ const lesson = await kv.get(KV.lessons, id);
6641
+ if (lesson && !lesson.deleted) return {
6642
+ observation,
6643
+ record: true
6644
+ };
6645
+ }
6646
+ const [mem, sem] = await Promise.all([kv.get(KV.memories, id), kv.get(KV.semantic, id)]);
6647
+ return {
6648
+ observation,
6649
+ record: !!(mem || sem)
6650
+ };
6651
+ }
6652
+ function facetTargetLive(facet, liveness) {
6653
+ if (!liveness) return false;
6654
+ if (facet.targetType === "observation") return liveness.observation;
6655
+ return liveness.record || liveness.observation;
6656
+ }
6657
+ async function deleteFacetsForTargets(kv, targetIds, options) {
6463
6658
  const ids = new Set(targetIds);
6464
6659
  ids.delete("");
6465
6660
  if (ids.size === 0) return;
6661
+ const targetTypes = new Set(options?.targetTypes ?? ["memory"]);
6466
6662
  try {
6467
- const live = /* @__PURE__ */ new Set();
6663
+ const live = /* @__PURE__ */ new Map();
6468
6664
  await Promise.all([...ids].map(async (id) => {
6469
6665
  for (let attempt = 0; attempt < 2; attempt++) try {
6470
- const [mem, sem] = await Promise.all([kv.get(KV.memories, id), kv.get(KV.semantic, id)]);
6471
- if (mem || sem) live.add(id);
6666
+ live.set(id, await targetLiveness(kv, id));
6472
6667
  return;
6473
6668
  } catch {
6474
6669
  if (attempt === 1) {
6475
- live.add(id);
6670
+ live.set(id, {
6671
+ observation: true,
6672
+ record: true
6673
+ });
6476
6674
  logger.warn("Facet cleanup skipped unverifiable target", { targetId: id });
6477
6675
  }
6478
6676
  }
6479
6677
  }));
6480
- const matches = (await kv.list(KV.facets)).filter((f) => ids.has(f.targetId) && !live.has(f.targetId) && f.targetType === "memory");
6678
+ const matches = (await kv.list(KV.facets)).filter((f) => ids.has(f.targetId) && targetTypes.has(f.targetType) && !facetTargetLive(f, live.get(f.targetId)));
6481
6679
  await Promise.all(matches.map((f) => kv.delete(KV.facets, f.id)));
6482
6680
  } catch (err) {
6483
6681
  logger.warn("Facet cleanup failed", {
@@ -6753,6 +6951,7 @@ function registerRememberFunction(sdk, kv) {
6753
6951
  if (data.ttlDays && typeof data.ttlDays === "number" && data.ttlDays > 0) memory.forgetAfter = new Date(Date.now() + data.ttlDays * 864e5).toISOString();
6754
6952
  if (supersededMemory) {
6755
6953
  supersededMemory.isLatest = false;
6954
+ supersededMemory.updatedAt = supersededAt(supersededMemory.updatedAt, now);
6756
6955
  await kv.set(KV.memories, supersededMemory.id, supersededMemory);
6757
6956
  try {
6758
6957
  getSearchIndex().remove(supersededMemory.id);
@@ -6775,7 +6974,11 @@ function registerRememberFunction(sdk, kv) {
6775
6974
  nearMatch
6776
6975
  };
6777
6976
  });
6778
- await vectorIndexAddGuarded(memory.id, memory.sessionIds?.[0] ?? "memory", memory.title + " " + memory.content, {
6977
+ await vectorIndexAddGuarded(memory.id, memory.sessionIds?.[0] ?? "memory", observationEmbedText({
6978
+ title: memory.title,
6979
+ narrative: memory.content,
6980
+ concepts: memory.concepts
6981
+ }), {
6779
6982
  kind: "memory",
6780
6983
  logId: memory.id
6781
6984
  }, (embedding, isCurrent) => withKeyedLock("mem:remember", async () => {
@@ -6917,6 +7120,7 @@ function registerRememberFunction(sdk, kv) {
6917
7120
  }
6918
7121
  });
6919
7122
  }
7123
+ if (deletedObservationIds.length > 0) await deleteFacetsForTargets(kv, deletedObservationIds, { targetTypes: ["memory", "observation"] });
6920
7124
  if (deleted > 0 || indexCleaned) await flushIndexSave();
6921
7125
  if (deleted > 0 || failures.length > 0) await recordAudit(kv, "forget", "mem::forget", [...deletedMemoryIds, ...deletedObservationIds], {
6922
7126
  sessionId: data.sessionId,
@@ -7357,6 +7561,7 @@ function registerRelationsFunction(sdk, kv) {
7357
7561
  isLatest: true
7358
7562
  };
7359
7563
  existing.isLatest = false;
7564
+ existing.updatedAt = supersededAt(existing.updatedAt, now);
7360
7565
  await kv.set(KV.memories, existing.id, existing);
7361
7566
  try {
7362
7567
  getSearchIndex().remove(existing.id);
@@ -7377,7 +7582,11 @@ function registerRelationsFunction(sdk, kv) {
7377
7582
  error: err instanceof Error ? err.message : String(err)
7378
7583
  });
7379
7584
  }
7380
- await vectorIndexAddGuarded(evolved.id, evolved.sessionIds?.[0] ?? "memory", evolved.title + " " + evolved.content, {
7585
+ await vectorIndexAddGuarded(evolved.id, evolved.sessionIds?.[0] ?? "memory", observationEmbedText({
7586
+ title: evolved.title,
7587
+ narrative: evolved.content,
7588
+ concepts: evolved.concepts
7589
+ }), {
7381
7590
  kind: "memory",
7382
7591
  logId: evolved.id
7383
7592
  });
@@ -7620,6 +7829,7 @@ function getFollowupStats() {
7620
7829
  };
7621
7830
  }
7622
7831
  const LESSON_CONTENT_PREVIEW_CHARS = 240;
7832
+ const SCOPED_RETRY_CANDIDATES = 1e3;
7623
7833
  function registerSmartSearchFunction(sdk, kv, searchFn) {
7624
7834
  sdk.registerFunction("mem::smart-search", async (data) => {
7625
7835
  if (data.targetLayer !== void 0 && !isSearchLayer(data.targetLayer)) throw new Error("mem::smart-search: targetLayer must be one of 'all', 'memory', or 'observation'");
@@ -7696,16 +7906,32 @@ function registerSmartSearchFunction(sdk, kv, searchFn) {
7696
7906
  return (await read(KV.sessions, sessionId))?.project === project;
7697
7907
  };
7698
7908
  const [hybridResults, lessons] = await Promise.all([searchFn(data.query, overFetchLimit, targetLayer, authorizeGraph), includeLessons ? recallLessons(sdk, data.query, lessonLimit, project) : Promise.resolve([])]);
7699
- const layerResults = hybridResults.filter((result) => matchesSearchLayer(result.observation.id, result.sessionId, targetLayer, result.layer ?? getSearchIndex().layerOf(result.observation.id)));
7700
- const projectResults = matchesProject ? await filterProjectResults(layerResults, (result) => matchesProject(result.observation.id, result.sessionId)) : layerResults;
7701
- const compact = (filterAgentId ? projectResults.filter((r) => r.observation.agentId === filterAgentId).slice(0, limit) : projectResults.slice(0, limit)).map((r) => ({
7702
- obsId: r.observation.id,
7703
- layer: r.layer ?? getSearchIndex().layerOf(r.observation.id) ?? getSearchResultLayer(r.observation.id, r.sessionId),
7704
- sessionId: r.sessionId,
7705
- title: r.observation.title,
7706
- type: r.observation.type,
7707
- score: r.combinedScore,
7708
- timestamp: r.observation.timestamp
7909
+ const scopeResults = async (results) => {
7910
+ const layerResults = results.filter((result) => matchesSearchLayer(result.observation.id, result.sessionId, targetLayer, result.layer ?? getSearchIndex().layerOf(result.observation.id)));
7911
+ const projectResults = matchesProject ? await filterProjectResults(layerResults, (result) => matchesProject(result.observation.id, result.sessionId)) : layerResults;
7912
+ return filterAgentId ? projectResults.filter((r) => r.observation.agentId === filterAgentId).slice(0, limit) : projectResults.slice(0, limit);
7913
+ };
7914
+ let filteredHybrid = await scopeResults(hybridResults);
7915
+ if ((filterAgentId || project) && filteredHybrid.length < limit && getSearchIndex().size > overFetchLimit) try {
7916
+ const deeper = await scopeResults(await searchFn(data.query, SCOPED_RETRY_CANDIDATES, targetLayer, authorizeGraph));
7917
+ const seen = new Set(deeper.map((result) => result.observation.id));
7918
+ filteredHybrid = [...deeper, ...filteredHybrid.filter((result) => !seen.has(result.observation.id))].slice(0, limit);
7919
+ } catch (err) {
7920
+ logger.warn("Smart search deeper scoped ranking failed; keeping first-pass results", { error: err instanceof Error ? err.message : String(err) });
7921
+ }
7922
+ const compact = await Promise.all(filteredHybrid.map(async (r) => {
7923
+ const layer = r.layer ?? getSearchIndex().layerOf(r.observation.id) ?? getSearchResultLayer(r.observation.id, r.sessionId);
7924
+ const memoryType = layer === "memory" ? (await kv.get(KV.memories, r.observation.id).catch(() => null))?.type ?? r.observation.memoryType : void 0;
7925
+ return {
7926
+ obsId: r.observation.id,
7927
+ layer,
7928
+ sessionId: r.sessionId,
7929
+ title: r.observation.title,
7930
+ type: r.observation.type,
7931
+ ...memoryType !== void 0 && { memoryType },
7932
+ score: r.combinedScore,
7933
+ timestamp: r.observation.timestamp
7934
+ };
7709
7935
  }));
7710
7936
  recordAccessBatch(kv, compact.map((r) => r.obsId));
7711
7937
  if (data.sessionId && typeof data.sessionId === "string" && data.source !== "viewer" && compact.length > 0) {
@@ -7906,6 +8132,7 @@ function registerRecentSearchesSweepFunction(sdk, kv) {
7906
8132
  const HOUR_MS = 3600 * 1e3;
7907
8133
  function isStaleActive(session, cutoff) {
7908
8134
  if (!session || session.status !== "active") return false;
8135
+ if (typeof session.id !== "string" || session.id.length === 0) return false;
7909
8136
  const freshness = session.updatedAt ?? session.startedAt;
7910
8137
  if (!freshness) return false;
7911
8138
  const ts = new Date(freshness).getTime();
@@ -8201,6 +8428,7 @@ function registerAutoForgetFunction(sdk, kv) {
8201
8428
  if (!dryRun) {
8202
8429
  const older = new Date(memA.createdAt).getTime() < new Date(memB.createdAt).getTime() ? memA : memB;
8203
8430
  older.isLatest = false;
8431
+ older.updatedAt = supersededAt(older.updatedAt, (/* @__PURE__ */ new Date()).toISOString());
8204
8432
  await kv.set(KV.memories, older.id, older);
8205
8433
  await recordAudit(kv, "forget", "mem::auto-forget", [older.id], {
8206
8434
  resource: "memory",
@@ -8723,6 +8951,7 @@ function registerLessonsFunctions(sdk, kv) {
8723
8951
  lessonRecords.delete(lesson.id);
8724
8952
  if (lessonIndex) lessonIndex.remove(lesson.id);
8725
8953
  noteLessonMutation();
8954
+ await deleteFacetsForTargets(kv, [lesson.id]);
8726
8955
  try {
8727
8956
  await recordAudit(kv, "lesson_delete", "mem::lesson-delete", [lesson.id]);
8728
8957
  } catch {}
@@ -8768,11 +8997,14 @@ function registerLessonsFunctions(sdk, kv) {
8768
8997
  }
8769
8998
  }
8770
8999
  await Promise.all(dirty.map((l) => kv.set(KV.lessons, l.id, l)));
9000
+ const softDeletedIds = [];
8771
9001
  for (const l of dirty) if (l.deleted) {
9002
+ softDeletedIds.push(l.id);
8772
9003
  lessonRecords.delete(l.id);
8773
9004
  if (lessonIndex) lessonIndex.remove(l.id);
8774
9005
  } else lessonRecords.set(l.id, l);
8775
9006
  if (dirty.length > 0) noteLessonMutation();
9007
+ if (softDeletedIds.length > 0) await deleteFacetsForTargets(kv, softDeletedIds);
8776
9008
  await Promise.all(auditEvents.map((event) => recordAudit(kv, "lesson_strengthen", "mem::lesson-decay-sweep", [event.id], {
8777
9009
  action: event.action,
8778
9010
  actor: "system",
@@ -9021,7 +9253,8 @@ function registerExportImportFunction(sdk, state) {
9021
9253
  "0.9.28",
9022
9254
  "0.9.29",
9023
9255
  "0.9.30",
9024
- "0.9.31"
9256
+ "0.9.31",
9257
+ "0.9.32"
9025
9258
  ]).has(importData.version)) return {
9026
9259
  success: false,
9027
9260
  error: `Unsupported export version: ${importData.version}`
@@ -10399,7 +10632,7 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
10399
10632
  if (!data?.force && !isConsolidationEnabled()) return {
10400
10633
  success: false,
10401
10634
  skipped: true,
10402
- reason: "Consolidation disabled: set CONSOLIDATION_ENABLED=true or configure an LLM provider (ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY / GEMINI_API_KEY / GOOGLE_API_KEY / MINIMAX_API_KEY, or AGENTMEMORY_ALLOW_AGENT_SDK=true). OPENAI_BASE_URL alone only points an OpenAI key at another server; it is not a provider."
10635
+ reason: "Consolidation disabled: set CONSOLIDATION_ENABLED=true or choose an LLM provider (AGENTMEMORY_LLM_PROVIDER=anthropic, openai, gemini, openrouter or minimax with that provider's key, or AGENTMEMORY_ALLOW_AGENT_SDK=true). A provider key alone does not turn on LLM use."
10403
10636
  };
10404
10637
  const tier = data?.tier || "all";
10405
10638
  const decayDays = getConsolidationDecayDays();
@@ -18382,7 +18615,9 @@ function createStatusReporter(sdk, kv, deps) {
18382
18615
  llmRoute: describeLlmRoute(config.provider),
18383
18616
  functionMetrics: functionMetrics ?? [],
18384
18617
  provider: detectLlmProviderKind(),
18618
+ providerHint: llmOptInHint(),
18385
18619
  embeddingProvider: describeEmbeddingProvider(),
18620
+ embeddingHint: embeddingOptInHint(),
18386
18621
  flags: buildConfigFlags(),
18387
18622
  index: {
18388
18623
  bm25Documents: idx.size,
@@ -18527,13 +18762,18 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
18527
18762
  sdk.registerFunction("api::config-flags", async (req) => {
18528
18763
  const authErr = checkAuth(req, secret);
18529
18764
  if (authErr) return authErr;
18765
+ const providerKind = detectLlmProviderKind();
18766
+ const embeddingProvider = describeEmbeddingProvider();
18767
+ const flags = buildConfigFlags();
18530
18768
  return {
18531
18769
  status_code: 200,
18532
18770
  body: {
18533
18771
  version: VERSION,
18534
- provider: detectLlmProviderKind(),
18535
- embeddingProvider: describeEmbeddingProvider(),
18536
- flags: buildConfigFlags()
18772
+ provider: providerKind,
18773
+ providerHint: llmOptInHint(),
18774
+ embeddingProvider,
18775
+ embeddingHint: embeddingOptInHint(),
18776
+ flags
18537
18777
  }
18538
18778
  };
18539
18779
  });
@@ -22384,6 +22624,45 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
22384
22624
  http_method: "POST"
22385
22625
  }
22386
22626
  });
22627
+ sdk.registerFunction("api::lesson-get", async (req) => {
22628
+ const denied = checkAuth(req, secret);
22629
+ if (denied) return denied;
22630
+ const body = req.body;
22631
+ const lessonId = typeof body?.lessonId === "string" ? body.lessonId.trim() : "";
22632
+ if (!lessonId) return {
22633
+ status_code: 400,
22634
+ body: { error: "lessonId is required" }
22635
+ };
22636
+ if (body.project !== void 0 && typeof body.project !== "string") return {
22637
+ status_code: 400,
22638
+ body: { error: "project must be a string" }
22639
+ };
22640
+ const project = typeof body.project === "string" ? body.project.trim() : "";
22641
+ const lesson = await kv.get(KV.lessons, lessonId);
22642
+ if (!lesson || lesson.deleted) return {
22643
+ status_code: 404,
22644
+ body: { error: "lesson not found" }
22645
+ };
22646
+ if (lesson.project !== void 0 && lesson.project !== project) return {
22647
+ status_code: 404,
22648
+ body: { error: "lesson not found" }
22649
+ };
22650
+ return {
22651
+ status_code: 200,
22652
+ body: {
22653
+ success: true,
22654
+ lesson
22655
+ }
22656
+ };
22657
+ });
22658
+ sdk.registerTrigger({
22659
+ type: "http",
22660
+ function_id: "api::lesson-get",
22661
+ config: {
22662
+ api_path: "/agentmemory/lessons/get",
22663
+ http_method: "POST"
22664
+ }
22665
+ });
22387
22666
  sdk.registerFunction("api::lesson-strengthen", async (req) => {
22388
22667
  const denied = checkAuth(req, secret);
22389
22668
  if (denied) return denied;
@@ -23162,13 +23441,18 @@ function parseCsvList(value) {
23162
23441
  if (Array.isArray(value)) return value.map((v) => typeof v === "string" ? v.trim() : "").filter(Boolean);
23163
23442
  return [];
23164
23443
  }
23444
+ const SLOT_TOOL_NAMES = new Set(getAllTools().filter((t) => t.name.startsWith("memory_slot_")).map((t) => t.name));
23445
+ function isSlotTool(name) {
23446
+ return SLOT_TOOL_NAMES.has(name);
23447
+ }
23165
23448
  function registerMcpEndpoints(sdk, kv, secret) {
23449
+ const slotsEnabled = isSlotsEnabled();
23166
23450
  sdk.registerFunction("mcp::tools::list", async (req) => {
23167
23451
  const authErr = checkAuth(req, secret);
23168
23452
  if (authErr) return authErr;
23169
23453
  return {
23170
23454
  status_code: 200,
23171
- body: { tools: getVisibleTools() }
23455
+ body: { tools: getVisibleTools().filter((t) => slotsEnabled || !isSlotTool(t.name)) }
23172
23456
  };
23173
23457
  });
23174
23458
  sdk.registerTrigger({
@@ -23187,6 +23471,7 @@ function registerMcpEndpoints(sdk, kv, secret) {
23187
23471
  body: { error: "name is required" }
23188
23472
  };
23189
23473
  const { name, arguments: args = {} } = req.body;
23474
+ if (isSlotTool(name) && !slotsEnabled) return slotsDisabledResponse();
23190
23475
  try {
23191
23476
  switch (name) {
23192
23477
  case "memory_recall": {
@@ -24208,6 +24493,11 @@ function registerMcpEndpoints(sdk, kv, secret) {
24208
24493
  body: { error: "content is required" }
24209
24494
  };
24210
24495
  const lessonTags = typeof args.tags === "string" && args.tags.trim() ? args.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
24496
+ const lessonSourceIds = normalizeLessonSourceIds(args.sourceIds);
24497
+ if (!lessonSourceIds) return {
24498
+ status_code: 400,
24499
+ body: { error: `sourceIds must be an array of at most 50 ids (session, memory, observation or crystal ids) without spaces` }
24500
+ };
24211
24501
  const lessonSaveResult = await sdk.trigger({
24212
24502
  function_id: "mem::lesson-save",
24213
24503
  payload: {
@@ -24216,7 +24506,8 @@ function registerMcpEndpoints(sdk, kv, secret) {
24216
24506
  confidence: args.confidence,
24217
24507
  project: args.project,
24218
24508
  tags: lessonTags,
24219
- source: "manual"
24509
+ source: "manual",
24510
+ sourceIds: lessonSourceIds
24220
24511
  }
24221
24512
  });
24222
24513
  return {
@@ -24638,24 +24929,16 @@ function registerMcpEndpoints(sdk, kv, secret) {
24638
24929
  };
24639
24930
  }
24640
24931
  if (uri === "agentmemory://graph/stats") try {
24641
- const graphState = await pinGraphState(kv);
24642
- const nodes = await graphState.list(KV.graphNodes);
24643
- const edges = await graphState.list(KV.graphEdges);
24644
- const nodesByType = {};
24645
- for (const n of nodes) nodesByType[n.type] = (nodesByType[n.type] || 0) + 1;
24646
- const edgesByType = {};
24647
- for (const e of edges) edgesByType[e.type] = (edgesByType[e.type] || 0) + 1;
24932
+ const stats = await sdk.trigger({
24933
+ function_id: "mem::graph-stats",
24934
+ payload: {}
24935
+ });
24648
24936
  return {
24649
24937
  status_code: 200,
24650
24938
  body: { contents: [{
24651
24939
  uri,
24652
24940
  mimeType: "application/json",
24653
- text: JSON.stringify({
24654
- totalNodes: nodes.length,
24655
- totalEdges: edges.length,
24656
- nodesByType,
24657
- edgesByType
24658
- })
24941
+ text: JSON.stringify(stats)
24659
24942
  }] }
24660
24943
  };
24661
24944
  } catch {
@@ -24665,8 +24948,15 @@ function registerMcpEndpoints(sdk, kv, secret) {
24665
24948
  uri,
24666
24949
  mimeType: "application/json",
24667
24950
  text: JSON.stringify({
24668
- totalNodes: 0,
24669
- totalEdges: 0
24951
+ totalNodes: null,
24952
+ totalEdges: null,
24953
+ nodesByType: {},
24954
+ edgesByType: {},
24955
+ fromSnapshot: false,
24956
+ fromIndex: false,
24957
+ available: false,
24958
+ coverage: "unknown",
24959
+ warning: "Graph stats are unavailable. Check agentmemory graph-index status."
24670
24960
  })
24671
24961
  }] }
24672
24962
  };
@@ -24948,14 +25238,18 @@ async function main() {
24948
25238
  const config = loadConfig();
24949
25239
  const embeddingConfig = loadEmbeddingConfig();
24950
25240
  const fallbackConfig = loadFallbackConfig();
24951
- const provider = fallbackConfig.providers.length > 0 ? createFallbackProvider(config.provider, fallbackConfig) : createProvider(config.provider);
25241
+ const provider = fallbackConfig.providers.length > 0 && config.provider.provider !== "noop" ? createFallbackProvider(config.provider, fallbackConfig) : createProvider(config.provider);
24952
25242
  const embeddingProvider = createEmbeddingProvider();
24953
25243
  const imageEmbeddingProvider = createImageEmbeddingProvider();
24954
25244
  bootLog(`Starting worker v${VERSION}...`);
24955
25245
  bootLog(`Engine: ${config.engineUrl}`);
24956
25246
  bootLog(`Provider: ${config.provider.provider} (${config.provider.model})`);
24957
25247
  if (embeddingProvider) bootLog(`Embedding provider: ${embeddingProvider.name} (${embeddingProvider.dimensions} dims)`);
24958
- else bootLog(`Embedding provider: none (BM25-only mode)`);
25248
+ else {
25249
+ bootLog(`Embedding provider: none (BM25-only mode)`);
25250
+ const embeddingHint = embeddingOptInHint();
25251
+ if (embeddingHint) bootLog(embeddingHint);
25252
+ }
24959
25253
  if (imageEmbeddingProvider) bootLog(`Image embedding provider: ${imageEmbeddingProvider.name} (${imageEmbeddingProvider.dimensions} dims) — vision-search active`);
24960
25254
  bootLog(`REST API: http://localhost:${config.restPort}/agentmemory/*`);
24961
25255
  const sdk = withWriteScrubbing(registerWorker(config.engineUrl, {
@@ -24978,6 +25272,9 @@ async function main() {
24978
25272
  stateBackend = getStateBackend();
24979
25273
  } catch {}
24980
25274
  const kv = new StateKV(sdk, { backend: stateBackend });
25275
+ const stateReadiness = await waitForStateReady(sdk, { onWaiting: (error) => bootWarn(`Waiting for the state worker to register state::get and state::list (${error})`) });
25276
+ assertStateReady(stateReadiness);
25277
+ if (stateReadiness.attempts > 1) bootLog(`State worker ready after ${stateReadiness.waitedMs}ms (${stateReadiness.attempts} attempts)`);
24981
25278
  const secret = resolveWorkerSecret();
24982
25279
  const metricsStore = new MetricsStore(kv);
24983
25280
  const dedupMap = new DedupMap();
@@ -25082,7 +25379,7 @@ async function main() {
25082
25379
  const bm25Index = getSearchIndex();
25083
25380
  const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
25084
25381
  const hybridSearch = new HybridSearch(bm25Index, vectorIndex, embeddingProvider, kv, embeddingConfig.bm25Weight, embeddingConfig.vectorWeight, graphWeight);
25085
- const hybridRanker = (query, limit, targetLayer, authorizeGraph) => hybridSearch.search(query, limit, targetLayer, authorizeGraph);
25382
+ const hybridRanker = (query, limit, targetLayer, authorizeGraph, request) => hybridSearch.search(query, limit, targetLayer, authorizeGraph, request);
25086
25383
  registerSmartSearchFunction(sdk, kv, hybridRanker);
25087
25384
  setHybridRanker(hybridRanker);
25088
25385
  registerRecentSearchesSweepFunction(sdk, kv);
@@ -25180,7 +25477,7 @@ async function main() {
25180
25477
  console.warn(`[agentmemory] Failed to rebuild the BM25 index:`, err);
25181
25478
  }
25182
25479
  bootLog(`Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
25183
- bootLog(`REST API: 146 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
25480
+ bootLog(`REST API: 147 endpoints at http://localhost:${config.restPort}/agentmemory/*`);
25184
25481
  bootLog(`MCP surface (opt-in via \`npx @agentmemory/mcp\`): ${getAllTools().length} tools · 6 resources · 3 prompts`);
25185
25482
  (async () => {
25186
25483
  try {
@@ -25340,4 +25637,4 @@ main().catch(async (err) => {
25340
25637
  //#endregion
25341
25638
  export {};
25342
25639
 
25343
- //# sourceMappingURL=src-DaMtDzt_.mjs.map
25640
+ //# sourceMappingURL=src-C23nMpda.mjs.map