@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.
Files changed (122) hide show
  1. package/README.md +2 -0
  2. package/dist/{actions-7txktrdj.js → actions-2expzt47.js} +6 -6
  3. package/dist/{app-xm3xsdye.js → app-dr8hwx7z.js} +3 -3
  4. package/dist/{assistant-qpv63wk6.js → assistant-9rhgsw7v.js} +9 -10
  5. package/dist/{boot-reembed-chxhrwv8.js → boot-reembed-3q1aywnz.js} +5 -4
  6. package/dist/{boot-reembed-9cxqka3e.js → boot-reembed-9kt97g1z.js} +4 -3
  7. package/dist/{boot-scrub-logs-8qtfkcz8.js → boot-scrub-logs-vskq108e.js} +2 -2
  8. package/dist/{cli-fedn86nz.js → cli-0jw6j755.js} +8 -3
  9. package/dist/{cli-yeemwevj.js → cli-23zk7xqy.js} +20 -16
  10. package/dist/{cli-v9283nma.js → cli-2g9jf9ek.js} +1 -1
  11. package/dist/{cli-rvae030h.js → cli-2hap19dv.js} +3 -3
  12. package/dist/{cli-wjyyv518.js → cli-3j01d33e.js} +9 -14
  13. package/dist/{cli-8hyv6c2v.js → cli-54h5daha.js} +41 -23
  14. package/dist/{cli-fygaw191.js → cli-5pf0b84v.js} +2 -2
  15. package/dist/{cli-m8f6qzck.js → cli-7v4y3hyd.js} +1 -1
  16. package/dist/{cli-fwxdt7kt.js → cli-d4z1csms.js} +30 -5
  17. package/dist/{cli-jt9d9fjw.js → cli-e7bwjntj.js} +1 -1
  18. package/dist/{cli-s14sb64w.js → cli-etvgz5x8.js} +5 -7
  19. package/dist/{cli-2qynerth.js → cli-gj2n2ec1.js} +1 -1
  20. package/dist/{cli-dz3d6zjg.js → cli-nzc0h54v.js} +121 -2
  21. package/dist/{cli-8w95v19d.js → cli-p4cj4vhc.js} +351 -24
  22. package/dist/{cli-xmtsan9k.js → cli-q5tshxd6.js} +1 -1
  23. package/dist/{http-zts4haq7.js → cli-qvzgncd8.js} +83966 -76422
  24. package/dist/{cli-4fnp2tc3.js → cli-s42f54ja.js} +1 -1
  25. package/dist/{cli-krvxq8bc.js → cli-t9v14cnd.js} +23 -4
  26. package/dist/{cli-gzepjz54.js → cli-v1syqkcb.js} +900 -352
  27. package/dist/{cli-ksq38x4n.js → cli-w1nfskkg.js} +2 -2
  28. package/dist/{cli-ns0r7mkr.js → cli-y9p3e94h.js} +3 -3
  29. package/dist/{cli-85pf4z38.js → cli-yjt8a36k.js} +3 -3
  30. package/dist/{cli-yfwwjeft.js → cli-yvektv79.js} +1 -1
  31. package/dist/{cli-kk51f3tf.js → cli-zaxksbgj.js} +5 -5
  32. package/dist/{cli-rttgde5f.js → cli-zghef0em.js} +1 -1
  33. package/dist/cli.js +10 -10
  34. package/dist/{commands-aqn63mcw.js → commands-wfrkef57.js} +2 -2
  35. package/dist/{db-azrvwsnj.js → db-c42pxt7h.js} +2 -2
  36. package/dist/{handlers-hf61har3.js → handlers-n36zkfvx.js} +10 -12
  37. package/dist/{hook-306p3yz4.js → hook-gw3vhv6s.js} +7 -5
  38. package/dist/http-59ygpn2h.js +17263 -0
  39. package/dist/{index-h2yqjyk0.js → index-881c1baf.js} +9 -8
  40. package/dist/{index-29vg51x6.js → index-jca7jdpt.js} +23 -10
  41. package/dist/{index-bfh9hgek.js → index-xf6fp4y1.js} +5 -5
  42. package/dist/{index-3c651yfk.js → index-xz8vaa5c.js} +14 -11
  43. package/dist/{keepalive-a0dg5pds.js → keepalive-mj4302vw.js} +4 -4
  44. package/dist/{lead-7mcxedkc.js → lead-qm0xjvkv.js} +19 -19
  45. package/dist/{maintenance-mrzqd53t.js → maintenance-xppxbe4t.js} +5 -4
  46. package/dist/{onboard-4dkpv9wa.js → onboard-ejx6yxh8.js} +2 -2
  47. package/dist/{otel-impl-jt7gpyp9.js → otel-impl-zs5exkpk.js} +1 -1
  48. package/dist/{pricing-refresh-xqnzh8dt.js → pricing-refresh-nqyf8bkb.js} +4 -4
  49. package/dist/{seed-pricing-56tjm1fj.js → seed-pricing-w3x550cj.js} +3 -3
  50. package/dist/{setup-tbwk8j10.js → setup-7729ktfj.js} +2 -2
  51. package/dist/{worker-e03za92t.js → worker-4ttemwhw.js} +19 -19
  52. package/openapi.json +90 -4
  53. package/package.json +3 -3
  54. package/src/be/memory/constants.ts +6 -0
  55. package/src/be/memory/providers/sqlite-store.ts +468 -71
  56. package/src/be/memory/raters/retrieval.ts +4 -2
  57. package/src/be/memory/reranker.ts +5 -2
  58. package/src/be/memory/retrieval-store.ts +2 -0
  59. package/src/be/memory/types.ts +46 -0
  60. package/src/be/migrations/099_memory_structured_key_versioning.sql +41 -0
  61. package/src/be/migrations/100_memory_retrieval_source.sql +8 -0
  62. package/src/be/migrations/101_script_connections.sql +67 -0
  63. package/src/be/script-connections.ts +744 -0
  64. package/src/be/script-credential-broker.ts +38 -0
  65. package/src/be/scripts/typecheck.ts +23 -2
  66. package/src/be/seed-scripts/catalog/boot-triage.inline.ts +16 -4
  67. package/src/be/seed-scripts/catalog/boot-triage.ts +16 -4
  68. package/src/commands/runner.ts +8 -6
  69. package/src/heartbeat/heartbeat.ts +42 -1
  70. package/src/heartbeat/index.ts +7 -1
  71. package/src/http/memory.ts +127 -14
  72. package/src/http/scripts.ts +15 -14
  73. package/src/scripts-runtime/api-client.ts +100 -0
  74. package/src/scripts-runtime/api-types.ts +45 -0
  75. package/src/scripts-runtime/credential-broker/README.md +40 -0
  76. package/src/scripts-runtime/credential-broker/broker.ts +51 -0
  77. package/src/scripts-runtime/credential-broker/default-bindings.ts +19 -0
  78. package/src/scripts-runtime/credential-broker/fetch-patch.ts +102 -0
  79. package/src/scripts-runtime/credential-broker/index.ts +17 -0
  80. package/src/scripts-runtime/credential-broker/store.ts +72 -0
  81. package/src/scripts-runtime/credential-broker/types.ts +54 -0
  82. package/src/scripts-runtime/ctx.ts +11 -1
  83. package/src/scripts-runtime/egress-secrets.ts +14 -76
  84. package/src/scripts-runtime/eval-harness.ts +1 -1
  85. package/src/scripts-runtime/executors/types.ts +5 -5
  86. package/src/scripts-runtime/loader.ts +6 -1
  87. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  88. package/src/scripts-runtime/types/stdlib.d.ts +11 -0
  89. package/src/scripts-runtime/types/swarm-sdk.d.ts +11 -0
  90. package/src/server.ts +6 -0
  91. package/src/tests/credential-broker.test.ts +281 -0
  92. package/src/tests/heartbeat.test.ts +239 -0
  93. package/src/tests/memory-edit.test.ts +177 -0
  94. package/src/tests/memory-hybrid.test.ts +212 -0
  95. package/src/tests/runner-repo-autostash.test.ts +39 -8
  96. package/src/tests/script-connections.test.ts +360 -0
  97. package/src/tests/scripts-http.test.ts +16 -10
  98. package/src/tests/scripts-runtime-secret-egress.test.ts +26 -5
  99. package/src/tests/swarm-config-reserved-keys.test.ts +23 -0
  100. package/src/tests/update-schedule-mcp-tool.test.ts +83 -4
  101. package/src/tools/credential-bindings/index.ts +1 -0
  102. package/src/tools/credential-bindings/tool.ts +238 -0
  103. package/src/tools/memory-edit.ts +148 -0
  104. package/src/tools/memory-search.ts +17 -10
  105. package/src/tools/schedules/delete-schedule.ts +18 -8
  106. package/src/tools/schedules/update-schedule.ts +4 -9
  107. package/src/tools/script-connections/index.ts +1 -0
  108. package/src/tools/script-connections/tool.ts +167 -0
  109. package/src/tools/swarm-config/delete-config.ts +5 -0
  110. package/src/tools/swarm-config/set-config.ts +23 -1
  111. package/src/tools/tool-config.ts +6 -1
  112. package/src/types.ts +6 -0
  113. package/src/workflows/executors/swarm-script.ts +2 -0
  114. package/templates/community/dora-metrics/PLAYBOOK.md +294 -0
  115. package/templates/community/dora-metrics/README.md +39 -0
  116. package/templates/community/dora-metrics/lead-prompt.md +56 -0
  117. package/templates/community/dora-metrics/report.mjs +393 -0
  118. package/templates/community/dora-metrics/run.sh +113 -0
  119. package/templates/schedules/weekly-dora-metrics/config.json +13 -0
  120. package/templates/schedules/weekly-dora-metrics/content.md +61 -0
  121. package/dist/cli-2307phk1.js +0 -24223
  122. 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-fedn86nz.js";
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 row = getDb().prepare(`INSERT INTO agent_memory (id, agentId, scope, name, content, summary, source, sourceTaskId, sourcePath, chunkIndex, totalChunks, tags, createdAt, accessedAt, expiresAt, accessCount, embeddingModel, contextKey)
6686
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`).get(id, input.agentId ?? null, input.scope, 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, expiresAt, 0, null, input.contextKey ?? null);
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
- delete(id) {
7203
+ purgeByIds(ids) {
7204
+ if (ids.length === 0)
7205
+ return;
6878
7206
  const db = getDb();
6879
7207
  if (this.vecInitialized && this.getVecTableSchema()) {
6880
- db.prepare("DELETE FROM memory_vec WHERE memory_id = ?").run(id);
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
- if (this.vecInitialized && this.getVecTableSchema()) {
6888
- const ids = db.prepare("SELECT id FROM agent_memory WHERE sourcePath = ? AND agentId = ?").all(sourcePath, agentId);
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
- if (this.vecInitialized && this.getVecTableSchema()) {
6903
- const batchSize = 500;
6904
- for (let i = 0;i < expiredIds.length; i += batchSize) {
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 { RECENCY_DECAY_HALF_LIFE, RECENCY_DECAY_HALF_LIFE_DAYS, SOURCE_QUALITY_MULTIPLIER, ACCESS_BOOST_MAX_MULTIPLIER, ACCESS_BOOST_RECENCY_WINDOW_HOURS, CANDIDATE_SET_MULTIPLIER, EMBEDDING_DIMENSIONS, init_constants, cosineSimilarity, serializeEmbedding, deserializeEmbedding, init_embedding, getEmbeddingProvider, getMemoryStore };
7365
+ export { CANDIDATE_SET_MULTIPLIER, EMBEDDING_DIMENSIONS, init_constants, cosineSimilarity, serializeEmbedding, deserializeEmbedding, init_embedding, rerank, init_reranker, getEmbeddingProvider, getMemoryStore };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_package,
3
3
  package_default
4
- } from "./cli-85pf4z38.js";
4
+ } from "./cli-yjt8a36k.js";
5
5
 
6
6
  // src/commands/shared/client-config.ts
7
7
  init_package();