@agentmemory/agentmemory 0.9.11 → 0.9.13
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 +167 -0
- package/README.md +65 -1
- package/dist/.env.example +167 -0
- package/dist/cli.mjs +50 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/image-refs-HVu22rfu.mjs +116 -0
- package/dist/image-refs-HVu22rfu.mjs.map +1 -0
- package/dist/image-store-BfN1vDbj.mjs +3 -0
- package/dist/index.mjs +214 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{src-IbjZ5iJi.mjs → src-Ca9oX6Hq.mjs} +224 -125
- package/dist/src-Ca9oX6Hq.mjs.map +1 -0
- package/dist/{standalone-awLAlipf.mjs → standalone-BpbiNqr9.mjs} +2 -2
- package/dist/{standalone-awLAlipf.mjs.map → standalone-BpbiNqr9.mjs.map} +1 -1
- package/dist/standalone.mjs +1 -1
- package/dist/standalone.mjs.map +1 -1
- package/dist/{tools-registry-CzSNN8Ca.mjs → tools-registry-D5l632PP.mjs} +3 -3
- package/dist/{tools-registry-CzSNN8Ca.mjs.map → tools-registry-D5l632PP.mjs.map} +1 -1
- package/dist/viewer/index.html +52 -25
- package/package.json +8 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/dist/image-refs-D4M1sJAz.mjs +0 -3
- package/dist/image-store-DR4g1FAW.mjs +0 -3
- package/dist/src-IbjZ5iJi.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { TriggerAction, registerWorker } from "iii-sdk";
|
|
3
4
|
import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
5
|
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
|
@@ -82,7 +83,7 @@ function detectProvider(env) {
|
|
|
82
83
|
if (!hasRealValue(env["GEMINI_API_KEY"]) && hasRealValue(env["GOOGLE_API_KEY"])) process.stderr.write("[agentmemory] GOOGLE_API_KEY detected — treating as GEMINI_API_KEY. Set GEMINI_API_KEY in ~/.agentmemory/.env to silence this warning.\n");
|
|
83
84
|
return {
|
|
84
85
|
provider: "gemini",
|
|
85
|
-
model: env["GEMINI_MODEL"] || "gemini-2.
|
|
86
|
+
model: env["GEMINI_MODEL"] || "gemini-2.5-flash",
|
|
86
87
|
maxTokens
|
|
87
88
|
};
|
|
88
89
|
}
|
|
@@ -570,7 +571,8 @@ var FallbackChainProvider = class {
|
|
|
570
571
|
//#endregion
|
|
571
572
|
//#region src/providers/embedding/gemini.ts
|
|
572
573
|
const BATCH_LIMIT = 100;
|
|
573
|
-
const
|
|
574
|
+
const MODEL = "models/gemini-embedding-001";
|
|
575
|
+
const API_BASE = `https://generativelanguage.googleapis.com/v1beta/${MODEL}:batchEmbedContents`;
|
|
574
576
|
var GeminiEmbeddingProvider = class {
|
|
575
577
|
name = "gemini";
|
|
576
578
|
dimensions = 768;
|
|
@@ -591,8 +593,9 @@ var GeminiEmbeddingProvider = class {
|
|
|
591
593
|
method: "POST",
|
|
592
594
|
headers: { "Content-Type": "application/json" },
|
|
593
595
|
body: JSON.stringify({ requests: chunk.map((t) => ({
|
|
594
|
-
model:
|
|
595
|
-
content: { parts: [{ text: t }] }
|
|
596
|
+
model: MODEL,
|
|
597
|
+
content: { parts: [{ text: t }] },
|
|
598
|
+
outputDimensionality: this.dimensions
|
|
596
599
|
})) })
|
|
597
600
|
});
|
|
598
601
|
if (!response.ok) {
|
|
@@ -600,11 +603,26 @@ var GeminiEmbeddingProvider = class {
|
|
|
600
603
|
throw new Error(`Gemini embedding failed (${response.status}): ${err}`);
|
|
601
604
|
}
|
|
602
605
|
const data = await response.json();
|
|
603
|
-
for (const emb of data.embeddings) results.push(new Float32Array(emb.values));
|
|
606
|
+
for (const emb of data.embeddings) results.push(l2Normalize(new Float32Array(emb.values)));
|
|
604
607
|
}
|
|
605
608
|
return results;
|
|
606
609
|
}
|
|
607
610
|
};
|
|
611
|
+
let zeroNormWarned = false;
|
|
612
|
+
function l2Normalize(vec) {
|
|
613
|
+
let sum = 0;
|
|
614
|
+
for (let i = 0; i < vec.length; i++) sum += vec[i] * vec[i];
|
|
615
|
+
const norm = Math.sqrt(sum);
|
|
616
|
+
if (norm === 0) {
|
|
617
|
+
if (!zeroNormWarned) {
|
|
618
|
+
zeroNormWarned = true;
|
|
619
|
+
process.stderr.write(`[agentmemory] warn: gemini-embedding-001 returned a zero-norm embedding (length=${vec.length}); leaving it un-normalized. Subsequent zero-norm vectors will not be reported.\n`);
|
|
620
|
+
}
|
|
621
|
+
return vec;
|
|
622
|
+
}
|
|
623
|
+
for (let i = 0; i < vec.length; i++) vec[i] = vec[i] / norm;
|
|
624
|
+
return vec;
|
|
625
|
+
}
|
|
608
626
|
|
|
609
627
|
//#endregion
|
|
610
628
|
//#region src/providers/embedding/openai.ts
|
|
@@ -2075,6 +2093,110 @@ function getSynonyms(stemmedTerm) {
|
|
|
2075
2093
|
return syns ? [...syns] : [];
|
|
2076
2094
|
}
|
|
2077
2095
|
|
|
2096
|
+
//#endregion
|
|
2097
|
+
//#region src/state/cjk-segmenter.ts
|
|
2098
|
+
const cjkRequire = createRequire(import.meta.url);
|
|
2099
|
+
const CJK_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
2100
|
+
const KANA_RE = /[\p{Script=Hiragana}\p{Script=Katakana}]/u;
|
|
2101
|
+
const HANGUL_RE = /\p{Script=Hangul}/u;
|
|
2102
|
+
const CJK_RUN_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
|
|
2103
|
+
const HANGUL_BLOCK_RE = /[가-]+/g;
|
|
2104
|
+
const hintShown = /* @__PURE__ */ new Set();
|
|
2105
|
+
function hasCjk(text) {
|
|
2106
|
+
return CJK_RE.test(text);
|
|
2107
|
+
}
|
|
2108
|
+
function showHintOnce(key, message) {
|
|
2109
|
+
if (hintShown.has(key)) return;
|
|
2110
|
+
hintShown.add(key);
|
|
2111
|
+
if (typeof process !== "undefined" && process.stderr?.write) process.stderr.write(`agentmemory: ${message}\n`);
|
|
2112
|
+
}
|
|
2113
|
+
let jiebaInstance = null;
|
|
2114
|
+
let jiebaLoaded = false;
|
|
2115
|
+
function getJieba() {
|
|
2116
|
+
if (jiebaLoaded) return jiebaInstance;
|
|
2117
|
+
jiebaLoaded = true;
|
|
2118
|
+
try {
|
|
2119
|
+
const mod = cjkRequire("@node-rs/jieba");
|
|
2120
|
+
try {
|
|
2121
|
+
const dictMod = cjkRequire("@node-rs/jieba/dict");
|
|
2122
|
+
jiebaInstance = mod.Jieba.withDict(dictMod.dict);
|
|
2123
|
+
} catch {
|
|
2124
|
+
jiebaInstance = new mod.Jieba();
|
|
2125
|
+
}
|
|
2126
|
+
return jiebaInstance;
|
|
2127
|
+
} catch {
|
|
2128
|
+
showHintOnce("jieba", "install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization");
|
|
2129
|
+
return null;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
let jaSegmenterInstance = null;
|
|
2133
|
+
let jaSegmenterLoaded = false;
|
|
2134
|
+
function getJaSegmenter() {
|
|
2135
|
+
if (jaSegmenterLoaded) return jaSegmenterInstance;
|
|
2136
|
+
jaSegmenterLoaded = true;
|
|
2137
|
+
try {
|
|
2138
|
+
jaSegmenterInstance = new (cjkRequire("tiny-segmenter"))();
|
|
2139
|
+
return jaSegmenterInstance;
|
|
2140
|
+
} catch {
|
|
2141
|
+
showHintOnce("tiny-segmenter", "install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization");
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function cleanTokens(tokens) {
|
|
2146
|
+
const out = [];
|
|
2147
|
+
for (const t of tokens) {
|
|
2148
|
+
const trimmed = t.trim();
|
|
2149
|
+
if (trimmed) out.push(trimmed);
|
|
2150
|
+
}
|
|
2151
|
+
return out;
|
|
2152
|
+
}
|
|
2153
|
+
function segmentHan(text) {
|
|
2154
|
+
const j = getJieba();
|
|
2155
|
+
if (!j) return [text];
|
|
2156
|
+
try {
|
|
2157
|
+
return cleanTokens(j.cut(text, true));
|
|
2158
|
+
} catch {
|
|
2159
|
+
return [text];
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
function segmentKana(text) {
|
|
2163
|
+
const s = getJaSegmenter();
|
|
2164
|
+
if (!s) return [text];
|
|
2165
|
+
try {
|
|
2166
|
+
return cleanTokens(s.segment(text));
|
|
2167
|
+
} catch {
|
|
2168
|
+
return [text];
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
function segmentHangul(text) {
|
|
2172
|
+
const out = [];
|
|
2173
|
+
for (const m of text.matchAll(HANGUL_BLOCK_RE)) if (m[0]) out.push(m[0]);
|
|
2174
|
+
return out;
|
|
2175
|
+
}
|
|
2176
|
+
function segmentCjk(text) {
|
|
2177
|
+
if (!hasCjk(text)) return [text];
|
|
2178
|
+
const out = [];
|
|
2179
|
+
let cursor = 0;
|
|
2180
|
+
for (const m of text.matchAll(CJK_RUN_RE)) {
|
|
2181
|
+
const start = m.index ?? 0;
|
|
2182
|
+
const run = m[0];
|
|
2183
|
+
const end = start + run.length;
|
|
2184
|
+
if (start > cursor) {
|
|
2185
|
+
const piece = text.slice(cursor, start).trim();
|
|
2186
|
+
if (piece) out.push(piece);
|
|
2187
|
+
}
|
|
2188
|
+
if (HANGUL_RE.test(run)) out.push(...segmentHangul(run));
|
|
2189
|
+
else if (KANA_RE.test(run)) out.push(...segmentKana(run));
|
|
2190
|
+
else out.push(...segmentHan(run));
|
|
2191
|
+
cursor = end;
|
|
2192
|
+
}
|
|
2193
|
+
if (cursor < text.length) {
|
|
2194
|
+
const trailing = text.slice(cursor).trim();
|
|
2195
|
+
if (trailing) out.push(trailing);
|
|
2196
|
+
}
|
|
2197
|
+
return out;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2078
2200
|
//#endregion
|
|
2079
2201
|
//#region src/state/search-index.ts
|
|
2080
2202
|
var SearchIndex = class SearchIndex {
|
|
@@ -2231,7 +2353,15 @@ var SearchIndex = class SearchIndex {
|
|
|
2231
2353
|
return this.tokenize(parts.join(" ").toLowerCase());
|
|
2232
2354
|
}
|
|
2233
2355
|
tokenize(text) {
|
|
2234
|
-
|
|
2356
|
+
const cleaned = text.replace(/[^\p{L}\p{N}\s/.\\-_]/gu, " ");
|
|
2357
|
+
const out = [];
|
|
2358
|
+
for (const raw of cleaned.split(/\s+/)) {
|
|
2359
|
+
if (raw.length < 2) continue;
|
|
2360
|
+
if (hasCjk(raw)) {
|
|
2361
|
+
for (const seg of segmentCjk(raw)) if (seg.length >= 1) out.push(seg);
|
|
2362
|
+
} else out.push(stem(raw));
|
|
2363
|
+
}
|
|
2364
|
+
return out;
|
|
2235
2365
|
}
|
|
2236
2366
|
getSortedTerms() {
|
|
2237
2367
|
if (!this.sortedTerms) this.sortedTerms = Array.from(this.invertedIndex.keys()).sort();
|
|
@@ -2530,13 +2660,55 @@ async function deleteAccessLog(kv, memoryId) {
|
|
|
2530
2660
|
//#endregion
|
|
2531
2661
|
//#region src/functions/search.ts
|
|
2532
2662
|
let index = null;
|
|
2663
|
+
let vectorIndex = null;
|
|
2664
|
+
let currentEmbeddingProvider = null;
|
|
2533
2665
|
function getSearchIndex() {
|
|
2534
2666
|
if (!index) index = new SearchIndex();
|
|
2535
2667
|
return index;
|
|
2536
2668
|
}
|
|
2669
|
+
function setVectorIndex(idx) {
|
|
2670
|
+
vectorIndex = idx;
|
|
2671
|
+
}
|
|
2672
|
+
function setEmbeddingProvider(provider) {
|
|
2673
|
+
currentEmbeddingProvider = provider;
|
|
2674
|
+
}
|
|
2675
|
+
const EMBED_MAX_CHARS = 16e3;
|
|
2676
|
+
function clipEmbedInput(text) {
|
|
2677
|
+
if (text.length <= EMBED_MAX_CHARS) return text;
|
|
2678
|
+
return text.slice(0, EMBED_MAX_CHARS);
|
|
2679
|
+
}
|
|
2680
|
+
async function vectorIndexAddGuarded(id, sessionId, text, context) {
|
|
2681
|
+
const vi = vectorIndex;
|
|
2682
|
+
const ep = currentEmbeddingProvider;
|
|
2683
|
+
if (!vi || !ep) return false;
|
|
2684
|
+
try {
|
|
2685
|
+
const embedding = await ep.embed(clipEmbedInput(text));
|
|
2686
|
+
if (embedding.length !== ep.dimensions) {
|
|
2687
|
+
logger.warn("vector-index add: dimension mismatch — skipping", {
|
|
2688
|
+
kind: context.kind,
|
|
2689
|
+
id: context.logId,
|
|
2690
|
+
provider: ep.name,
|
|
2691
|
+
expected: ep.dimensions,
|
|
2692
|
+
received: embedding.length
|
|
2693
|
+
});
|
|
2694
|
+
return false;
|
|
2695
|
+
}
|
|
2696
|
+
vi.add(id, sessionId, embedding);
|
|
2697
|
+
return true;
|
|
2698
|
+
} catch (err) {
|
|
2699
|
+
logger.warn("vector-index add: embed failed — skipping", {
|
|
2700
|
+
kind: context.kind,
|
|
2701
|
+
id: context.logId,
|
|
2702
|
+
provider: ep.name,
|
|
2703
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2704
|
+
});
|
|
2705
|
+
return false;
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2537
2708
|
async function rebuildIndex(kv) {
|
|
2538
2709
|
const idx = getSearchIndex();
|
|
2539
2710
|
idx.clear();
|
|
2711
|
+
vectorIndex?.clear();
|
|
2540
2712
|
let count = 0;
|
|
2541
2713
|
try {
|
|
2542
2714
|
const memories = await kv.list(KV.memories);
|
|
@@ -2544,6 +2716,10 @@ async function rebuildIndex(kv) {
|
|
|
2544
2716
|
if (memory.isLatest === false) continue;
|
|
2545
2717
|
if (!memory.title || !memory.content) continue;
|
|
2546
2718
|
idx.add(memoryToObservation(memory));
|
|
2719
|
+
await vectorIndexAddGuarded(memory.id, memory.sessionIds[0] ?? "memory", memory.title + " " + memory.content, {
|
|
2720
|
+
kind: "memory",
|
|
2721
|
+
logId: memory.id
|
|
2722
|
+
});
|
|
2547
2723
|
count++;
|
|
2548
2724
|
}
|
|
2549
2725
|
} catch (err) {
|
|
@@ -2568,6 +2744,10 @@ async function rebuildIndex(kv) {
|
|
|
2568
2744
|
if (failedSessions.length > 0) logger.warn("rebuildIndex: failed to load observations for sessions", { failedSessions });
|
|
2569
2745
|
for (const observations of obsPerSession) for (const obs of observations) if (obs.title && obs.narrative) {
|
|
2570
2746
|
idx.add(obs);
|
|
2747
|
+
await vectorIndexAddGuarded(obs.id, obs.sessionId, obs.title + " " + obs.narrative, {
|
|
2748
|
+
kind: "observation",
|
|
2749
|
+
logId: obs.id
|
|
2750
|
+
});
|
|
2571
2751
|
count++;
|
|
2572
2752
|
}
|
|
2573
2753
|
return count;
|
|
@@ -2874,6 +3054,10 @@ function registerObserveFunction(sdk, kv, dedupMap, maxObservationsPerSession) {
|
|
|
2874
3054
|
const synthetic = buildSyntheticCompression(raw);
|
|
2875
3055
|
await kv.set(KV.observations(payload.sessionId), obsId, synthetic);
|
|
2876
3056
|
getSearchIndex().add(synthetic);
|
|
3057
|
+
await vectorIndexAddGuarded(synthetic.id, synthetic.sessionId, synthetic.title + " " + (synthetic.narrative || ""), {
|
|
3058
|
+
kind: "synthetic",
|
|
3059
|
+
logId: synthetic.id
|
|
3060
|
+
});
|
|
2877
3061
|
await sdk.trigger({
|
|
2878
3062
|
function_id: "stream::set",
|
|
2879
3063
|
payload: {
|
|
@@ -4118,7 +4302,20 @@ function registerCompressFunction(sdk, kv, provider, metricsStore) {
|
|
|
4118
4302
|
...data.raw.imageData ? { imageRef: data.raw.imageData } : {}
|
|
4119
4303
|
};
|
|
4120
4304
|
await kv.set(KV.observations(data.sessionId), data.observationId, compressed);
|
|
4121
|
-
|
|
4305
|
+
try {
|
|
4306
|
+
getSearchIndex().add(compressed);
|
|
4307
|
+
} catch (err) {
|
|
4308
|
+
logger.warn("Failed to index compressed observation into BM25", {
|
|
4309
|
+
obsId: compressed.id,
|
|
4310
|
+
sessionId: compressed.sessionId,
|
|
4311
|
+
title: compressed.title,
|
|
4312
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
4315
|
+
await vectorIndexAddGuarded(compressed.id, compressed.sessionId, compressed.title + " " + (compressed.narrative || ""), {
|
|
4316
|
+
kind: "observation",
|
|
4317
|
+
logId: compressed.id
|
|
4318
|
+
});
|
|
4122
4319
|
const streamResults = await Promise.allSettled([sdk.trigger({
|
|
4123
4320
|
function_id: "stream::set",
|
|
4124
4321
|
payload: {
|
|
@@ -4958,6 +5155,10 @@ function registerRememberFunction(sdk, kv) {
|
|
|
4958
5155
|
error: err instanceof Error ? err.message : String(err)
|
|
4959
5156
|
});
|
|
4960
5157
|
}
|
|
5158
|
+
await vectorIndexAddGuarded(memory.id, memory.sessionIds[0] ?? "memory", memory.title + " " + memory.content, {
|
|
5159
|
+
kind: "memory",
|
|
5160
|
+
logId: memory.id
|
|
5161
|
+
});
|
|
4961
5162
|
if (supersededId) await sdk.trigger({
|
|
4962
5163
|
function_id: "mem::cascade-update",
|
|
4963
5164
|
payload: { supersededMemoryId: supersededId },
|
|
@@ -5755,7 +5956,7 @@ function registerAutoForgetFunction(sdk, kv) {
|
|
|
5755
5956
|
|
|
5756
5957
|
//#endregion
|
|
5757
5958
|
//#region src/version.ts
|
|
5758
|
-
const VERSION = "0.9.
|
|
5959
|
+
const VERSION = "0.9.13";
|
|
5759
5960
|
|
|
5760
5961
|
//#endregion
|
|
5761
5962
|
//#region src/functions/export-import.ts
|
|
@@ -5884,7 +6085,9 @@ function registerExportImportFunction(sdk, kv) {
|
|
|
5884
6085
|
"0.9.8",
|
|
5885
6086
|
"0.9.9",
|
|
5886
6087
|
"0.9.10",
|
|
5887
|
-
"0.9.11"
|
|
6088
|
+
"0.9.11",
|
|
6089
|
+
"0.9.12",
|
|
6090
|
+
"0.9.13"
|
|
5888
6091
|
]).has(importData.version)) return {
|
|
5889
6092
|
success: false,
|
|
5890
6093
|
error: `Unsupported export version: ${importData.version}`
|
|
@@ -19451,6 +19654,8 @@ async function main() {
|
|
|
19451
19654
|
const metricsStore = new MetricsStore(kv);
|
|
19452
19655
|
const dedupMap = new DedupMap();
|
|
19453
19656
|
const vectorIndex = embeddingProvider ? new VectorIndex() : null;
|
|
19657
|
+
setVectorIndex(vectorIndex);
|
|
19658
|
+
setEmbeddingProvider(embeddingProvider);
|
|
19454
19659
|
initMetrics(hasGetMeter(sdk) ? sdk.getMeter.bind(sdk) : void 0);
|
|
19455
19660
|
registerPrivacyFunction(sdk);
|
|
19456
19661
|
registerObserveFunction(sdk, kv, dedupMap, config.maxObservationsPerSession);
|