@desplega.ai/agent-swarm 1.103.0 → 1.104.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/README.md +2 -0
- package/dist/{actions-7txktrdj.js → actions-2expzt47.js} +6 -6
- package/dist/{app-xm3xsdye.js → app-dr8hwx7z.js} +3 -3
- package/dist/{assistant-qpv63wk6.js → assistant-9rhgsw7v.js} +9 -10
- package/dist/{boot-reembed-chxhrwv8.js → boot-reembed-3q1aywnz.js} +5 -4
- package/dist/{boot-reembed-9cxqka3e.js → boot-reembed-9kt97g1z.js} +4 -3
- package/dist/{boot-scrub-logs-8qtfkcz8.js → boot-scrub-logs-vskq108e.js} +2 -2
- package/dist/{cli-fedn86nz.js → cli-0jw6j755.js} +8 -3
- package/dist/{cli-yeemwevj.js → cli-23zk7xqy.js} +20 -16
- package/dist/{cli-v9283nma.js → cli-2g9jf9ek.js} +1 -1
- package/dist/{cli-rvae030h.js → cli-2hap19dv.js} +3 -3
- package/dist/{cli-wjyyv518.js → cli-3j01d33e.js} +9 -14
- package/dist/{cli-8hyv6c2v.js → cli-54h5daha.js} +41 -23
- package/dist/{cli-fygaw191.js → cli-5pf0b84v.js} +2 -2
- package/dist/{cli-m8f6qzck.js → cli-7v4y3hyd.js} +1 -1
- package/dist/{cli-fwxdt7kt.js → cli-d4z1csms.js} +30 -5
- package/dist/{cli-jt9d9fjw.js → cli-e7bwjntj.js} +1 -1
- package/dist/{cli-s14sb64w.js → cli-etvgz5x8.js} +5 -7
- package/dist/{cli-2qynerth.js → cli-gj2n2ec1.js} +1 -1
- package/dist/{cli-dz3d6zjg.js → cli-nzc0h54v.js} +121 -2
- package/dist/{cli-8w95v19d.js → cli-p4cj4vhc.js} +351 -24
- package/dist/{cli-xmtsan9k.js → cli-q5tshxd6.js} +1 -1
- package/dist/{http-zts4haq7.js → cli-qvzgncd8.js} +83966 -76422
- package/dist/{cli-4fnp2tc3.js → cli-s42f54ja.js} +1 -1
- package/dist/{cli-krvxq8bc.js → cli-t9v14cnd.js} +23 -4
- package/dist/{cli-gzepjz54.js → cli-v1syqkcb.js} +900 -352
- package/dist/{cli-ksq38x4n.js → cli-w1nfskkg.js} +2 -2
- package/dist/{cli-ns0r7mkr.js → cli-y9p3e94h.js} +3 -3
- package/dist/{cli-85pf4z38.js → cli-yjt8a36k.js} +3 -3
- package/dist/{cli-yfwwjeft.js → cli-yvektv79.js} +1 -1
- package/dist/{cli-kk51f3tf.js → cli-zaxksbgj.js} +5 -5
- package/dist/{cli-rttgde5f.js → cli-zghef0em.js} +1 -1
- package/dist/cli.js +10 -10
- package/dist/{commands-aqn63mcw.js → commands-wfrkef57.js} +2 -2
- package/dist/{db-azrvwsnj.js → db-c42pxt7h.js} +2 -2
- package/dist/{handlers-hf61har3.js → handlers-n36zkfvx.js} +10 -12
- package/dist/{hook-306p3yz4.js → hook-gw3vhv6s.js} +7 -5
- package/dist/http-59ygpn2h.js +17263 -0
- package/dist/{index-h2yqjyk0.js → index-881c1baf.js} +9 -8
- package/dist/{index-29vg51x6.js → index-jca7jdpt.js} +23 -10
- package/dist/{index-bfh9hgek.js → index-xf6fp4y1.js} +5 -5
- package/dist/{index-3c651yfk.js → index-xz8vaa5c.js} +14 -11
- package/dist/{keepalive-a0dg5pds.js → keepalive-mj4302vw.js} +4 -4
- package/dist/{lead-7mcxedkc.js → lead-qm0xjvkv.js} +19 -19
- package/dist/{maintenance-mrzqd53t.js → maintenance-xppxbe4t.js} +5 -4
- package/dist/{onboard-4dkpv9wa.js → onboard-ejx6yxh8.js} +2 -2
- package/dist/{otel-impl-jt7gpyp9.js → otel-impl-zs5exkpk.js} +1 -1
- package/dist/{pricing-refresh-xqnzh8dt.js → pricing-refresh-nqyf8bkb.js} +4 -4
- package/dist/{seed-pricing-56tjm1fj.js → seed-pricing-w3x550cj.js} +3 -3
- package/dist/{setup-tbwk8j10.js → setup-7729ktfj.js} +2 -2
- package/dist/{worker-e03za92t.js → worker-4ttemwhw.js} +19 -19
- package/openapi.json +90 -4
- package/package.json +3 -3
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/providers/sqlite-store.ts +468 -71
- package/src/be/memory/raters/retrieval.ts +4 -2
- package/src/be/memory/reranker.ts +5 -2
- package/src/be/memory/retrieval-store.ts +2 -0
- package/src/be/memory/types.ts +46 -0
- package/src/be/migrations/099_memory_structured_key_versioning.sql +41 -0
- package/src/be/migrations/100_memory_retrieval_source.sql +8 -0
- package/src/be/migrations/101_script_connections.sql +67 -0
- package/src/be/script-connections.ts +744 -0
- package/src/be/script-credential-broker.ts +38 -0
- package/src/be/scripts/typecheck.ts +23 -2
- package/src/be/seed-scripts/catalog/boot-triage.inline.ts +16 -4
- package/src/be/seed-scripts/catalog/boot-triage.ts +16 -4
- package/src/commands/runner.ts +8 -6
- package/src/heartbeat/heartbeat.ts +42 -1
- package/src/heartbeat/index.ts +7 -1
- package/src/http/memory.ts +127 -14
- package/src/http/scripts.ts +15 -14
- package/src/scripts-runtime/api-client.ts +100 -0
- package/src/scripts-runtime/api-types.ts +45 -0
- package/src/scripts-runtime/credential-broker/README.md +40 -0
- package/src/scripts-runtime/credential-broker/broker.ts +51 -0
- package/src/scripts-runtime/credential-broker/default-bindings.ts +19 -0
- package/src/scripts-runtime/credential-broker/fetch-patch.ts +102 -0
- package/src/scripts-runtime/credential-broker/index.ts +17 -0
- package/src/scripts-runtime/credential-broker/store.ts +72 -0
- package/src/scripts-runtime/credential-broker/types.ts +54 -0
- package/src/scripts-runtime/ctx.ts +11 -1
- package/src/scripts-runtime/egress-secrets.ts +14 -76
- package/src/scripts-runtime/eval-harness.ts +1 -1
- package/src/scripts-runtime/executors/types.ts +5 -5
- package/src/scripts-runtime/loader.ts +6 -1
- package/src/scripts-runtime/sdk-allowlist.ts +1 -0
- package/src/scripts-runtime/types/stdlib.d.ts +11 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +11 -0
- package/src/server.ts +6 -0
- package/src/tests/credential-broker.test.ts +281 -0
- package/src/tests/heartbeat.test.ts +239 -0
- package/src/tests/memory-edit.test.ts +177 -0
- package/src/tests/memory-hybrid.test.ts +212 -0
- package/src/tests/runner-repo-autostash.test.ts +39 -8
- package/src/tests/script-connections.test.ts +360 -0
- package/src/tests/scripts-http.test.ts +16 -10
- package/src/tests/scripts-runtime-secret-egress.test.ts +26 -5
- package/src/tests/swarm-config-reserved-keys.test.ts +23 -0
- package/src/tests/update-schedule-mcp-tool.test.ts +83 -4
- package/src/tools/credential-bindings/index.ts +1 -0
- package/src/tools/credential-bindings/tool.ts +238 -0
- package/src/tools/memory-edit.ts +148 -0
- package/src/tools/memory-search.ts +17 -10
- package/src/tools/schedules/delete-schedule.ts +18 -8
- package/src/tools/schedules/update-schedule.ts +4 -9
- package/src/tools/script-connections/index.ts +1 -0
- package/src/tools/script-connections/tool.ts +167 -0
- package/src/tools/swarm-config/delete-config.ts +5 -0
- package/src/tools/swarm-config/set-config.ts +23 -1
- package/src/tools/tool-config.ts +6 -1
- package/src/types.ts +6 -0
- package/src/workflows/executors/swarm-script.ts +2 -0
- package/templates/community/dora-metrics/PLAYBOOK.md +294 -0
- package/templates/community/dora-metrics/README.md +39 -0
- package/templates/community/dora-metrics/lead-prompt.md +56 -0
- package/templates/community/dora-metrics/report.mjs +393 -0
- package/templates/community/dora-metrics/run.sh +113 -0
- package/templates/schedules/weekly-dora-metrics/config.json +13 -0
- package/templates/schedules/weekly-dora-metrics/content.md +61 -0
- package/dist/cli-2307phk1.js +0 -24223
- package/dist/cli-2deg0cen.js +0 -120
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
contentSha256,
|
|
3
|
+
init_profile_sync
|
|
4
|
+
} from "./cli-3j01d33e.js";
|
|
1
5
|
import {
|
|
2
6
|
getDb,
|
|
3
7
|
init_db,
|
|
4
8
|
isSqliteVecAvailable
|
|
5
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-0jw6j755.js";
|
|
6
10
|
import {
|
|
7
11
|
__esm,
|
|
8
12
|
__export,
|
|
@@ -17,6 +21,10 @@ function numEnv(key, fallback) {
|
|
|
17
21
|
const parsed = Number(val);
|
|
18
22
|
return Number.isNaN(parsed) ? fallback : parsed;
|
|
19
23
|
}
|
|
24
|
+
function isHybridSearchEnabled() {
|
|
25
|
+
const val = process.env.MEMORY_HYBRID_SEARCH ?? "0";
|
|
26
|
+
return val === "1" || val.toLowerCase() === "true";
|
|
27
|
+
}
|
|
20
28
|
var TTL_DEFAULTS, GLOBAL_HALF_LIFE_OVERRIDE, GLOBAL_HALF_LIFE, RECENCY_DECAY_HALF_LIFE, RECENCY_DECAY_HALF_LIFE_DAYS, SOURCE_QUALITY_MULTIPLIER, MIN_SIMILARITY, ACCESS_BOOST_MAX_MULTIPLIER, ACCESS_BOOST_RECENCY_WINDOW_HOURS, CANDIDATE_SET_MULTIPLIER, EMBEDDING_DIMENSIONS, DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small", PROTECTED_SOURCES;
|
|
21
29
|
var init_constants = __esm(() => {
|
|
22
30
|
TTL_DEFAULTS = {
|
|
@@ -6529,9 +6537,70 @@ function deserializeEmbedding(buffer) {
|
|
|
6529
6537
|
}
|
|
6530
6538
|
var init_embedding = () => {};
|
|
6531
6539
|
|
|
6540
|
+
// src/be/memory/reranker.ts
|
|
6541
|
+
function recencyDecay(createdAt, now, source) {
|
|
6542
|
+
const halfLife = source ? RECENCY_DECAY_HALF_LIFE[source] : RECENCY_DECAY_HALF_LIFE_DAYS;
|
|
6543
|
+
if (!Number.isFinite(halfLife))
|
|
6544
|
+
return 1;
|
|
6545
|
+
const ageDays = (now.getTime() - new Date(createdAt).getTime()) / MS_PER_DAY;
|
|
6546
|
+
if (ageDays <= 0)
|
|
6547
|
+
return 1;
|
|
6548
|
+
return 2 ** (-ageDays / halfLife);
|
|
6549
|
+
}
|
|
6550
|
+
function accessBoost(accessedAt, accessCount, now) {
|
|
6551
|
+
if (accessCount <= 0)
|
|
6552
|
+
return 1;
|
|
6553
|
+
const hoursSinceAccess = (now.getTime() - new Date(accessedAt).getTime()) / MS_PER_HOUR;
|
|
6554
|
+
const recencyFactor = hoursSinceAccess <= ACCESS_BOOST_RECENCY_WINDOW_HOURS ? 1 : 0.5;
|
|
6555
|
+
const boost = 1 + Math.min(accessCount / 10, ACCESS_BOOST_MAX_MULTIPLIER - 1) * recencyFactor;
|
|
6556
|
+
return boost;
|
|
6557
|
+
}
|
|
6558
|
+
function sourceQuality(source) {
|
|
6559
|
+
return SOURCE_QUALITY_MULTIPLIER[source] ?? 1;
|
|
6560
|
+
}
|
|
6561
|
+
function readDemotionFloor() {
|
|
6562
|
+
const raw = process.env.MEMORY_DEMOTION_FLOOR;
|
|
6563
|
+
const n = raw == null || raw === "" ? 1 : Number(raw);
|
|
6564
|
+
return Number.isFinite(n) ? n : 1;
|
|
6565
|
+
}
|
|
6566
|
+
function usefulness(alpha, beta) {
|
|
6567
|
+
const denom = alpha + beta;
|
|
6568
|
+
if (denom <= 0)
|
|
6569
|
+
return 1;
|
|
6570
|
+
const mean = alpha / denom;
|
|
6571
|
+
return Math.max(readDemotionFloor(), Math.min(2, 2 * mean));
|
|
6572
|
+
}
|
|
6573
|
+
function computeScore(candidate, now) {
|
|
6574
|
+
const decay = candidate.recencyDecayApplied ? 1 : recencyDecay(candidate.createdAt, now, candidate.source);
|
|
6575
|
+
return candidate.similarity * decay * accessBoost(candidate.accessedAt, candidate.accessCount, now) * sourceQuality(candidate.source) * usefulness(candidate.alpha, candidate.beta);
|
|
6576
|
+
}
|
|
6577
|
+
function rerank(candidates, options) {
|
|
6578
|
+
const { limit: limit2, now = new Date } = options;
|
|
6579
|
+
const scored = candidates.map((candidate) => {
|
|
6580
|
+
const rawSimilarity = candidate.rawSimilarity ?? candidate.similarity;
|
|
6581
|
+
const compositeScore = computeScore(candidate, now);
|
|
6582
|
+
return {
|
|
6583
|
+
...candidate,
|
|
6584
|
+
rawSimilarity,
|
|
6585
|
+
compositeScore,
|
|
6586
|
+
similarity: compositeScore
|
|
6587
|
+
};
|
|
6588
|
+
});
|
|
6589
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
6590
|
+
return scored.slice(0, limit2);
|
|
6591
|
+
}
|
|
6592
|
+
var MS_PER_DAY, MS_PER_HOUR;
|
|
6593
|
+
var init_reranker = __esm(() => {
|
|
6594
|
+
init_constants();
|
|
6595
|
+
MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
6596
|
+
MS_PER_HOUR = 1000 * 60 * 60;
|
|
6597
|
+
});
|
|
6598
|
+
|
|
6532
6599
|
// src/be/memory/providers/sqlite-store.ts
|
|
6533
6600
|
var exports_sqlite_store = {};
|
|
6534
6601
|
__export(exports_sqlite_store, {
|
|
6602
|
+
computeRrfScore: () => computeRrfScore,
|
|
6603
|
+
applyEditMode: () => applyEditMode,
|
|
6535
6604
|
SqliteMemoryStore: () => SqliteMemoryStore
|
|
6536
6605
|
});
|
|
6537
6606
|
function rowToAgentMemory(row) {
|
|
@@ -6539,6 +6608,7 @@ function rowToAgentMemory(row) {
|
|
|
6539
6608
|
id: row.id,
|
|
6540
6609
|
agentId: row.agentId,
|
|
6541
6610
|
scope: row.scope,
|
|
6611
|
+
key: row.key ?? null,
|
|
6542
6612
|
name: row.name,
|
|
6543
6613
|
content: row.content,
|
|
6544
6614
|
summary: row.summary,
|
|
@@ -6549,10 +6619,13 @@ function rowToAgentMemory(row) {
|
|
|
6549
6619
|
totalChunks: row.totalChunks,
|
|
6550
6620
|
tags: JSON.parse(row.tags || "[]"),
|
|
6551
6621
|
createdAt: row.createdAt,
|
|
6622
|
+
updatedAt: row.updatedAt ?? null,
|
|
6552
6623
|
accessedAt: row.accessedAt,
|
|
6553
6624
|
expiresAt: row.expiresAt ?? null,
|
|
6554
6625
|
accessCount: row.accessCount ?? 0,
|
|
6555
|
-
embeddingModel: row.embeddingModel ?? null
|
|
6626
|
+
embeddingModel: row.embeddingModel ?? null,
|
|
6627
|
+
contentHash: row.contentHash ?? null,
|
|
6628
|
+
version: row.version ?? 1
|
|
6556
6629
|
};
|
|
6557
6630
|
}
|
|
6558
6631
|
function rowToCandidate(row, similarity) {
|
|
@@ -6566,6 +6639,35 @@ function rowToCandidate(row, similarity) {
|
|
|
6566
6639
|
beta: row.beta ?? 1
|
|
6567
6640
|
};
|
|
6568
6641
|
}
|
|
6642
|
+
function retrievalSourceFor(sources) {
|
|
6643
|
+
if (sources.has("fts") && sources.has("vec"))
|
|
6644
|
+
return "hybrid";
|
|
6645
|
+
if (sources.has("fts"))
|
|
6646
|
+
return "fts";
|
|
6647
|
+
if (sources.has("vec"))
|
|
6648
|
+
return "vec";
|
|
6649
|
+
return "fallback";
|
|
6650
|
+
}
|
|
6651
|
+
function computeRrfScore(rank, decayFactor, k = 60) {
|
|
6652
|
+
return 1 / (k + rank + 1) * decayFactor;
|
|
6653
|
+
}
|
|
6654
|
+
function applyEditMode(mode, currentContent, fields) {
|
|
6655
|
+
if (mode === "replace") {
|
|
6656
|
+
if (fields.content == null)
|
|
6657
|
+
throw new Error("replace mode requires content");
|
|
6658
|
+
return fields.content;
|
|
6659
|
+
}
|
|
6660
|
+
if (!fields.oldString || fields.newString == null) {
|
|
6661
|
+
throw new Error("exact mode requires oldString and newString");
|
|
6662
|
+
}
|
|
6663
|
+
const first = currentContent.indexOf(fields.oldString);
|
|
6664
|
+
if (first === -1)
|
|
6665
|
+
throw new Error("oldString not found");
|
|
6666
|
+
if (currentContent.indexOf(fields.oldString, first + fields.oldString.length) !== -1) {
|
|
6667
|
+
throw new Error("oldString is ambiguous");
|
|
6668
|
+
}
|
|
6669
|
+
return currentContent.slice(0, first) + fields.newString + currentContent.slice(first + fields.oldString.length);
|
|
6670
|
+
}
|
|
6569
6671
|
function computeExpiresAt(source) {
|
|
6570
6672
|
const ttlDays = TTL_DEFAULTS[source];
|
|
6571
6673
|
if (ttlDays == null)
|
|
@@ -6575,9 +6677,69 @@ function computeExpiresAt(source) {
|
|
|
6575
6677
|
|
|
6576
6678
|
class SqliteMemoryStore {
|
|
6577
6679
|
vecInitialized = false;
|
|
6680
|
+
ftsInitialized = false;
|
|
6578
6681
|
lastPopulate = null;
|
|
6579
6682
|
constructor() {
|
|
6580
6683
|
this.ensureVecTable();
|
|
6684
|
+
this.ensureFtsTable();
|
|
6685
|
+
}
|
|
6686
|
+
ensureFtsTable() {
|
|
6687
|
+
if (this.ftsInitialized)
|
|
6688
|
+
return;
|
|
6689
|
+
const db = getDb();
|
|
6690
|
+
try {
|
|
6691
|
+
db.run(`
|
|
6692
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
6693
|
+
memory_id UNINDEXED,
|
|
6694
|
+
name,
|
|
6695
|
+
content,
|
|
6696
|
+
tokenize='porter unicode61'
|
|
6697
|
+
)
|
|
6698
|
+
`);
|
|
6699
|
+
this.populateFtsTable();
|
|
6700
|
+
this.ftsInitialized = true;
|
|
6701
|
+
} catch (err) {
|
|
6702
|
+
this.ftsInitialized = false;
|
|
6703
|
+
console.error("[memory-fts] Failed to initialize memory_fts:", err.message);
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6706
|
+
getFtsTableSchema() {
|
|
6707
|
+
try {
|
|
6708
|
+
return getDb().prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memory_fts'").get()?.sql ?? null;
|
|
6709
|
+
} catch {
|
|
6710
|
+
return null;
|
|
6711
|
+
}
|
|
6712
|
+
}
|
|
6713
|
+
populateFtsTable() {
|
|
6714
|
+
const db = getDb();
|
|
6715
|
+
const deletedExtra = db.prepare(`DELETE FROM memory_fts
|
|
6716
|
+
WHERE memory_id NOT IN (SELECT id FROM agent_memory)`).run();
|
|
6717
|
+
if (deletedExtra.changes > 0) {
|
|
6718
|
+
console.warn(`[memory-fts] removed_extra_rows count=${deletedExtra.changes}`);
|
|
6719
|
+
}
|
|
6720
|
+
const inserted = db.prepare(`INSERT INTO memory_fts(memory_id, name, content)
|
|
6721
|
+
SELECT m.id, m.name, m.content
|
|
6722
|
+
FROM agent_memory m
|
|
6723
|
+
WHERE NOT EXISTS (SELECT 1 FROM memory_fts f WHERE f.memory_id = m.id)`).run();
|
|
6724
|
+
console.log(`[memory-fts] populate inserted=${inserted.changes}`);
|
|
6725
|
+
}
|
|
6726
|
+
syncFtsRow(memoryId, name, content) {
|
|
6727
|
+
if (!this.ftsInitialized && !this.getFtsTableSchema())
|
|
6728
|
+
return;
|
|
6729
|
+
const db = getDb();
|
|
6730
|
+
try {
|
|
6731
|
+
db.prepare("DELETE FROM memory_fts WHERE memory_id = ?").run(memoryId);
|
|
6732
|
+
db.prepare("INSERT INTO memory_fts(memory_id, name, content) VALUES (?, ?, ?)").run(memoryId, name, content);
|
|
6733
|
+
} catch (err) {
|
|
6734
|
+
console.error(`[memory-fts] sync failed memory_id=${memoryId}: ${err.message}`);
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
deleteFtsRows(ids) {
|
|
6738
|
+
if (ids.length === 0 || !this.ftsInitialized && !this.getFtsTableSchema())
|
|
6739
|
+
return;
|
|
6740
|
+
const db = getDb();
|
|
6741
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
6742
|
+
db.prepare(`DELETE FROM memory_fts WHERE memory_id IN (${placeholders})`).run(...ids);
|
|
6581
6743
|
}
|
|
6582
6744
|
ensureVecTable() {
|
|
6583
6745
|
if (this.vecInitialized)
|
|
@@ -6682,10 +6844,23 @@ class SqliteMemoryStore {
|
|
|
6682
6844
|
const id = crypto.randomUUID();
|
|
6683
6845
|
const now = new Date().toISOString();
|
|
6684
6846
|
const expiresAt = computeExpiresAt(input.source);
|
|
6685
|
-
const
|
|
6686
|
-
|
|
6847
|
+
const key = input.key ?? `${input.scope}/${input.source}/${id}`;
|
|
6848
|
+
const contentHash = contentSha256(input.content);
|
|
6849
|
+
const version = 1;
|
|
6850
|
+
const db = getDb();
|
|
6851
|
+
let row;
|
|
6852
|
+
const tx = db.transaction(() => {
|
|
6853
|
+
row = db.prepare(`INSERT INTO agent_memory (id, agentId, scope, key, name, content, summary, source, sourceTaskId, sourcePath, chunkIndex, totalChunks, tags, createdAt, updatedAt, accessedAt, expiresAt, accessCount, embeddingModel, contextKey, contentHash, version)
|
|
6854
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, input.agentId ?? null, input.scope, key, input.name, input.content, input.summary ?? null, input.source, input.sourceTaskId ?? null, input.sourcePath ?? null, input.chunkIndex ?? 0, input.totalChunks ?? 1, JSON.stringify(input.tags ?? []), now, now, now, expiresAt, 0, null, input.contextKey ?? null, contentHash, version);
|
|
6855
|
+
if (!row)
|
|
6856
|
+
throw new Error("Failed to create memory");
|
|
6857
|
+
db.prepare(`INSERT INTO agent_memory_version (id, memory_id, version, content, contentHash, intent, operation, changedByAgentId, createdAt, updatedAt, created_by, updated_by)
|
|
6858
|
+
VALUES (?, ?, ?, ?, ?, ?, 'create', ?, ?, ?, ?, ?)`).run(crypto.randomUUID(), row.id, version, input.content, contentHash, input.intent ?? "create memory", input.agentId ?? null, now, now, input.agentId ?? null, input.agentId ?? null);
|
|
6859
|
+
});
|
|
6860
|
+
tx();
|
|
6687
6861
|
if (!row)
|
|
6688
6862
|
throw new Error("Failed to create memory");
|
|
6863
|
+
this.syncFtsRow(row.id, row.name, row.content);
|
|
6689
6864
|
return rowToAgentMemory(row);
|
|
6690
6865
|
}
|
|
6691
6866
|
storeBatch(inputs) {
|
|
@@ -6716,6 +6891,16 @@ class SqliteMemoryStore {
|
|
|
6716
6891
|
search(embedding, agentId, options = {}) {
|
|
6717
6892
|
const { scope = "all", limit: limit2 = 10, source, isLead = false, includeExpired = false } = options;
|
|
6718
6893
|
const health = this.getHealth();
|
|
6894
|
+
if (isHybridSearchEnabled() && options.queryText && this.ftsInitialized && this.getFtsTableSchema() && health.retrievalMode === "vec" && embedding.length === EMBEDDING_DIMENSIONS) {
|
|
6895
|
+
console.log(`[memory-search] retrieval_path=hybrid scope=${scope} limit=${limit2} vec_rows=${health.counts.memoryVec} searchable=${health.counts.searchable}`);
|
|
6896
|
+
return this.searchHybrid(embedding, options.queryText, agentId, {
|
|
6897
|
+
scope,
|
|
6898
|
+
limit: limit2,
|
|
6899
|
+
source,
|
|
6900
|
+
isLead,
|
|
6901
|
+
includeExpired
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6719
6904
|
if (health.retrievalMode === "vec" && embedding.length === EMBEDDING_DIMENSIONS) {
|
|
6720
6905
|
console.log(`[memory-search] retrieval_path=vec scope=${scope} limit=${limit2} vec_rows=${health.counts.memoryVec} searchable=${health.counts.searchable}`);
|
|
6721
6906
|
return this.searchWithVec(embedding, agentId, {
|
|
@@ -6726,6 +6911,16 @@ class SqliteMemoryStore {
|
|
|
6726
6911
|
includeExpired
|
|
6727
6912
|
});
|
|
6728
6913
|
}
|
|
6914
|
+
if (options.queryText && this.ftsInitialized && this.getFtsTableSchema()) {
|
|
6915
|
+
console.log(`[memory-search] retrieval_path=fts scope=${scope} limit=${limit2} reason=${embedding.length !== EMBEDDING_DIMENSIONS ? "query_dimension_mismatch" : health.reasons.join("|") || "vec_unavailable"}`);
|
|
6916
|
+
return this.searchFts(options.queryText, agentId, {
|
|
6917
|
+
scope,
|
|
6918
|
+
limit: limit2,
|
|
6919
|
+
source,
|
|
6920
|
+
isLead,
|
|
6921
|
+
includeExpired
|
|
6922
|
+
});
|
|
6923
|
+
}
|
|
6729
6924
|
console.log(`[memory-search] retrieval_path=fallback scope=${scope} limit=${limit2} reason=${embedding.length !== EMBEDDING_DIMENSIONS ? "query_dimension_mismatch" : health.reasons.join("|") || "vec_unavailable"}`);
|
|
6730
6925
|
return this.searchBruteForce(embedding, agentId, {
|
|
6731
6926
|
scope,
|
|
@@ -6735,6 +6930,83 @@ class SqliteMemoryStore {
|
|
|
6735
6930
|
includeExpired
|
|
6736
6931
|
});
|
|
6737
6932
|
}
|
|
6933
|
+
searchHybrid(queryEmbedding, queryText, agentId, options) {
|
|
6934
|
+
const overfetchLimit = Math.min(Math.max(options.limit * 4, options.limit), 100);
|
|
6935
|
+
const vectorCandidates = this.searchWithVec(queryEmbedding, agentId, {
|
|
6936
|
+
...options,
|
|
6937
|
+
limit: overfetchLimit
|
|
6938
|
+
});
|
|
6939
|
+
const ftsCandidates = this.searchFts(queryText, agentId, { ...options, limit: overfetchLimit });
|
|
6940
|
+
if (ftsCandidates.length === 0)
|
|
6941
|
+
return vectorCandidates.slice(0, options.limit);
|
|
6942
|
+
const byId = new Map;
|
|
6943
|
+
const scores = new Map;
|
|
6944
|
+
const sources = new Map;
|
|
6945
|
+
const now = new Date;
|
|
6946
|
+
const add = (candidate, rank) => {
|
|
6947
|
+
byId.set(candidate.id, byId.get(candidate.id) ?? candidate);
|
|
6948
|
+
const retrievalSource = candidate.retrievalSource === "fts" ? "fts" : "vec";
|
|
6949
|
+
const candidateSources = sources.get(candidate.id) ?? new Set;
|
|
6950
|
+
candidateSources.add(retrievalSource);
|
|
6951
|
+
sources.set(candidate.id, candidateSources);
|
|
6952
|
+
const decay = recencyDecay(candidate.createdAt, now, candidate.source);
|
|
6953
|
+
scores.set(candidate.id, (scores.get(candidate.id) ?? 0) + computeRrfScore(rank, decay));
|
|
6954
|
+
};
|
|
6955
|
+
vectorCandidates.forEach(add);
|
|
6956
|
+
ftsCandidates.forEach(add);
|
|
6957
|
+
return [...byId.values()].map((candidate) => ({
|
|
6958
|
+
...candidate,
|
|
6959
|
+
rawSimilarity: candidate.rawSimilarity ?? candidate.similarity,
|
|
6960
|
+
similarity: scores.get(candidate.id) ?? candidate.similarity,
|
|
6961
|
+
retrievalSource: retrievalSourceFor(sources.get(candidate.id) ?? new Set),
|
|
6962
|
+
recencyDecayApplied: true
|
|
6963
|
+
})).sort((a, b) => b.similarity - a.similarity).slice(0, options.limit);
|
|
6964
|
+
}
|
|
6965
|
+
searchFts(queryText, agentId, options) {
|
|
6966
|
+
const match = this.buildFtsMatch(queryText);
|
|
6967
|
+
if (!match)
|
|
6968
|
+
return [];
|
|
6969
|
+
const db = getDb();
|
|
6970
|
+
const { scope, limit: limit2, source, isLead, includeExpired } = options;
|
|
6971
|
+
const conditions = ["memory_fts MATCH ?"];
|
|
6972
|
+
const params = [match];
|
|
6973
|
+
this.addScopeConditions(conditions, params, agentId, scope, isLead, "m");
|
|
6974
|
+
if (source) {
|
|
6975
|
+
conditions.push("m.source = ?");
|
|
6976
|
+
params.push(source);
|
|
6977
|
+
}
|
|
6978
|
+
if (!includeExpired) {
|
|
6979
|
+
conditions.push("(m.expiresAt IS NULL OR m.expiresAt > datetime('now'))");
|
|
6980
|
+
}
|
|
6981
|
+
try {
|
|
6982
|
+
const sqlLimit = Math.min(Math.max(limit2 * 4, limit2), 100);
|
|
6983
|
+
const rows = db.prepare(`SELECT m.*, bm25(memory_fts) AS rank
|
|
6984
|
+
FROM memory_fts
|
|
6985
|
+
JOIN agent_memory m ON m.id = memory_fts.memory_id
|
|
6986
|
+
WHERE ${conditions.join(" AND ")}
|
|
6987
|
+
ORDER BY rank
|
|
6988
|
+
LIMIT ?`).all(...params, sqlLimit);
|
|
6989
|
+
const now = new Date;
|
|
6990
|
+
return rows.map((row, index) => {
|
|
6991
|
+
const rawSimilarity = 1 / (index + 1);
|
|
6992
|
+
return {
|
|
6993
|
+
...rowToCandidate(row, rawSimilarity * recencyDecay(row.createdAt, now, row.source)),
|
|
6994
|
+
rawSimilarity,
|
|
6995
|
+
retrievalSource: "fts",
|
|
6996
|
+
recencyDecayApplied: true
|
|
6997
|
+
};
|
|
6998
|
+
}).sort((a, b) => b.similarity - a.similarity).slice(0, limit2);
|
|
6999
|
+
} catch (err) {
|
|
7000
|
+
console.warn("[memory-fts] query failed:", err.message);
|
|
7001
|
+
return [];
|
|
7002
|
+
}
|
|
7003
|
+
}
|
|
7004
|
+
buildFtsMatch(queryText) {
|
|
7005
|
+
const terms = queryText.trim().split(/[^\p{L}\p{N}_-]+/u).map((term) => term.trim()).filter((term) => term.length > 0).slice(0, 12);
|
|
7006
|
+
if (terms.length === 0)
|
|
7007
|
+
return null;
|
|
7008
|
+
return terms.map((term) => `"${term.replaceAll('"', '""')}"`).join(" OR ");
|
|
7009
|
+
}
|
|
6738
7010
|
searchWithVec(queryEmbedding, agentId, options) {
|
|
6739
7011
|
const db = getDb();
|
|
6740
7012
|
const { scope, limit: limit2, source, isLead, includeExpired } = options;
|
|
@@ -6763,7 +7035,7 @@ class SqliteMemoryStore {
|
|
|
6763
7035
|
const similarity = 1 - row.distance;
|
|
6764
7036
|
if (similarity < MIN_SIMILARITY)
|
|
6765
7037
|
continue;
|
|
6766
|
-
candidates.push(rowToCandidate(row, similarity));
|
|
7038
|
+
candidates.push({ ...rowToCandidate(row, similarity), retrievalSource: "vec" });
|
|
6767
7039
|
}
|
|
6768
7040
|
return candidates;
|
|
6769
7041
|
}
|
|
@@ -6792,7 +7064,7 @@ class SqliteMemoryStore {
|
|
|
6792
7064
|
const similarity = cosineSimilarity(queryEmbedding, emb);
|
|
6793
7065
|
if (similarity < MIN_SIMILARITY)
|
|
6794
7066
|
continue;
|
|
6795
|
-
candidates.push(rowToCandidate(row, similarity));
|
|
7067
|
+
candidates.push({ ...rowToCandidate(row, similarity), retrievalSource: "fallback" });
|
|
6796
7068
|
}
|
|
6797
7069
|
candidates.sort((a, b) => b.similarity - a.similarity);
|
|
6798
7070
|
return candidates.slice(0, limit2);
|
|
@@ -6857,6 +7129,60 @@ class SqliteMemoryStore {
|
|
|
6857
7129
|
isSourceProtected(source) {
|
|
6858
7130
|
return PROTECTED_SOURCES.has(source);
|
|
6859
7131
|
}
|
|
7132
|
+
edit(input) {
|
|
7133
|
+
if (!input.id && !(input.key && input.scope)) {
|
|
7134
|
+
throw new Error("memory edit requires either id or key+scope");
|
|
7135
|
+
}
|
|
7136
|
+
const db = getDb();
|
|
7137
|
+
const row = input.id ? db.prepare("SELECT * FROM agent_memory WHERE id = ?").get(input.id) : db.prepare(`SELECT * FROM agent_memory
|
|
7138
|
+
WHERE key = ? AND scope = ? AND coalesce(agentId, '') = coalesce(?, '')
|
|
7139
|
+
ORDER BY chunkIndex ASC
|
|
7140
|
+
LIMIT 1`).get(input.key, input.scope, input.agentId ?? null);
|
|
7141
|
+
if (!row)
|
|
7142
|
+
throw new Error("memory not found");
|
|
7143
|
+
if ((row.totalChunks ?? 1) !== 1)
|
|
7144
|
+
throw new Error("memory edit only supports single-chunk rows");
|
|
7145
|
+
if (input.expectedVersion && input.expectedVersion !== (row.version ?? 1)) {
|
|
7146
|
+
throw new Error("memory version conflict");
|
|
7147
|
+
}
|
|
7148
|
+
const previousVersion = row.version ?? 1;
|
|
7149
|
+
const nextContent = applyEditMode(input.mode, row.content, {
|
|
7150
|
+
content: input.content,
|
|
7151
|
+
oldString: input.oldString,
|
|
7152
|
+
newString: input.newString
|
|
7153
|
+
});
|
|
7154
|
+
const nextHash = contentSha256(nextContent);
|
|
7155
|
+
if (nextHash === row.contentHash) {
|
|
7156
|
+
return {
|
|
7157
|
+
memory: rowToAgentMemory(row),
|
|
7158
|
+
changed: false,
|
|
7159
|
+
previousVersion,
|
|
7160
|
+
version: previousVersion,
|
|
7161
|
+
contentHash: nextHash
|
|
7162
|
+
};
|
|
7163
|
+
}
|
|
7164
|
+
const nextVersion = previousVersion + 1;
|
|
7165
|
+
const now = new Date().toISOString();
|
|
7166
|
+
const tx = db.transaction(() => {
|
|
7167
|
+
db.prepare(`INSERT INTO agent_memory_version (id, memory_id, version, content, contentHash, intent, operation, changedByAgentId, createdAt, updatedAt, created_by, updated_by)
|
|
7168
|
+
VALUES (?, ?, ?, ?, ?, ?, 'edit', ?, ?, ?, ?, ?)`).run(crypto.randomUUID(), row.id, nextVersion, nextContent, nextHash, input.intent, input.changedByAgentId ?? null, now, now, input.changedByAgentId ?? null, input.changedByAgentId ?? null);
|
|
7169
|
+
db.prepare(`UPDATE agent_memory
|
|
7170
|
+
SET content = ?, contentHash = ?, version = ?, updatedAt = ?
|
|
7171
|
+
WHERE id = ?`).run(nextContent, nextHash, nextVersion, now, row.id);
|
|
7172
|
+
});
|
|
7173
|
+
tx();
|
|
7174
|
+
this.syncFtsRow(row.id, row.name, nextContent);
|
|
7175
|
+
const updated = db.prepare("SELECT * FROM agent_memory WHERE id = ?").get(row.id);
|
|
7176
|
+
if (!updated)
|
|
7177
|
+
throw new Error("memory disappeared after edit");
|
|
7178
|
+
return {
|
|
7179
|
+
memory: rowToAgentMemory(updated),
|
|
7180
|
+
changed: true,
|
|
7181
|
+
previousVersion,
|
|
7182
|
+
version: nextVersion,
|
|
7183
|
+
contentHash: nextHash
|
|
7184
|
+
};
|
|
7185
|
+
}
|
|
6860
7186
|
listForCuration(agentId) {
|
|
6861
7187
|
const db = getDb();
|
|
6862
7188
|
const protectedList = [...PROTECTED_SOURCES].map((s) => `'${s}'`).join(",");
|
|
@@ -6874,38 +7200,37 @@ class SqliteMemoryStore {
|
|
|
6874
7200
|
}
|
|
6875
7201
|
return db.prepare("SELECT id, content FROM agent_memory").all();
|
|
6876
7202
|
}
|
|
6877
|
-
|
|
7203
|
+
purgeByIds(ids) {
|
|
7204
|
+
if (ids.length === 0)
|
|
7205
|
+
return;
|
|
6878
7206
|
const db = getDb();
|
|
6879
7207
|
if (this.vecInitialized && this.getVecTableSchema()) {
|
|
6880
|
-
|
|
7208
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
7209
|
+
db.prepare(`DELETE FROM memory_vec WHERE memory_id IN (${placeholders})`).run(...ids);
|
|
6881
7210
|
}
|
|
7211
|
+
this.deleteFtsRows(ids);
|
|
7212
|
+
}
|
|
7213
|
+
delete(id) {
|
|
7214
|
+
const db = getDb();
|
|
7215
|
+
this.purgeByIds([id]);
|
|
6882
7216
|
const result = db.prepare("DELETE FROM agent_memory WHERE id = ?").run(id);
|
|
6883
7217
|
return result.changes > 0;
|
|
6884
7218
|
}
|
|
6885
7219
|
deleteBySourcePath(sourcePath, agentId) {
|
|
6886
7220
|
const db = getDb();
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
if (ids.length > 0) {
|
|
6890
|
-
const placeholders = ids.map(() => "?").join(",");
|
|
6891
|
-
db.prepare(`DELETE FROM memory_vec WHERE memory_id IN (${placeholders})`).run(...ids.map((r) => r.id));
|
|
6892
|
-
}
|
|
6893
|
-
}
|
|
7221
|
+
const ids = db.prepare("SELECT id FROM agent_memory WHERE sourcePath = ? AND agentId = ?").all(sourcePath, agentId);
|
|
7222
|
+
this.purgeByIds(ids.map((r) => r.id));
|
|
6894
7223
|
const result = db.prepare("DELETE FROM agent_memory WHERE sourcePath = ? AND agentId = ?").run(sourcePath, agentId);
|
|
6895
|
-
return result.changes;
|
|
7224
|
+
return ids.length || result.changes;
|
|
6896
7225
|
}
|
|
6897
7226
|
purgeExpired() {
|
|
6898
7227
|
const db = getDb();
|
|
6899
7228
|
const expiredIds = db.prepare("SELECT id FROM agent_memory WHERE expiresAt IS NOT NULL AND expiresAt <= datetime('now')").all();
|
|
6900
7229
|
if (expiredIds.length === 0)
|
|
6901
7230
|
return 0;
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
const batch = expiredIds.slice(i, i + batchSize);
|
|
6906
|
-
const placeholders = batch.map(() => "?").join(",");
|
|
6907
|
-
db.prepare(`DELETE FROM memory_vec WHERE memory_id IN (${placeholders})`).run(...batch.map((r) => r.id));
|
|
6908
|
-
}
|
|
7231
|
+
const batchSize = 500;
|
|
7232
|
+
for (let i = 0;i < expiredIds.length; i += batchSize) {
|
|
7233
|
+
this.purgeByIds(expiredIds.slice(i, i + batchSize).map((r) => r.id));
|
|
6909
7234
|
}
|
|
6910
7235
|
const result = db.prepare("DELETE FROM agent_memory WHERE expiresAt IS NOT NULL AND expiresAt <= datetime('now')").run();
|
|
6911
7236
|
console.log(`[memory] Purged ${result.changes} expired memory row(s) (vec cleanup: ${expiredIds.length} id(s))`);
|
|
@@ -7013,7 +7338,9 @@ var VECTOR_BYTES;
|
|
|
7013
7338
|
var init_sqlite_store = __esm(() => {
|
|
7014
7339
|
init_db();
|
|
7015
7340
|
init_embedding();
|
|
7341
|
+
init_profile_sync();
|
|
7016
7342
|
init_constants();
|
|
7343
|
+
init_reranker();
|
|
7017
7344
|
VECTOR_BYTES = EMBEDDING_DIMENSIONS * Float32Array.BYTES_PER_ELEMENT;
|
|
7018
7345
|
});
|
|
7019
7346
|
|
|
@@ -7035,4 +7362,4 @@ function getMemoryStore() {
|
|
|
7035
7362
|
return memoryStore;
|
|
7036
7363
|
}
|
|
7037
7364
|
|
|
7038
|
-
export {
|
|
7365
|
+
export { CANDIDATE_SET_MULTIPLIER, EMBEDDING_DIMENSIONS, init_constants, cosineSimilarity, serializeEmbedding, deserializeEmbedding, init_embedding, rerank, init_reranker, getEmbeddingProvider, getMemoryStore };
|