ziiagentmemory 0.1.0 → 0.2.0
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/dist/cli.mjs +6 -6
- package/dist/{image-refs-C2YU7BIm.mjs → image-refs-Cc7-ua5j.mjs} +2 -2
- package/dist/{image-refs-C2YU7BIm.mjs.map → image-refs-Cc7-ua5j.mjs.map} +1 -1
- package/dist/index.mjs +178 -110
- package/dist/index.mjs.map +1 -1
- package/dist/schema-BQLY6d8C.mjs +206 -0
- package/dist/schema-BQLY6d8C.mjs.map +1 -0
- package/dist/{src-Cr3pH_uH.mjs → src-Dl4Re_-n.mjs} +58 -118
- package/dist/src-Dl4Re_-n.mjs.map +1 -0
- package/dist/{standalone-C6KzNkt5.mjs → standalone-CSnfnb1m.mjs} +2 -2
- package/dist/{standalone-C6KzNkt5.mjs.map → standalone-CSnfnb1m.mjs.map} +1 -1
- package/dist/standalone.mjs +2 -0
- package/dist/standalone.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/schema-CNiCWzV9.mjs +0 -78
- package/dist/schema-CNiCWzV9.mjs.map +0 -1
- package/dist/src-Cr3pH_uH.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as runAdapter, i as resolveAdapter, r as knownAgents, t as ADAPTERS } from "./connect-JnsJXc68.mjs";
|
|
3
|
-
import { i as generateId } from "./schema-
|
|
3
|
+
import { i as generateId } from "./schema-BQLY6d8C.mjs";
|
|
4
4
|
import { r as setBootVerbose } from "./logger-BEB7pCI9.mjs";
|
|
5
5
|
import { i as VERSION, n as getAllTools, t as ESSENTIAL_TOOLS } from "./tools-registry-DCt2KAWA.mjs";
|
|
6
6
|
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -1552,7 +1552,7 @@ async function main() {
|
|
|
1552
1552
|
if (firstRun || IS_RESET) await runOnboarding();
|
|
1553
1553
|
if (skipEngine) {
|
|
1554
1554
|
if (IS_VERBOSE) p.log.info("Skipping engine check (--no-engine)");
|
|
1555
|
-
await import("./src-
|
|
1555
|
+
await import("./src-Dl4Re_-n.mjs");
|
|
1556
1556
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
1557
1557
|
const consoleState = await ensureIiiConsole();
|
|
1558
1558
|
await maybeOfferGlobalInstall();
|
|
@@ -1567,7 +1567,7 @@ async function main() {
|
|
|
1567
1567
|
const detected = attachedBin ? iiiBinVersion(attachedBin) : null;
|
|
1568
1568
|
if (detected === IIPINNED_VERSION) {
|
|
1569
1569
|
adoptRunningEngine();
|
|
1570
|
-
await import("./src-
|
|
1570
|
+
await import("./src-Dl4Re_-n.mjs");
|
|
1571
1571
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
1572
1572
|
const consoleState = await ensureIiiConsole();
|
|
1573
1573
|
await maybeOfferGlobalInstall();
|
|
@@ -1638,7 +1638,7 @@ async function main() {
|
|
|
1638
1638
|
process.exit(1);
|
|
1639
1639
|
}
|
|
1640
1640
|
s.stop(c.ok("iii-engine is ready"));
|
|
1641
|
-
await import("./src-
|
|
1641
|
+
await import("./src-Dl4Re_-n.mjs");
|
|
1642
1642
|
if (await waitForAgentmemoryReady(15e3)) {
|
|
1643
1643
|
const consoleState = await ensureIiiConsole();
|
|
1644
1644
|
await maybeOfferGlobalInstall();
|
|
@@ -2286,7 +2286,7 @@ async function startServerForDemo() {
|
|
|
2286
2286
|
process.exit(1);
|
|
2287
2287
|
}
|
|
2288
2288
|
}
|
|
2289
|
-
await import("./src-
|
|
2289
|
+
await import("./src-Dl4Re_-n.mjs");
|
|
2290
2290
|
if (!await waitForAgentmemoryReady(15e3)) {
|
|
2291
2291
|
p.log.error("agentmemory worker did not become ready within 15s.");
|
|
2292
2292
|
process.exit(1);
|
|
@@ -2619,7 +2619,7 @@ async function runStop() {
|
|
|
2619
2619
|
p.outro("Stopped. Memories persisted to disk; restart anytime with: npx @agentmemory/agentmemory");
|
|
2620
2620
|
}
|
|
2621
2621
|
async function runMcp() {
|
|
2622
|
-
await import("./standalone-
|
|
2622
|
+
await import("./standalone-CSnfnb1m.mjs");
|
|
2623
2623
|
}
|
|
2624
2624
|
async function runConnectCmd() {
|
|
2625
2625
|
const { runConnect } = await import("./connect-JnsJXc68.mjs").then((n) => n.n);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as KV } from "./schema-
|
|
1
|
+
import { t as KV } from "./schema-BQLY6d8C.mjs";
|
|
2
2
|
import { r as deleteImage, s as touchImage, t as withKeyedLock } from "./keyed-mutex-DTHtcZrt.mjs";
|
|
3
3
|
import { TriggerAction } from "iii-sdk";
|
|
4
4
|
//#region src/functions/image-refs.ts
|
|
@@ -31,4 +31,4 @@ async function decrementImageRef(kv, sdk, filePath) {
|
|
|
31
31
|
//#endregion
|
|
32
32
|
export { decrementImageRef, getImageRefCount, incrementImageRef };
|
|
33
33
|
|
|
34
|
-
//# sourceMappingURL=image-refs-
|
|
34
|
+
//# sourceMappingURL=image-refs-Cc7-ua5j.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-refs-
|
|
1
|
+
{"version":3,"file":"image-refs-Cc7-ua5j.mjs","names":[],"sources":["../src/functions/image-refs.ts"],"sourcesContent":["import { TriggerAction, type ISdk } from \"iii-sdk\";\r\nimport { KV } from \"../state/schema.js\";\r\nimport { StateKV } from \"../state/kv.js\";\r\nimport { deleteImage, touchImage } from \"../utils/image-store.js\";\r\nimport { withKeyedLock } from \"../state/keyed-mutex.js\";\r\n\r\nexport async function getImageRefCount(kv: StateKV, filePath: string): Promise<number> {\r\n const count = await kv.get<number>(KV.imageRefs, filePath);\r\n return count ? Number(count) : 0;\r\n}\r\n\r\nexport async function incrementImageRef(kv: StateKV, filePath: string): Promise<void> {\r\n return withKeyedLock(`imgRef:${filePath}`, async () => {\r\n const current = await getImageRefCount(kv, filePath);\r\n await kv.set(KV.imageRefs, filePath, current + 1);\r\n await touchImage(filePath);\r\n });\r\n}\r\n\r\nexport async function decrementImageRef(kv: StateKV, sdk: ISdk, filePath: string): Promise<void> {\r\n return withKeyedLock(`imgRef:${filePath}`, async () => {\r\n const current = await getImageRefCount(kv, filePath);\r\n if (current <= 1) {\r\n await kv.delete(KV.imageEmbeddings, filePath);\r\n await kv.delete(KV.imageRefs, filePath);\r\n const { deletedBytes } = await deleteImage(filePath);\r\n if (deletedBytes > 0) {\r\n sdk.trigger({\r\n function_id: \"mem::disk-size-delta\",\r\n payload: { deltaBytes: -deletedBytes },\r\n action: TriggerAction.Void(),\r\n });\r\n }\r\n } else {\r\n await kv.set(KV.imageRefs, filePath, current - 1);\r\n }\r\n });\r\n}\r\n"],"mappings":";;;;AAMA,eAAsB,iBAAiB,IAAa,UAAmC;CACrF,MAAM,QAAQ,MAAM,GAAG,IAAY,GAAG,WAAW,SAAS;AAC1D,QAAO,QAAQ,OAAO,MAAM,GAAG;;AAGjC,eAAsB,kBAAkB,IAAa,UAAiC;AACpF,QAAO,cAAc,UAAU,YAAY,YAAY;EACrD,MAAM,UAAU,MAAM,iBAAiB,IAAI,SAAS;AACpD,QAAM,GAAG,IAAI,GAAG,WAAW,UAAU,UAAU,EAAE;AACjD,QAAM,WAAW,SAAS;GAC1B;;AAGJ,eAAsB,kBAAkB,IAAa,KAAW,UAAiC;AAC/F,QAAO,cAAc,UAAU,YAAY,YAAY;EACrD,MAAM,UAAU,MAAM,iBAAiB,IAAI,SAAS;AACpD,MAAI,WAAW,GAAG;AAChB,SAAM,GAAG,OAAO,GAAG,iBAAiB,SAAS;AAC7C,SAAM,GAAG,OAAO,GAAG,WAAW,SAAS;GACvC,MAAM,EAAE,iBAAiB,MAAM,YAAY,SAAS;AACpD,OAAI,eAAe,EACjB,KAAI,QAAQ;IACV,aAAa;IACb,SAAS,EAAE,YAAY,CAAC,cAAc;IACtC,QAAQ,cAAc,MAAM;IAC7B,CAAC;QAGJ,OAAM,GAAG,IAAI,GAAG,WAAW,UAAU,UAAU,EAAE;GAEnD"}
|
package/dist/index.mjs
CHANGED
|
@@ -1301,6 +1301,109 @@ var StateKV = class {
|
|
|
1301
1301
|
}
|
|
1302
1302
|
};
|
|
1303
1303
|
//#endregion
|
|
1304
|
+
//#region src/state/cjk-segmenter.ts
|
|
1305
|
+
const cjkRequire = createRequire(import.meta.url);
|
|
1306
|
+
const CJK_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
1307
|
+
const KANA_RE = /[\p{Script=Hiragana}\p{Script=Katakana}]/u;
|
|
1308
|
+
const HANGUL_RE = /\p{Script=Hangul}/u;
|
|
1309
|
+
const CJK_RUN_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
|
|
1310
|
+
const HANGUL_BLOCK_RE = /[가-]+/g;
|
|
1311
|
+
const hintShown = /* @__PURE__ */ new Set();
|
|
1312
|
+
function hasCjk(text) {
|
|
1313
|
+
return CJK_RE.test(text);
|
|
1314
|
+
}
|
|
1315
|
+
function showHintOnce(key, message) {
|
|
1316
|
+
if (hintShown.has(key)) return;
|
|
1317
|
+
hintShown.add(key);
|
|
1318
|
+
if (typeof process !== "undefined" && process.stderr?.write) process.stderr.write(`agentmemory: ${message}\n`);
|
|
1319
|
+
}
|
|
1320
|
+
let jiebaInstance = null;
|
|
1321
|
+
let jiebaLoaded = false;
|
|
1322
|
+
function getJieba() {
|
|
1323
|
+
if (jiebaLoaded) return jiebaInstance;
|
|
1324
|
+
jiebaLoaded = true;
|
|
1325
|
+
try {
|
|
1326
|
+
const mod = cjkRequire("@node-rs/jieba");
|
|
1327
|
+
try {
|
|
1328
|
+
const dictMod = cjkRequire("@node-rs/jieba/dict");
|
|
1329
|
+
jiebaInstance = mod.Jieba.withDict(dictMod.dict);
|
|
1330
|
+
} catch {
|
|
1331
|
+
jiebaInstance = new mod.Jieba();
|
|
1332
|
+
}
|
|
1333
|
+
return jiebaInstance;
|
|
1334
|
+
} catch {
|
|
1335
|
+
showHintOnce("jieba", "install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization");
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
let jaSegmenterInstance = null;
|
|
1340
|
+
let jaSegmenterLoaded = false;
|
|
1341
|
+
function getJaSegmenter() {
|
|
1342
|
+
if (jaSegmenterLoaded) return jaSegmenterInstance;
|
|
1343
|
+
jaSegmenterLoaded = true;
|
|
1344
|
+
try {
|
|
1345
|
+
jaSegmenterInstance = new (cjkRequire("tiny-segmenter"))();
|
|
1346
|
+
return jaSegmenterInstance;
|
|
1347
|
+
} catch {
|
|
1348
|
+
showHintOnce("tiny-segmenter", "install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization");
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
function cleanTokens(tokens) {
|
|
1353
|
+
const out = [];
|
|
1354
|
+
for (const t of tokens) {
|
|
1355
|
+
const trimmed = t.trim();
|
|
1356
|
+
if (trimmed) out.push(trimmed);
|
|
1357
|
+
}
|
|
1358
|
+
return out;
|
|
1359
|
+
}
|
|
1360
|
+
function segmentHan(text) {
|
|
1361
|
+
const j = getJieba();
|
|
1362
|
+
if (!j) return [text];
|
|
1363
|
+
try {
|
|
1364
|
+
return cleanTokens(j.cut(text, true));
|
|
1365
|
+
} catch {
|
|
1366
|
+
return [text];
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function segmentKana(text) {
|
|
1370
|
+
const s = getJaSegmenter();
|
|
1371
|
+
if (!s) return [text];
|
|
1372
|
+
try {
|
|
1373
|
+
return cleanTokens(s.segment(text));
|
|
1374
|
+
} catch {
|
|
1375
|
+
return [text];
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
function segmentHangul(text) {
|
|
1379
|
+
const out = [];
|
|
1380
|
+
for (const m of text.matchAll(HANGUL_BLOCK_RE)) if (m[0]) out.push(m[0]);
|
|
1381
|
+
return out;
|
|
1382
|
+
}
|
|
1383
|
+
function segmentCjk(text) {
|
|
1384
|
+
if (!hasCjk(text)) return [text];
|
|
1385
|
+
const out = [];
|
|
1386
|
+
let cursor = 0;
|
|
1387
|
+
for (const m of text.matchAll(CJK_RUN_RE)) {
|
|
1388
|
+
const start = m.index ?? 0;
|
|
1389
|
+
const run = m[0];
|
|
1390
|
+
const end = start + run.length;
|
|
1391
|
+
if (start > cursor) {
|
|
1392
|
+
const piece = text.slice(cursor, start).trim();
|
|
1393
|
+
if (piece) out.push(piece);
|
|
1394
|
+
}
|
|
1395
|
+
if (HANGUL_RE.test(run)) out.push(...segmentHangul(run));
|
|
1396
|
+
else if (KANA_RE.test(run)) out.push(...segmentKana(run));
|
|
1397
|
+
else out.push(...segmentHan(run));
|
|
1398
|
+
cursor = end;
|
|
1399
|
+
}
|
|
1400
|
+
if (cursor < text.length) {
|
|
1401
|
+
const trailing = text.slice(cursor).trim();
|
|
1402
|
+
if (trailing) out.push(trailing);
|
|
1403
|
+
}
|
|
1404
|
+
return out;
|
|
1405
|
+
}
|
|
1406
|
+
//#endregion
|
|
1304
1407
|
//#region src/state/schema.ts
|
|
1305
1408
|
const KV = {
|
|
1306
1409
|
sessions: "mem:sessions",
|
|
@@ -1365,9 +1468,33 @@ function generateId(prefix) {
|
|
|
1365
1468
|
function fingerprintId(prefix, content) {
|
|
1366
1469
|
return `${prefix}_${createHash("sha256").update(content).digest("hex").slice(0, 16)}`;
|
|
1367
1470
|
}
|
|
1471
|
+
/**
|
|
1472
|
+
* CJK-aware tokenizer for Jaccard similarity.
|
|
1473
|
+
*
|
|
1474
|
+
* For English/whitespace-delimited text we keep the original behaviour:
|
|
1475
|
+
* split on whitespace, filter tokens longer than 2 characters.
|
|
1476
|
+
*
|
|
1477
|
+
* For CJK text (Chinese/Japanese/Korean) whitespace splitting produces
|
|
1478
|
+
* near-disjoint sets because CJK text is typically written without spaces.
|
|
1479
|
+
* We reuse the existing `segmentCjk` function and additionally emit
|
|
1480
|
+
* character bigrams from CJK runs so that no-space Chinese text still
|
|
1481
|
+
* produces useful overlap.
|
|
1482
|
+
*/
|
|
1483
|
+
function tokenizeForJaccard(text) {
|
|
1484
|
+
if (!hasCjk(text)) return new Set(text.split(/\s+/).filter((t) => t.length > 2));
|
|
1485
|
+
const segments = segmentCjk(text);
|
|
1486
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
1487
|
+
for (const seg of segments) {
|
|
1488
|
+
const cleaned = seg.replace(/[^\p{L}\p{N}]/gu, "").toLowerCase();
|
|
1489
|
+
if (cleaned.length > 2) tokens.add(cleaned);
|
|
1490
|
+
const cjkChars = seg.match(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu);
|
|
1491
|
+
if (cjkChars && cjkChars.length >= 2) for (let i = 0; i < cjkChars.length - 1; i++) tokens.add(cjkChars[i] + cjkChars[i + 1]);
|
|
1492
|
+
}
|
|
1493
|
+
return tokens;
|
|
1494
|
+
}
|
|
1368
1495
|
function jaccardSimilarity(a, b) {
|
|
1369
|
-
const setA =
|
|
1370
|
-
const setB =
|
|
1496
|
+
const setA = tokenizeForJaccard(a);
|
|
1497
|
+
const setB = tokenizeForJaccard(b);
|
|
1371
1498
|
if (setA.size === 0 && setB.size === 0) return 1;
|
|
1372
1499
|
if (setA.size === 0 || setB.size === 0) return 0;
|
|
1373
1500
|
let intersection = 0;
|
|
@@ -2429,109 +2556,6 @@ function getSynonyms(stemmedTerm) {
|
|
|
2429
2556
|
return syns ? [...syns] : [];
|
|
2430
2557
|
}
|
|
2431
2558
|
//#endregion
|
|
2432
|
-
//#region src/state/cjk-segmenter.ts
|
|
2433
|
-
const cjkRequire = createRequire(import.meta.url);
|
|
2434
|
-
const CJK_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
2435
|
-
const KANA_RE = /[\p{Script=Hiragana}\p{Script=Katakana}]/u;
|
|
2436
|
-
const HANGUL_RE = /\p{Script=Hangul}/u;
|
|
2437
|
-
const CJK_RUN_RE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu;
|
|
2438
|
-
const HANGUL_BLOCK_RE = /[가-]+/g;
|
|
2439
|
-
const hintShown = /* @__PURE__ */ new Set();
|
|
2440
|
-
function hasCjk(text) {
|
|
2441
|
-
return CJK_RE.test(text);
|
|
2442
|
-
}
|
|
2443
|
-
function showHintOnce(key, message) {
|
|
2444
|
-
if (hintShown.has(key)) return;
|
|
2445
|
-
hintShown.add(key);
|
|
2446
|
-
if (typeof process !== "undefined" && process.stderr?.write) process.stderr.write(`agentmemory: ${message}\n`);
|
|
2447
|
-
}
|
|
2448
|
-
let jiebaInstance = null;
|
|
2449
|
-
let jiebaLoaded = false;
|
|
2450
|
-
function getJieba() {
|
|
2451
|
-
if (jiebaLoaded) return jiebaInstance;
|
|
2452
|
-
jiebaLoaded = true;
|
|
2453
|
-
try {
|
|
2454
|
-
const mod = cjkRequire("@node-rs/jieba");
|
|
2455
|
-
try {
|
|
2456
|
-
const dictMod = cjkRequire("@node-rs/jieba/dict");
|
|
2457
|
-
jiebaInstance = mod.Jieba.withDict(dictMod.dict);
|
|
2458
|
-
} catch {
|
|
2459
|
-
jiebaInstance = new mod.Jieba();
|
|
2460
|
-
}
|
|
2461
|
-
return jiebaInstance;
|
|
2462
|
-
} catch {
|
|
2463
|
-
showHintOnce("jieba", "install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization");
|
|
2464
|
-
return null;
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
let jaSegmenterInstance = null;
|
|
2468
|
-
let jaSegmenterLoaded = false;
|
|
2469
|
-
function getJaSegmenter() {
|
|
2470
|
-
if (jaSegmenterLoaded) return jaSegmenterInstance;
|
|
2471
|
-
jaSegmenterLoaded = true;
|
|
2472
|
-
try {
|
|
2473
|
-
jaSegmenterInstance = new (cjkRequire("tiny-segmenter"))();
|
|
2474
|
-
return jaSegmenterInstance;
|
|
2475
|
-
} catch {
|
|
2476
|
-
showHintOnce("tiny-segmenter", "install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization");
|
|
2477
|
-
return null;
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
function cleanTokens(tokens) {
|
|
2481
|
-
const out = [];
|
|
2482
|
-
for (const t of tokens) {
|
|
2483
|
-
const trimmed = t.trim();
|
|
2484
|
-
if (trimmed) out.push(trimmed);
|
|
2485
|
-
}
|
|
2486
|
-
return out;
|
|
2487
|
-
}
|
|
2488
|
-
function segmentHan(text) {
|
|
2489
|
-
const j = getJieba();
|
|
2490
|
-
if (!j) return [text];
|
|
2491
|
-
try {
|
|
2492
|
-
return cleanTokens(j.cut(text, true));
|
|
2493
|
-
} catch {
|
|
2494
|
-
return [text];
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
function segmentKana(text) {
|
|
2498
|
-
const s = getJaSegmenter();
|
|
2499
|
-
if (!s) return [text];
|
|
2500
|
-
try {
|
|
2501
|
-
return cleanTokens(s.segment(text));
|
|
2502
|
-
} catch {
|
|
2503
|
-
return [text];
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
function segmentHangul(text) {
|
|
2507
|
-
const out = [];
|
|
2508
|
-
for (const m of text.matchAll(HANGUL_BLOCK_RE)) if (m[0]) out.push(m[0]);
|
|
2509
|
-
return out;
|
|
2510
|
-
}
|
|
2511
|
-
function segmentCjk(text) {
|
|
2512
|
-
if (!hasCjk(text)) return [text];
|
|
2513
|
-
const out = [];
|
|
2514
|
-
let cursor = 0;
|
|
2515
|
-
for (const m of text.matchAll(CJK_RUN_RE)) {
|
|
2516
|
-
const start = m.index ?? 0;
|
|
2517
|
-
const run = m[0];
|
|
2518
|
-
const end = start + run.length;
|
|
2519
|
-
if (start > cursor) {
|
|
2520
|
-
const piece = text.slice(cursor, start).trim();
|
|
2521
|
-
if (piece) out.push(piece);
|
|
2522
|
-
}
|
|
2523
|
-
if (HANGUL_RE.test(run)) out.push(...segmentHangul(run));
|
|
2524
|
-
else if (KANA_RE.test(run)) out.push(...segmentKana(run));
|
|
2525
|
-
else out.push(...segmentHan(run));
|
|
2526
|
-
cursor = end;
|
|
2527
|
-
}
|
|
2528
|
-
if (cursor < text.length) {
|
|
2529
|
-
const trailing = text.slice(cursor).trim();
|
|
2530
|
-
if (trailing) out.push(trailing);
|
|
2531
|
-
}
|
|
2532
|
-
return out;
|
|
2533
|
-
}
|
|
2534
|
-
//#endregion
|
|
2535
2559
|
//#region src/state/search-index.ts
|
|
2536
2560
|
var SearchIndex = class SearchIndex {
|
|
2537
2561
|
entries = /* @__PURE__ */ new Map();
|
|
@@ -5382,7 +5406,7 @@ function registerSummarizeFunction(sdk, kv, provider, metricsStore) {
|
|
|
5382
5406
|
error: "no_observations"
|
|
5383
5407
|
};
|
|
5384
5408
|
}
|
|
5385
|
-
if (provider.name === "noop") {
|
|
5409
|
+
if (provider.name === "noop" || provider.name === "resilient(noop)") {
|
|
5386
5410
|
logger.info("Summarize skipped — no LLM provider configured", { sessionId });
|
|
5387
5411
|
return {
|
|
5388
5412
|
success: false,
|
|
@@ -5993,6 +6017,34 @@ function registerPatternsFunction(sdk, kv) {
|
|
|
5993
6017
|
});
|
|
5994
6018
|
}
|
|
5995
6019
|
//#endregion
|
|
6020
|
+
//#region src/state/memory-dedup.ts
|
|
6021
|
+
/**
|
|
6022
|
+
* Negation guard for memory dedup.
|
|
6023
|
+
*
|
|
6024
|
+
* Prevents "do X" from superseding "do not X" (and vice versa) even when
|
|
6025
|
+
* the two strings share enough tokens to exceed the Jaccard threshold.
|
|
6026
|
+
*
|
|
6027
|
+
* The guard is deliberately narrow and conservative:
|
|
6028
|
+
* - Only triggers when one side has a negation marker and the other does not.
|
|
6029
|
+
* - Supports English ("not", "never", "don't", "do not") and CJK
|
|
6030
|
+
* ("不要", "别", "無", "なし", "않") markers.
|
|
6031
|
+
* - If BOTH sides have negation markers, they are treated as compatible
|
|
6032
|
+
* (both say "don't do X") and the guard does not fire.
|
|
6033
|
+
*/
|
|
6034
|
+
const EN_NEGATION = /\b(not|never|don't|do not|cannot|should not|must not|no)\b/i;
|
|
6035
|
+
const CJK_NEGATION = /不要|别|勿|無|无|不|なし|않|안|못/i;
|
|
6036
|
+
function hasNegation(text) {
|
|
6037
|
+
return EN_NEGATION.test(text) || CJK_NEGATION.test(text);
|
|
6038
|
+
}
|
|
6039
|
+
/**
|
|
6040
|
+
* Returns true if the two texts appear to be in a negation conflict
|
|
6041
|
+
* (one says do X, the other says do NOT do X) and should NOT be superseded.
|
|
6042
|
+
*/
|
|
6043
|
+
function isNegationConflict(a, b) {
|
|
6044
|
+
if (hasNegation(a) === hasNegation(b)) return false;
|
|
6045
|
+
return true;
|
|
6046
|
+
}
|
|
6047
|
+
//#endregion
|
|
5996
6048
|
//#region src/functions/remember.ts
|
|
5997
6049
|
function registerRememberFunction(sdk, kv) {
|
|
5998
6050
|
sdk.registerFunction("mem::remember", async (data) => {
|
|
@@ -6031,7 +6083,7 @@ function registerRememberFunction(sdk, kv) {
|
|
|
6031
6083
|
for (const existing of existingMemories) {
|
|
6032
6084
|
if (existing.isLatest === false) continue;
|
|
6033
6085
|
if (project && existing.project && existing.project !== project) continue;
|
|
6034
|
-
if (jaccardSimilarity(lowerContent, existing.content.toLowerCase()) > .7) {
|
|
6086
|
+
if (jaccardSimilarity(lowerContent, existing.content.toLowerCase()) > .7 && !isNegationConflict(lowerContent, existing.content.toLowerCase())) {
|
|
6035
6087
|
supersededId = existing.id;
|
|
6036
6088
|
supersededVersion = existing.version ?? 1;
|
|
6037
6089
|
supersededMemory = existing;
|
|
@@ -15457,6 +15509,10 @@ function parseOptionalInt(raw) {
|
|
|
15457
15509
|
const n = typeof raw === "number" ? raw : parseInt(String(raw), 10);
|
|
15458
15510
|
return Number.isFinite(n) ? n : void 0;
|
|
15459
15511
|
}
|
|
15512
|
+
function parsePositiveLimit(raw) {
|
|
15513
|
+
const n = parseOptionalInt(raw);
|
|
15514
|
+
return n !== void 0 && n > 0 ? n : void 0;
|
|
15515
|
+
}
|
|
15460
15516
|
function checkAuth(req, secret) {
|
|
15461
15517
|
if (!secret) return null;
|
|
15462
15518
|
const auth = req.headers?.["authorization"] || req.headers?.["Authorization"];
|
|
@@ -15845,11 +15901,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
15845
15901
|
if (authErr) return authErr;
|
|
15846
15902
|
const sessions = await kv.list(KV.sessions);
|
|
15847
15903
|
sessions.sort((a, b) => (b.startedAt || "").localeCompare(a.startedAt || ""));
|
|
15904
|
+
const limit = parsePositiveLimit(req.query_params?.["limit"]);
|
|
15848
15905
|
return {
|
|
15849
15906
|
status_code: 200,
|
|
15850
15907
|
body: {
|
|
15851
15908
|
success: true,
|
|
15852
|
-
sessions
|
|
15909
|
+
sessions: limit !== void 0 ? sessions.slice(0, limit) : sessions
|
|
15853
15910
|
}
|
|
15854
15911
|
};
|
|
15855
15912
|
});
|
|
@@ -16121,10 +16178,12 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
|
|
|
16121
16178
|
const wildcardAgent = normalizedAgentId === "*";
|
|
16122
16179
|
const filterAgentId = wildcardAgent ? void 0 : (normalizedAgentId && !wildcardAgent ? normalizedAgentId : void 0) ?? (isAgentScopeIsolated() ? getAgentId() : void 0);
|
|
16123
16180
|
const filtered = filterAgentId ? sessions.filter((s) => s.agentId === filterAgentId) : sessions;
|
|
16124
|
-
const
|
|
16181
|
+
const limit = parsePositiveLimit(req.query_params?.["limit"]);
|
|
16182
|
+
const sliced = limit !== void 0 ? filtered.slice(0, limit) : filtered;
|
|
16183
|
+
const summaries = await Promise.all(sliced.map((s) => kv.get(KV.summaries, s.id).catch(() => null)));
|
|
16125
16184
|
return {
|
|
16126
16185
|
status_code: 200,
|
|
16127
|
-
body: { sessions:
|
|
16186
|
+
body: { sessions: sliced.map((s, i) => summaries[i] ? {
|
|
16128
16187
|
...s,
|
|
16129
16188
|
summary: summaries[i]
|
|
16130
16189
|
} : s) }
|
|
@@ -22171,6 +22230,15 @@ async function main() {
|
|
|
22171
22230
|
if (snapshotConfig.enabled) {
|
|
22172
22231
|
registerSnapshotFunction(sdk, kv, snapshotConfig.dir);
|
|
22173
22232
|
bootLog(`Git snapshots: ${snapshotConfig.dir} (every ${snapshotConfig.interval}s)`);
|
|
22233
|
+
const snapshotIntervalMs = snapshotConfig.interval * 1e3;
|
|
22234
|
+
setInterval(async () => {
|
|
22235
|
+
try {
|
|
22236
|
+
await sdk.trigger({
|
|
22237
|
+
function_id: "mem::snapshot-create",
|
|
22238
|
+
payload: {}
|
|
22239
|
+
});
|
|
22240
|
+
} catch {}
|
|
22241
|
+
}, snapshotIntervalMs).unref();
|
|
22174
22242
|
}
|
|
22175
22243
|
const bm25Index = getSearchIndex();
|
|
22176
22244
|
const graphWeight = parseFloat(getEnvVar("AGENTMEMORY_GRAPH_WEIGHT") || "0.3");
|