@agentmemory/agentmemory 0.9.20 → 0.9.21
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.
- package/.env.example +2 -0
- package/README.md +16 -5
- package/dist/.env.example +2 -0
- package/dist/cli.mjs +24 -6
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +416 -24
- package/dist/index.mjs.map +1 -1
- package/dist/{src-DPSaLB5-.mjs → src-D5arboxc.mjs} +414 -25
- package/dist/src-D5arboxc.mjs.map +1 -0
- package/dist/{standalone-DMLk7YxP.mjs → standalone-C7BgzzIN.mjs} +32 -8
- package/dist/standalone-C7BgzzIN.mjs.map +1 -0
- package/dist/standalone.d.mts.map +1 -1
- package/dist/standalone.mjs +31 -7
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-Dz8ssuMf.mjs → tools-registry-CRTWUFw9.mjs} +5 -2
- package/dist/tools-registry-CRTWUFw9.mjs.map +1 -0
- package/dist/viewer/index.html +57 -12
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.codex.json +6 -6
- package/plugin/hooks/hooks.json +12 -12
- package/plugin/opencode/README.md +229 -0
- package/plugin/opencode/agentmemory-capture.ts +662 -0
- package/plugin/opencode/commands/recall.md +19 -0
- package/plugin/opencode/commands/remember.md +19 -0
- package/plugin/opencode/plugin.json +12 -0
- package/dist/src-DPSaLB5-.mjs.map +0 -1
- package/dist/standalone-DMLk7YxP.mjs.map +0 -1
- package/dist/tools-registry-Dz8ssuMf.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as STREAM, c as jaccardSimilarity, i as KV, n as bootLog, o as fingerprintId, r as logger, s as generateId, t as VERSION } from "./cli.mjs";
|
|
2
2
|
import { a as isManagedImagePath, getImageRefCount, i as getMaxBytes, n as IMAGES_DIR, r as deleteImage, t as withKeyedLock } from "./image-refs-R3tin9MR.mjs";
|
|
3
|
-
import { _ as
|
|
3
|
+
import { _ as loadSnapshotConfig, a as getConsolidationDecayDays, c as isAutoCompressEnabled, d as isDropStaleIndexEnabled, f as isGraphExtractionEnabled, g as loadFallbackConfig, h as loadEmbeddingConfig, i as detectLlmProviderKind, l as isConsolidationEnabled, m as loadConfig, n as getVisibleTools, o as getEnvVar, p as loadClaudeBridgeConfig, r as detectEmbeddingProvider, t as getAllTools, u as isContextInjectionEnabled, v as loadTeamConfig } from "./tools-registry-CRTWUFw9.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { execFile } from "node:child_process";
|
|
6
6
|
import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -2643,20 +2643,108 @@ async function vectorIndexAddGuarded(id, sessionId, text, context) {
|
|
|
2643
2643
|
return false;
|
|
2644
2644
|
}
|
|
2645
2645
|
}
|
|
2646
|
+
async function vectorIndexAddBatchGuarded(items) {
|
|
2647
|
+
const vi = vectorIndex;
|
|
2648
|
+
const ep = currentEmbeddingProvider;
|
|
2649
|
+
if (!vi || !ep || items.length === 0) return {
|
|
2650
|
+
ok: 0,
|
|
2651
|
+
fail: 0
|
|
2652
|
+
};
|
|
2653
|
+
let embeddings;
|
|
2654
|
+
try {
|
|
2655
|
+
embeddings = await ep.embedBatch(items.map((i) => clipEmbedInput(i.text)));
|
|
2656
|
+
} catch (err) {
|
|
2657
|
+
logger.warn("vector-index add batch: embed failed — skipping batch", {
|
|
2658
|
+
batchSize: items.length,
|
|
2659
|
+
provider: ep.name,
|
|
2660
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2661
|
+
});
|
|
2662
|
+
return {
|
|
2663
|
+
ok: 0,
|
|
2664
|
+
fail: items.length
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
if (embeddings.length !== items.length) {
|
|
2668
|
+
logger.warn("vector-index add batch: provider returned wrong length — skipping batch", {
|
|
2669
|
+
batchSize: items.length,
|
|
2670
|
+
returned: embeddings.length,
|
|
2671
|
+
provider: ep.name
|
|
2672
|
+
});
|
|
2673
|
+
return {
|
|
2674
|
+
ok: 0,
|
|
2675
|
+
fail: items.length
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
let ok = 0;
|
|
2679
|
+
let fail = 0;
|
|
2680
|
+
for (let i = 0; i < items.length; i++) {
|
|
2681
|
+
const item = items[i];
|
|
2682
|
+
const embedding = embeddings[i];
|
|
2683
|
+
if (embedding.length !== ep.dimensions) {
|
|
2684
|
+
logger.warn("vector-index add batch: dimension mismatch — skipping item", {
|
|
2685
|
+
kind: item.context.kind,
|
|
2686
|
+
id: item.context.logId,
|
|
2687
|
+
provider: ep.name,
|
|
2688
|
+
expected: ep.dimensions,
|
|
2689
|
+
received: embedding.length
|
|
2690
|
+
});
|
|
2691
|
+
fail++;
|
|
2692
|
+
continue;
|
|
2693
|
+
}
|
|
2694
|
+
try {
|
|
2695
|
+
vi.add(item.id, item.sessionId, embedding);
|
|
2696
|
+
ok++;
|
|
2697
|
+
} catch (err) {
|
|
2698
|
+
logger.warn("vector-index add batch: index write failed — skipping item", {
|
|
2699
|
+
kind: item.context.kind,
|
|
2700
|
+
id: item.context.logId,
|
|
2701
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2702
|
+
});
|
|
2703
|
+
fail++;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
return {
|
|
2707
|
+
ok,
|
|
2708
|
+
fail
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
const DEFAULT_REBUILD_EMBED_BATCH = 32;
|
|
2712
|
+
function getRebuildEmbedBatchSize() {
|
|
2713
|
+
const raw = process.env.REBUILD_EMBED_BATCH_SIZE;
|
|
2714
|
+
if (!raw) return DEFAULT_REBUILD_EMBED_BATCH;
|
|
2715
|
+
const n = parseInt(raw, 10);
|
|
2716
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_REBUILD_EMBED_BATCH;
|
|
2717
|
+
}
|
|
2646
2718
|
async function rebuildIndex(kv) {
|
|
2647
2719
|
const idx = getSearchIndex();
|
|
2648
2720
|
idx.clear();
|
|
2649
2721
|
vectorIndex?.clear();
|
|
2722
|
+
const batchSize = getRebuildEmbedBatchSize();
|
|
2723
|
+
const pending = [];
|
|
2650
2724
|
let count = 0;
|
|
2725
|
+
const flush = async () => {
|
|
2726
|
+
if (pending.length === 0) return;
|
|
2727
|
+
await vectorIndexAddBatchGuarded(pending);
|
|
2728
|
+
pending.length = 0;
|
|
2729
|
+
};
|
|
2730
|
+
const enqueue = async (job) => {
|
|
2731
|
+
pending.push(job);
|
|
2732
|
+
if (pending.length >= batchSize) await flush();
|
|
2733
|
+
};
|
|
2651
2734
|
try {
|
|
2652
2735
|
const memories = await kv.list(KV.memories);
|
|
2653
2736
|
for (const memory of memories) {
|
|
2654
2737
|
if (memory.isLatest === false) continue;
|
|
2655
2738
|
if (!memory.title || !memory.content) continue;
|
|
2656
2739
|
idx.add(memoryToObservation(memory));
|
|
2657
|
-
await
|
|
2658
|
-
|
|
2659
|
-
|
|
2740
|
+
await enqueue({
|
|
2741
|
+
id: memory.id,
|
|
2742
|
+
sessionId: memory.sessionIds[0] ?? "memory",
|
|
2743
|
+
text: memory.title + " " + memory.content,
|
|
2744
|
+
context: {
|
|
2745
|
+
kind: "memory",
|
|
2746
|
+
logId: memory.id
|
|
2747
|
+
}
|
|
2660
2748
|
});
|
|
2661
2749
|
count++;
|
|
2662
2750
|
}
|
|
@@ -2664,7 +2752,10 @@ async function rebuildIndex(kv) {
|
|
|
2664
2752
|
logger.warn("rebuildIndex: failed to load memories", { error: err instanceof Error ? err.message : String(err) });
|
|
2665
2753
|
}
|
|
2666
2754
|
const sessions = await kv.list(KV.sessions);
|
|
2667
|
-
if (!sessions.length)
|
|
2755
|
+
if (!sessions.length) {
|
|
2756
|
+
await flush();
|
|
2757
|
+
return count;
|
|
2758
|
+
}
|
|
2668
2759
|
const obsPerSession = [];
|
|
2669
2760
|
const failedSessions = [];
|
|
2670
2761
|
for (let batch = 0; batch < sessions.length; batch += 10) {
|
|
@@ -2682,12 +2773,18 @@ async function rebuildIndex(kv) {
|
|
|
2682
2773
|
if (failedSessions.length > 0) logger.warn("rebuildIndex: failed to load observations for sessions", { failedSessions });
|
|
2683
2774
|
for (const observations of obsPerSession) for (const obs of observations) if (obs.title && obs.narrative) {
|
|
2684
2775
|
idx.add(obs);
|
|
2685
|
-
await
|
|
2686
|
-
|
|
2687
|
-
|
|
2776
|
+
await enqueue({
|
|
2777
|
+
id: obs.id,
|
|
2778
|
+
sessionId: obs.sessionId,
|
|
2779
|
+
text: obs.title + " " + obs.narrative,
|
|
2780
|
+
context: {
|
|
2781
|
+
kind: "observation",
|
|
2782
|
+
logId: obs.id
|
|
2783
|
+
}
|
|
2688
2784
|
});
|
|
2689
2785
|
count++;
|
|
2690
2786
|
}
|
|
2787
|
+
await flush();
|
|
2691
2788
|
return count;
|
|
2692
2789
|
}
|
|
2693
2790
|
function registerSearchFunction(sdk, kv) {
|
|
@@ -4365,9 +4462,134 @@ function buildSummaryPrompt(observations) {
|
|
|
4365
4462
|
});
|
|
4366
4463
|
return `Session observations (${observations.length} total):\n\n${lines.join("\n\n---\n\n")}`;
|
|
4367
4464
|
}
|
|
4465
|
+
const REDUCE_SYSTEM = `You are merging multiple partial summaries of the SAME coding session into one final session summary. The partials are chronological chunks of one continuous session — not separate sessions.
|
|
4466
|
+
|
|
4467
|
+
Output EXACTLY this XML format with no additional text:
|
|
4468
|
+
|
|
4469
|
+
<summary>
|
|
4470
|
+
<title>Short session title (max 100 chars)</title>
|
|
4471
|
+
<narrative>3-5 sentence narrative covering the whole session</narrative>
|
|
4472
|
+
<decisions>
|
|
4473
|
+
<decision>Key technical decision made</decision>
|
|
4474
|
+
</decisions>
|
|
4475
|
+
<files>
|
|
4476
|
+
<file>path/to/modified/file</file>
|
|
4477
|
+
</files>
|
|
4478
|
+
<concepts>
|
|
4479
|
+
<concept>key concept from session</concept>
|
|
4480
|
+
</concepts>
|
|
4481
|
+
</summary>
|
|
4482
|
+
|
|
4483
|
+
Rules:
|
|
4484
|
+
- Synthesize a single narrative that reflects the whole arc, not a chunk-by-chunk recap
|
|
4485
|
+
- Preserve every distinct decision across chunks
|
|
4486
|
+
- Union (deduplicate) all files and concepts
|
|
4487
|
+
- Title should capture the session's overall outcome`;
|
|
4488
|
+
function buildReducePrompt(partials) {
|
|
4489
|
+
const sections = partials.map((p, i) => {
|
|
4490
|
+
const decisions = p.keyDecisions.map((d) => ` - ${d}`).join("\n");
|
|
4491
|
+
const files = p.filesModified.map((f) => ` - ${f}`).join("\n");
|
|
4492
|
+
const concepts = p.concepts.join(", ");
|
|
4493
|
+
return `[Chunk ${i + 1} of ${partials.length} — obs ${p.obsRangeStart}-${p.obsRangeEnd}]
|
|
4494
|
+
Title: ${p.title}
|
|
4495
|
+
Narrative: ${p.narrative}
|
|
4496
|
+
Decisions:
|
|
4497
|
+
${decisions}
|
|
4498
|
+
Files:
|
|
4499
|
+
${files}
|
|
4500
|
+
Concepts: ${concepts}`;
|
|
4501
|
+
});
|
|
4502
|
+
return `Partial summaries (${partials.length} chunks of one session, chronological):\n\n${sections.join("\n\n---\n\n")}`;
|
|
4503
|
+
}
|
|
4368
4504
|
|
|
4369
4505
|
//#endregion
|
|
4370
4506
|
//#region src/functions/summarize.ts
|
|
4507
|
+
const CHUNK_SIZE_DEFAULT = 400;
|
|
4508
|
+
const CHUNK_CONCURRENCY_DEFAULT = 6;
|
|
4509
|
+
const MAX_SKIP_RATIO = .5;
|
|
4510
|
+
function getChunkSize() {
|
|
4511
|
+
const raw = process.env.SUMMARIZE_CHUNK_SIZE;
|
|
4512
|
+
if (!raw) return CHUNK_SIZE_DEFAULT;
|
|
4513
|
+
const n = parseInt(raw, 10);
|
|
4514
|
+
return Number.isFinite(n) && n > 0 ? n : CHUNK_SIZE_DEFAULT;
|
|
4515
|
+
}
|
|
4516
|
+
function getChunkConcurrency() {
|
|
4517
|
+
const raw = process.env.SUMMARIZE_CHUNK_CONCURRENCY;
|
|
4518
|
+
if (!raw) return CHUNK_CONCURRENCY_DEFAULT;
|
|
4519
|
+
const n = parseInt(raw, 10);
|
|
4520
|
+
return Number.isFinite(n) && n > 0 ? n : CHUNK_CONCURRENCY_DEFAULT;
|
|
4521
|
+
}
|
|
4522
|
+
async function summarizeChunkWithRetry(provider, chunk, sessionId, project, idx, total) {
|
|
4523
|
+
for (let attempt = 1; attempt <= 2; attempt++) try {
|
|
4524
|
+
const parsed = parseSummaryXml(await provider.summarize(SUMMARY_SYSTEM, buildSummaryPrompt(chunk)), sessionId, project, chunk.length);
|
|
4525
|
+
if (parsed) return parsed;
|
|
4526
|
+
logger.warn("Summarize chunk parse failed", {
|
|
4527
|
+
sessionId,
|
|
4528
|
+
chunk: `${idx + 1}/${total}`,
|
|
4529
|
+
attempt
|
|
4530
|
+
});
|
|
4531
|
+
} catch (err) {
|
|
4532
|
+
logger.warn("Summarize chunk LLM call failed", {
|
|
4533
|
+
sessionId,
|
|
4534
|
+
chunk: `${idx + 1}/${total}`,
|
|
4535
|
+
attempt,
|
|
4536
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4537
|
+
});
|
|
4538
|
+
}
|
|
4539
|
+
return null;
|
|
4540
|
+
}
|
|
4541
|
+
async function produceSummaryXml(provider, compressed, sessionId, project) {
|
|
4542
|
+
const chunkSize = getChunkSize();
|
|
4543
|
+
if (compressed.length <= chunkSize) return {
|
|
4544
|
+
response: await provider.summarize(SUMMARY_SYSTEM, buildSummaryPrompt(compressed)),
|
|
4545
|
+
mode: "single",
|
|
4546
|
+
chunks: 1
|
|
4547
|
+
};
|
|
4548
|
+
const chunks = [];
|
|
4549
|
+
for (let i = 0; i < compressed.length; i += chunkSize) chunks.push(compressed.slice(i, i + chunkSize));
|
|
4550
|
+
const concurrency = getChunkConcurrency();
|
|
4551
|
+
logger.info("Summarize chunking session", {
|
|
4552
|
+
sessionId,
|
|
4553
|
+
chunks: chunks.length,
|
|
4554
|
+
chunkSize,
|
|
4555
|
+
concurrency,
|
|
4556
|
+
totalObservations: compressed.length
|
|
4557
|
+
});
|
|
4558
|
+
const partialByIdx = new Array(chunks.length).fill(null);
|
|
4559
|
+
for (let batchStart = 0; batchStart < chunks.length; batchStart += concurrency) {
|
|
4560
|
+
const batch = chunks.slice(batchStart, batchStart + concurrency);
|
|
4561
|
+
await Promise.all(batch.map(async (chunk, j) => {
|
|
4562
|
+
const idx = batchStart + j;
|
|
4563
|
+
partialByIdx[idx] = await summarizeChunkWithRetry(provider, chunk, sessionId, project, idx, chunks.length);
|
|
4564
|
+
}));
|
|
4565
|
+
}
|
|
4566
|
+
const skipped = partialByIdx.filter((p) => p === null).length;
|
|
4567
|
+
const partials = partialByIdx.filter((p) => p !== null);
|
|
4568
|
+
if (skipped > Math.floor(chunks.length * MAX_SKIP_RATIO)) throw new Error(`too_many_chunks_skipped: ${skipped}/${chunks.length} chunks failed to parse after retry`);
|
|
4569
|
+
if (skipped > 0) logger.warn("Summarize chunks partially skipped", {
|
|
4570
|
+
sessionId,
|
|
4571
|
+
skipped,
|
|
4572
|
+
total: chunks.length
|
|
4573
|
+
});
|
|
4574
|
+
const reduceInput = partials.map((p) => {
|
|
4575
|
+
const originalIdx = partialByIdx.indexOf(p);
|
|
4576
|
+
return {
|
|
4577
|
+
title: p.title,
|
|
4578
|
+
narrative: p.narrative,
|
|
4579
|
+
keyDecisions: p.keyDecisions,
|
|
4580
|
+
filesModified: p.filesModified,
|
|
4581
|
+
concepts: p.concepts,
|
|
4582
|
+
obsRangeStart: originalIdx * chunkSize + 1,
|
|
4583
|
+
obsRangeEnd: Math.min((originalIdx + 1) * chunkSize, compressed.length)
|
|
4584
|
+
};
|
|
4585
|
+
});
|
|
4586
|
+
return {
|
|
4587
|
+
response: await provider.summarize(REDUCE_SYSTEM, buildReducePrompt(reduceInput)),
|
|
4588
|
+
mode: "chunked",
|
|
4589
|
+
chunks: chunks.length,
|
|
4590
|
+
skipped
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4371
4593
|
function parseSummaryXml(xml, sessionId, project, obsCount) {
|
|
4372
4594
|
const title = getXmlTag(xml, "title");
|
|
4373
4595
|
if (!title) return null;
|
|
@@ -4416,16 +4638,15 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
4416
4638
|
};
|
|
4417
4639
|
}
|
|
4418
4640
|
try {
|
|
4419
|
-
const
|
|
4420
|
-
const response = await provider.summarize(SUMMARY_SYSTEM, prompt);
|
|
4641
|
+
const { response, mode, chunks } = await produceSummaryXml(provider, compressed, sessionId, session.project);
|
|
4421
4642
|
if (!response || !response.trim()) {
|
|
4422
4643
|
const latencyMs = Date.now() - startMs;
|
|
4423
4644
|
if (metricsStore) await metricsStore.record("mem::summarize", latencyMs, false);
|
|
4424
4645
|
logger.warn("Empty provider response on summarize", {
|
|
4425
4646
|
sessionId,
|
|
4426
4647
|
provider: provider.name,
|
|
4427
|
-
|
|
4428
|
-
|
|
4648
|
+
mode,
|
|
4649
|
+
chunks,
|
|
4429
4650
|
observationCount: compressed.length
|
|
4430
4651
|
});
|
|
4431
4652
|
return {
|
|
@@ -5582,6 +5803,7 @@ async function findByKeyword(kv, keyword, project) {
|
|
|
5582
5803
|
|
|
5583
5804
|
//#endregion
|
|
5584
5805
|
//#region src/functions/smart-search.ts
|
|
5806
|
+
const LESSON_CONTENT_PREVIEW_CHARS = 240;
|
|
5585
5807
|
function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
5586
5808
|
sdk.registerFunction("mem::smart-search", async (data) => {
|
|
5587
5809
|
if (data.expandIds && data.expandIds.length > 0) {
|
|
@@ -5624,7 +5846,10 @@ function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
|
5624
5846
|
error: "query is required"
|
|
5625
5847
|
};
|
|
5626
5848
|
const limit = Math.max(1, Math.min(data.limit ?? 20, 100));
|
|
5627
|
-
const
|
|
5849
|
+
const lessonLimit = Math.min(limit, 10);
|
|
5850
|
+
const includeLessons = data.includeLessons !== false;
|
|
5851
|
+
const [hybridResults, lessons] = await Promise.all([searchFn(data.query, limit), includeLessons ? recallLessons(sdk, data.query, lessonLimit, data.project) : Promise.resolve([])]);
|
|
5852
|
+
const compact = hybridResults.map((r) => ({
|
|
5628
5853
|
obsId: r.observation.id,
|
|
5629
5854
|
sessionId: r.sessionId,
|
|
5630
5855
|
title: r.observation.title,
|
|
@@ -5635,14 +5860,42 @@ function registerSmartSearchFunction(sdk, kv, searchFn) {
|
|
|
5635
5860
|
recordAccessBatch(kv, compact.map((r) => r.obsId));
|
|
5636
5861
|
logger.info("Smart search compact", {
|
|
5637
5862
|
query: data.query,
|
|
5638
|
-
results: compact.length
|
|
5863
|
+
results: compact.length,
|
|
5864
|
+
lessons: lessons.length
|
|
5639
5865
|
});
|
|
5640
|
-
|
|
5866
|
+
const response = {
|
|
5641
5867
|
mode: "compact",
|
|
5642
5868
|
results: compact
|
|
5643
5869
|
};
|
|
5870
|
+
if (includeLessons) response.lessons = lessons;
|
|
5871
|
+
return response;
|
|
5644
5872
|
});
|
|
5645
5873
|
}
|
|
5874
|
+
async function recallLessons(sdk, query, limit, project) {
|
|
5875
|
+
try {
|
|
5876
|
+
const result = await sdk.trigger({
|
|
5877
|
+
function_id: "mem::lesson-recall",
|
|
5878
|
+
payload: {
|
|
5879
|
+
query,
|
|
5880
|
+
limit,
|
|
5881
|
+
project
|
|
5882
|
+
}
|
|
5883
|
+
});
|
|
5884
|
+
if (!result?.success || !Array.isArray(result.lessons)) return [];
|
|
5885
|
+
return result.lessons.map((l) => ({
|
|
5886
|
+
lessonId: l.id,
|
|
5887
|
+
content: l.content.length > LESSON_CONTENT_PREVIEW_CHARS ? l.content.slice(0, LESSON_CONTENT_PREVIEW_CHARS) + "…" : l.content,
|
|
5888
|
+
confidence: l.confidence,
|
|
5889
|
+
score: l.score ?? l.confidence,
|
|
5890
|
+
createdAt: l.createdAt,
|
|
5891
|
+
project: l.project,
|
|
5892
|
+
tags: l.tags ?? []
|
|
5893
|
+
}));
|
|
5894
|
+
} catch (err) {
|
|
5895
|
+
logger.warn("Smart search: mem::lesson-recall failed; returning empty lesson list", { error: err instanceof Error ? err.message : String(err) });
|
|
5896
|
+
return [];
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5646
5899
|
async function findObservation$1(kv, obsId, sessionIdHint) {
|
|
5647
5900
|
if (sessionIdHint) {
|
|
5648
5901
|
const obs = await kv.get(KV.observations(sessionIdHint), obsId).catch(() => null);
|
|
@@ -6002,7 +6255,8 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
6002
6255
|
"0.9.17",
|
|
6003
6256
|
"0.9.18",
|
|
6004
6257
|
"0.9.19",
|
|
6005
|
-
"0.9.20"
|
|
6258
|
+
"0.9.20",
|
|
6259
|
+
"0.9.21"
|
|
6006
6260
|
]).has(importData.version)) return {
|
|
6007
6261
|
success: false,
|
|
6008
6262
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -9704,6 +9958,12 @@ const ALL_CATEGORIES = [
|
|
|
9704
9958
|
"signals",
|
|
9705
9959
|
"sessions",
|
|
9706
9960
|
"memories",
|
|
9961
|
+
"lessons",
|
|
9962
|
+
"summaries",
|
|
9963
|
+
"semantic",
|
|
9964
|
+
"procedural",
|
|
9965
|
+
"crystals",
|
|
9966
|
+
"insights",
|
|
9707
9967
|
"mesh"
|
|
9708
9968
|
];
|
|
9709
9969
|
const TWENTY_FOUR_HOURS_MS = 1440 * 60 * 1e3;
|
|
@@ -9936,6 +10196,133 @@ function registerDiagnosticsFunction(sdk, kv) {
|
|
|
9936
10196
|
fixable: false
|
|
9937
10197
|
});
|
|
9938
10198
|
}
|
|
10199
|
+
if (categories.includes("lessons")) {
|
|
10200
|
+
const lessons = await kv.list(KV.lessons);
|
|
10201
|
+
const live = lessons.filter((l) => !l.deleted);
|
|
10202
|
+
let lessonIssues = 0;
|
|
10203
|
+
for (const l of live) if (!Number.isFinite(l.confidence) || l.confidence < 0 || l.confidence > 1) {
|
|
10204
|
+
checks.push({
|
|
10205
|
+
name: `lesson-bad-confidence:${l.id}`,
|
|
10206
|
+
category: "lessons",
|
|
10207
|
+
status: "warn",
|
|
10208
|
+
message: `Lesson ${l.id} has confidence ${l.confidence} (expected finite number in 0..1)`,
|
|
10209
|
+
fixable: false
|
|
10210
|
+
});
|
|
10211
|
+
lessonIssues++;
|
|
10212
|
+
}
|
|
10213
|
+
if (lessonIssues === 0) checks.push({
|
|
10214
|
+
name: "lessons-ok",
|
|
10215
|
+
category: "lessons",
|
|
10216
|
+
status: "pass",
|
|
10217
|
+
message: `All ${live.length} lessons are healthy (${lessons.length - live.length} tombstoned)`,
|
|
10218
|
+
fixable: false
|
|
10219
|
+
});
|
|
10220
|
+
}
|
|
10221
|
+
if (categories.includes("summaries")) {
|
|
10222
|
+
const summaries = await kv.list(KV.summaries);
|
|
10223
|
+
let summaryIssues = 0;
|
|
10224
|
+
for (const s of summaries) if (typeof s.title !== "string" || s.title.trim().length === 0) {
|
|
10225
|
+
checks.push({
|
|
10226
|
+
name: `summary-missing-title:${s.sessionId}`,
|
|
10227
|
+
category: "summaries",
|
|
10228
|
+
status: "warn",
|
|
10229
|
+
message: `Summary for session ${s.sessionId} has no title`,
|
|
10230
|
+
fixable: false
|
|
10231
|
+
});
|
|
10232
|
+
summaryIssues++;
|
|
10233
|
+
}
|
|
10234
|
+
if (summaryIssues === 0) checks.push({
|
|
10235
|
+
name: "summaries-ok",
|
|
10236
|
+
category: "summaries",
|
|
10237
|
+
status: "pass",
|
|
10238
|
+
message: `All ${summaries.length} session summaries are consistent`,
|
|
10239
|
+
fixable: false
|
|
10240
|
+
});
|
|
10241
|
+
}
|
|
10242
|
+
if (categories.includes("semantic")) {
|
|
10243
|
+
const semantic = await kv.list(KV.semantic);
|
|
10244
|
+
let semanticIssues = 0;
|
|
10245
|
+
for (const s of semantic) if (!Number.isFinite(s.confidence) || s.confidence < 0 || s.confidence > 1) {
|
|
10246
|
+
checks.push({
|
|
10247
|
+
name: `semantic-bad-confidence:${s.id}`,
|
|
10248
|
+
category: "semantic",
|
|
10249
|
+
status: "warn",
|
|
10250
|
+
message: `Semantic fact ${s.id} has confidence ${s.confidence} (expected finite number in 0..1)`,
|
|
10251
|
+
fixable: false
|
|
10252
|
+
});
|
|
10253
|
+
semanticIssues++;
|
|
10254
|
+
}
|
|
10255
|
+
if (semanticIssues === 0) checks.push({
|
|
10256
|
+
name: "semantic-ok",
|
|
10257
|
+
category: "semantic",
|
|
10258
|
+
status: "pass",
|
|
10259
|
+
message: `All ${semantic.length} semantic memories are consistent`,
|
|
10260
|
+
fixable: false
|
|
10261
|
+
});
|
|
10262
|
+
}
|
|
10263
|
+
if (categories.includes("procedural")) {
|
|
10264
|
+
const procedural = await kv.list(KV.procedural);
|
|
10265
|
+
let proceduralIssues = 0;
|
|
10266
|
+
for (const p of procedural) if (!Array.isArray(p.steps) || p.steps.length === 0) {
|
|
10267
|
+
checks.push({
|
|
10268
|
+
name: `procedural-empty-steps:${p.id}`,
|
|
10269
|
+
category: "procedural",
|
|
10270
|
+
status: "warn",
|
|
10271
|
+
message: `Procedural memory "${p.name}" (${p.id}) has no steps`,
|
|
10272
|
+
fixable: false
|
|
10273
|
+
});
|
|
10274
|
+
proceduralIssues++;
|
|
10275
|
+
}
|
|
10276
|
+
if (proceduralIssues === 0) checks.push({
|
|
10277
|
+
name: "procedural-ok",
|
|
10278
|
+
category: "procedural",
|
|
10279
|
+
status: "pass",
|
|
10280
|
+
message: `All ${procedural.length} procedural memories are consistent`,
|
|
10281
|
+
fixable: false
|
|
10282
|
+
});
|
|
10283
|
+
}
|
|
10284
|
+
if (categories.includes("crystals")) {
|
|
10285
|
+
const crystals = await kv.list(KV.crystals);
|
|
10286
|
+
let crystalIssues = 0;
|
|
10287
|
+
for (const c of crystals) if (typeof c.narrative !== "string" || c.narrative.trim().length === 0) {
|
|
10288
|
+
checks.push({
|
|
10289
|
+
name: `crystal-empty-narrative:${c.id}`,
|
|
10290
|
+
category: "crystals",
|
|
10291
|
+
status: "warn",
|
|
10292
|
+
message: `Crystal ${c.id} has empty narrative`,
|
|
10293
|
+
fixable: false
|
|
10294
|
+
});
|
|
10295
|
+
crystalIssues++;
|
|
10296
|
+
}
|
|
10297
|
+
if (crystalIssues === 0) checks.push({
|
|
10298
|
+
name: "crystals-ok",
|
|
10299
|
+
category: "crystals",
|
|
10300
|
+
status: "pass",
|
|
10301
|
+
message: `All ${crystals.length} crystals are consistent`,
|
|
10302
|
+
fixable: false
|
|
10303
|
+
});
|
|
10304
|
+
}
|
|
10305
|
+
if (categories.includes("insights")) {
|
|
10306
|
+
const insights = await kv.list(KV.insights);
|
|
10307
|
+
let insightIssues = 0;
|
|
10308
|
+
for (const i of insights) if (!Number.isFinite(i.confidence) || i.confidence < 0 || i.confidence > 1) {
|
|
10309
|
+
checks.push({
|
|
10310
|
+
name: `insight-bad-confidence:${i.id}`,
|
|
10311
|
+
category: "insights",
|
|
10312
|
+
status: "warn",
|
|
10313
|
+
message: `Insight ${i.id} has confidence ${i.confidence} (expected finite number in 0..1)`,
|
|
10314
|
+
fixable: false
|
|
10315
|
+
});
|
|
10316
|
+
insightIssues++;
|
|
10317
|
+
}
|
|
10318
|
+
if (insightIssues === 0) checks.push({
|
|
10319
|
+
name: "insights-ok",
|
|
10320
|
+
category: "insights",
|
|
10321
|
+
status: "pass",
|
|
10322
|
+
message: `All ${insights.length} insights are consistent`,
|
|
10323
|
+
fixable: false
|
|
10324
|
+
});
|
|
10325
|
+
}
|
|
9939
10326
|
if (categories.includes("mesh")) {
|
|
9940
10327
|
const peers = await kv.list(KV.mesh);
|
|
9941
10328
|
let meshIssues = 0;
|
|
@@ -13746,13 +14133,16 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
13746
14133
|
status_code: 400,
|
|
13747
14134
|
body: { error: "sessionId, project, and cwd are required non-empty strings" }
|
|
13748
14135
|
};
|
|
14136
|
+
const title = typeof body.title === "string" ? body.title.trim() : void 0;
|
|
13749
14137
|
const session = {
|
|
13750
14138
|
id: sessionId,
|
|
13751
14139
|
project,
|
|
13752
14140
|
cwd,
|
|
13753
14141
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13754
14142
|
status: "active",
|
|
13755
|
-
observationCount: 0
|
|
14143
|
+
observationCount: 0,
|
|
14144
|
+
...title ? { summary: title.slice(0, 200) } : {},
|
|
14145
|
+
...title ? { firstPrompt: title.slice(0, 200) } : {}
|
|
13756
14146
|
};
|
|
13757
14147
|
await kv.set(KV.sessions, sessionId, session);
|
|
13758
14148
|
return {
|
|
@@ -18827,23 +19217,22 @@ async function main() {
|
|
|
18827
19217
|
if (mismatches.length > 0) {
|
|
18828
19218
|
const sample = mismatches.slice(0, 5).map((m) => `${m.obsId} (dim=${m.dim})`).join(", ");
|
|
18829
19219
|
const distinct = Array.from(seenDimensions).sort((a, b) => a - b).join(", ");
|
|
18830
|
-
if (
|
|
19220
|
+
if (isDropStaleIndexEnabled()) console.warn(`[agentmemory] Persisted vector index has ${mismatches.length} of ${loaded.vector.size} vectors with the wrong dimension. Active provider (${embeddingProvider?.name}) declares ${activeDim}; dimensions seen on disk: ${distinct}. AGENTMEMORY_DROP_STALE_INDEX=true is set — discarding the persisted vectors. Live observations will rebuild the index over time.`);
|
|
18831
19221
|
else throw new Error(`[agentmemory] Refusing to start: persisted vector index has ${mismatches.length} of ${loaded.vector.size} vectors with the wrong dimension. Active provider (${embeddingProvider?.name}) declares ${activeDim}; dimensions seen on disk: ${distinct}. First mismatched obsIds: ${sample}. Loading would silently corrupt search (cross-dimension cosine returns 0). Choose one:\n - Re-embed the existing index against the new provider, then start.\n - Set AGENTMEMORY_DROP_STALE_INDEX=true to discard the persisted vectors and rebuild from live observations.\n - Switch the embedding provider back to the one that wrote the index.`);
|
|
18832
19222
|
} else {
|
|
18833
19223
|
vectorIndex.restoreFrom(loaded.vector);
|
|
18834
19224
|
bootLog(`Loaded persisted vector index (${vectorIndex.size} vectors)`);
|
|
18835
19225
|
}
|
|
18836
19226
|
}
|
|
18837
|
-
if (bm25Index.size === 0) {
|
|
18838
|
-
const indexCount = await rebuildIndex(kv).catch((err) => {
|
|
18839
|
-
console.warn(`[agentmemory] Failed to rebuild search index:`, err);
|
|
18840
|
-
return 0;
|
|
18841
|
-
});
|
|
19227
|
+
if (bm25Index.size === 0) rebuildIndex(kv).then((indexCount) => {
|
|
18842
19228
|
if (indexCount > 0) {
|
|
18843
19229
|
bootLog(`Search index rebuilt: ${indexCount} entries`);
|
|
18844
19230
|
indexPersistence.scheduleSave();
|
|
18845
19231
|
}
|
|
18846
|
-
}
|
|
19232
|
+
}).catch((err) => {
|
|
19233
|
+
console.warn(`[agentmemory] Failed to rebuild search index:`, err);
|
|
19234
|
+
});
|
|
19235
|
+
else try {
|
|
18847
19236
|
const memories = await kv.list(KV.memories);
|
|
18848
19237
|
let backfilled = 0;
|
|
18849
19238
|
for (const memory of memories) {
|
|
@@ -18940,4 +19329,4 @@ main().catch((err) => {
|
|
|
18940
19329
|
|
|
18941
19330
|
//#endregion
|
|
18942
19331
|
export { };
|
|
18943
|
-
//# sourceMappingURL=src-
|
|
19332
|
+
//# sourceMappingURL=src-D5arboxc.mjs.map
|