open-agents-ai 0.187.439 → 0.187.441

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/index.js CHANGED
@@ -512278,6 +512278,317 @@ var init_toolPatternStore = __esm({
512278
512278
  }
512279
512279
  });
512280
512280
 
512281
+ // packages/memory/dist/zettelkasten.js
512282
+ function cosineSimilarity2(a2, b) {
512283
+ if (a2.length !== b.length || a2.length === 0)
512284
+ return 0;
512285
+ let dot = 0, normA = 0, normB = 0;
512286
+ for (let i2 = 0; i2 < a2.length; i2++) {
512287
+ dot += a2[i2] * b[i2];
512288
+ normA += a2[i2] * a2[i2];
512289
+ normB += b[i2] * b[i2];
512290
+ }
512291
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
512292
+ return denom > 0 ? dot / denom : 0;
512293
+ }
512294
+ function findNeighbors(episode, candidates, topK, minSimilarity) {
512295
+ if (!episode.embedding)
512296
+ return [];
512297
+ const scored = [];
512298
+ for (const candidate of candidates) {
512299
+ if (candidate.id === episode.id)
512300
+ continue;
512301
+ if (!candidate.embedding)
512302
+ continue;
512303
+ let sim;
512304
+ if (episode.embedding.length === candidate.embedding.length) {
512305
+ sim = cosineSimilarity2(episode.embedding, candidate.embedding);
512306
+ } else if (episode.clipEmbedding && candidate.clipEmbedding && episode.clipEmbedding.length === candidate.clipEmbedding.length) {
512307
+ sim = cosineSimilarity2(episode.clipEmbedding, candidate.clipEmbedding);
512308
+ } else {
512309
+ continue;
512310
+ }
512311
+ if (sim >= minSimilarity) {
512312
+ scored.push({ episode: candidate, similarity: sim });
512313
+ }
512314
+ }
512315
+ scored.sort((a2, b) => b.similarity - a2.similarity);
512316
+ return scored.slice(0, topK);
512317
+ }
512318
+ function linkEpisode(episode, episodeStore, graph, config) {
512319
+ const cfg = { ...DEFAULT_CONFIG3, ...config };
512320
+ const result = { episodeId: episode.id, linkedTo: [], linksCreated: 0 };
512321
+ if (!episode.embedding)
512322
+ return result;
512323
+ const candidates = episodeStore.recent(500).filter((e2) => e2.embedding != null);
512324
+ const neighbors2 = findNeighbors(episode, candidates, cfg.topK, cfg.minSimilarity);
512325
+ for (const { episode: neighbor, similarity } of neighbors2) {
512326
+ if (result.linksCreated >= cfg.maxLinksPerEpisode)
512327
+ break;
512328
+ const srcNodeId = graph.upsertNode({
512329
+ text: episode.content.slice(0, 100),
512330
+ nodeType: "event"
512331
+ });
512332
+ const dstNodeId = graph.upsertNode({
512333
+ text: neighbor.content.slice(0, 100),
512334
+ nodeType: "event"
512335
+ });
512336
+ graph.addEdge({
512337
+ srcId: srcNodeId,
512338
+ dstId: dstNodeId,
512339
+ relation: "related_to",
512340
+ fact: `Associated: ${episode.content.slice(0, 50)} ↔ ${neighbor.content.slice(0, 50)}`,
512341
+ edgeType: "synonym",
512342
+ confidence: similarity,
512343
+ sourceEpisodeId: episode.id
512344
+ });
512345
+ result.linkedTo.push({ neighborId: neighbor.id, similarity, linked: true });
512346
+ result.linksCreated++;
512347
+ if (similarity >= cfg.evolutionThreshold && neighbor.gist !== episode.content.slice(0, 200)) {
512348
+ const evolvedGist = neighbor.gist ? `${neighbor.gist} | Also related: ${episode.content.slice(0, 100)}` : `Related context: ${episode.content.slice(0, 150)}`;
512349
+ episodeStore.setGist(neighbor.id, evolvedGist.slice(0, 500));
512350
+ }
512351
+ }
512352
+ return result;
512353
+ }
512354
+ function batchLink(episodeStore, graph, config) {
512355
+ const episodes = episodeStore.recent(200).filter((e2) => e2.embedding != null);
512356
+ let totalLinks = 0;
512357
+ let processed = 0;
512358
+ for (const ep of episodes) {
512359
+ const result = linkEpisode(ep, episodeStore, graph, config);
512360
+ totalLinks += result.linksCreated;
512361
+ if (result.linksCreated > 0)
512362
+ processed++;
512363
+ }
512364
+ return { processed, linksCreated: totalLinks };
512365
+ }
512366
+ var DEFAULT_CONFIG3;
512367
+ var init_zettelkasten = __esm({
512368
+ "packages/memory/dist/zettelkasten.js"() {
512369
+ "use strict";
512370
+ DEFAULT_CONFIG3 = {
512371
+ topK: 3,
512372
+ minSimilarity: 0.3,
512373
+ evolutionThreshold: 0.6,
512374
+ maxLinksPerEpisode: 10
512375
+ };
512376
+ }
512377
+ });
512378
+
512379
+ // packages/memory/dist/pprRetrieval.js
512380
+ function extractQueryEntities(query) {
512381
+ const entities = [];
512382
+ const seen = /* @__PURE__ */ new Set();
512383
+ const add2 = (text) => {
512384
+ const normalized = text.trim().toLowerCase();
512385
+ if (normalized.length < 2 || seen.has(normalized))
512386
+ return;
512387
+ seen.add(normalized);
512388
+ entities.push(text.trim());
512389
+ };
512390
+ const filePattern = /(?:[\w./-]+\/[\w./-]+|[\w-]+\.\w{1,5})\b/g;
512391
+ for (const match of query.matchAll(filePattern)) {
512392
+ if (match[0].includes(".") || match[0].includes("/"))
512393
+ add2(match[0]);
512394
+ }
512395
+ const errorPattern = /\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b|\b\w+(?:Error|Exception|Failed|Failure)\b/g;
512396
+ for (const match of query.matchAll(errorPattern))
512397
+ add2(match[0]);
512398
+ const camelPattern = /\b[a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]+)+\b/g;
512399
+ for (const match of query.matchAll(camelPattern))
512400
+ add2(match[0]);
512401
+ const toolPattern = /\b(?:file_read|file_write|file_edit|shell|grep_search|find_files|memory_\w+|web_\w+|camera_\w+|audio_\w+|wifi_\w+|bluetooth_\w+|sdr_\w+|flipper_\w+|meshtastic|gps_\w+|visual_\w+)\b/g;
512402
+ for (const match of query.matchAll(toolPattern))
512403
+ add2(match[0]);
512404
+ const quotedPattern = /["']([^"']{2,50})["']/g;
512405
+ for (const match of query.matchAll(quotedPattern))
512406
+ add2(match[1]);
512407
+ const STOP_WORDS2 = /* @__PURE__ */ new Set([
512408
+ "the",
512409
+ "and",
512410
+ "for",
512411
+ "was",
512412
+ "are",
512413
+ "not",
512414
+ "but",
512415
+ "had",
512416
+ "has",
512417
+ "have",
512418
+ "this",
512419
+ "that",
512420
+ "with",
512421
+ "from",
512422
+ "what",
512423
+ "when",
512424
+ "where",
512425
+ "how",
512426
+ "why",
512427
+ "which",
512428
+ "who",
512429
+ "did",
512430
+ "does",
512431
+ "can",
512432
+ "could",
512433
+ "would",
512434
+ "should",
512435
+ "will",
512436
+ "been",
512437
+ "being",
512438
+ "were",
512439
+ "about",
512440
+ "than",
512441
+ "then",
512442
+ "also",
512443
+ "just",
512444
+ "more",
512445
+ "some",
512446
+ "only",
512447
+ "into",
512448
+ "over"
512449
+ ]);
512450
+ const words = query.split(/\s+/).filter((w) => w.length >= 4 && !STOP_WORDS2.has(w.toLowerCase()));
512451
+ for (const word2 of words) {
512452
+ if (/^[A-Z]/.test(word2) || /[._-]/.test(word2) || /\d/.test(word2)) {
512453
+ add2(word2.replace(/[.,;:!?]+$/, ""));
512454
+ }
512455
+ }
512456
+ return entities;
512457
+ }
512458
+ function personalizedPageRank(graph, seedNodeIds, config) {
512459
+ const cfg = { ...DEFAULT_CONFIG4, ...config };
512460
+ const scores = /* @__PURE__ */ new Map();
512461
+ if (seedNodeIds.length === 0)
512462
+ return scores;
512463
+ const nodeCount = graph.nodeCount();
512464
+ if (nodeCount === 0)
512465
+ return scores;
512466
+ const teleportProb = 1 / seedNodeIds.length;
512467
+ const seedSet = new Set(seedNodeIds);
512468
+ const allNodes = /* @__PURE__ */ new Set();
512469
+ for (const seedId of seedNodeIds) {
512470
+ allNodes.add(seedId);
512471
+ const neighbors2 = graph.neighbors(seedId);
512472
+ for (const { node } of neighbors2) {
512473
+ allNodes.add(node.id);
512474
+ const hop2 = graph.neighbors(node.id);
512475
+ for (const { node: n2 } of hop2)
512476
+ allNodes.add(n2.id);
512477
+ }
512478
+ }
512479
+ for (const nodeId of allNodes) {
512480
+ scores.set(nodeId, seedSet.has(nodeId) ? teleportProb : 0);
512481
+ }
512482
+ for (let iter = 0; iter < cfg.maxIterations; iter++) {
512483
+ const newScores = /* @__PURE__ */ new Map();
512484
+ let maxDelta = 0;
512485
+ for (const nodeId of allNodes) {
512486
+ const teleport = seedSet.has(nodeId) ? (1 - cfg.damping) * teleportProb : 0;
512487
+ let walkSum = 0;
512488
+ const inEdges = graph.currentEdges(nodeId);
512489
+ for (const edge of inEdges) {
512490
+ const neighborId = edge.srcId === nodeId ? edge.dstId : edge.srcId;
512491
+ const neighborScore = scores.get(neighborId) ?? 0;
512492
+ const neighborOutDegree = graph.currentEdges(neighborId).length || 1;
512493
+ walkSum += neighborScore / neighborOutDegree;
512494
+ }
512495
+ const newScore = teleport + cfg.damping * walkSum;
512496
+ newScores.set(nodeId, newScore);
512497
+ const delta = Math.abs(newScore - (scores.get(nodeId) ?? 0));
512498
+ if (delta > maxDelta)
512499
+ maxDelta = delta;
512500
+ }
512501
+ for (const [id, score] of newScores)
512502
+ scores.set(id, score);
512503
+ if (maxDelta < cfg.convergenceThreshold) {
512504
+ return scores;
512505
+ }
512506
+ }
512507
+ return scores;
512508
+ }
512509
+ function retrieveByPPR(query, graph, episodeStore, config) {
512510
+ const cfg = { ...DEFAULT_CONFIG4, ...config };
512511
+ const queryEntities = extractQueryEntities(query);
512512
+ if (queryEntities.length === 0) {
512513
+ return { episodes: [], queryEntities: [], seedNodes: [], iterations: 0 };
512514
+ }
512515
+ const seedNodes = [];
512516
+ for (const entity of queryEntities) {
512517
+ const seedsBefore = seedNodes.length;
512518
+ const node = graph.findNode(entity);
512519
+ if (node) {
512520
+ seedNodes.push(node.id);
512521
+ }
512522
+ const symbolNodes = graph.findSymbolNodesByName(entity, 10);
512523
+ for (const s2 of symbolNodes)
512524
+ seedNodes.push(s2.id);
512525
+ if (seedNodes.length > seedsBefore)
512526
+ continue;
512527
+ for (const nodeType of ["file", "error", "tool", "entity", "event", "concept", "person"]) {
512528
+ const candidates = graph.nodesByType(nodeType, 50);
512529
+ for (const candidate of candidates) {
512530
+ if (candidate.text.toLowerCase().includes(entity.toLowerCase()) || entity.toLowerCase().includes(candidate.text.toLowerCase())) {
512531
+ seedNodes.push(candidate.id);
512532
+ break;
512533
+ }
512534
+ }
512535
+ if (seedNodes.length > seedsBefore)
512536
+ break;
512537
+ }
512538
+ }
512539
+ if (seedNodes.length === 0) {
512540
+ return { episodes: [], queryEntities, seedNodes: [], iterations: 0 };
512541
+ }
512542
+ const pprScores = personalizedPageRank(graph, seedNodes, cfg);
512543
+ const episodeScores = /* @__PURE__ */ new Map();
512544
+ for (const [nodeId, score] of pprScores) {
512545
+ if (score < 1e-3)
512546
+ continue;
512547
+ const node = graph.getNode(nodeId);
512548
+ if (!node)
512549
+ continue;
512550
+ const edges = graph.currentEdges(nodeId);
512551
+ for (const edge of edges) {
512552
+ if (edge.sourceEpisodeId) {
512553
+ const existing = episodeScores.get(edge.sourceEpisodeId);
512554
+ if (existing) {
512555
+ existing.score += score;
512556
+ existing.matchedNodes.push(node.text);
512557
+ } else {
512558
+ episodeScores.set(edge.sourceEpisodeId, { score, matchedNodes: [node.text] });
512559
+ }
512560
+ }
512561
+ }
512562
+ }
512563
+ const ranked = [...episodeScores.entries()].sort((a2, b) => b[1].score - a2[1].score).slice(0, cfg.topK);
512564
+ const episodes = [];
512565
+ for (const [epId, { score, matchedNodes }] of ranked) {
512566
+ const episode = episodeStore.get(epId);
512567
+ if (episode) {
512568
+ episodes.push({ episode, pprScore: score, matchedNodes: [...new Set(matchedNodes)] });
512569
+ }
512570
+ }
512571
+ return {
512572
+ episodes,
512573
+ queryEntities,
512574
+ seedNodes,
512575
+ iterations: cfg.maxIterations
512576
+ };
512577
+ }
512578
+ var DEFAULT_CONFIG4;
512579
+ var init_pprRetrieval = __esm({
512580
+ "packages/memory/dist/pprRetrieval.js"() {
512581
+ "use strict";
512582
+ init_zettelkasten();
512583
+ DEFAULT_CONFIG4 = {
512584
+ damping: 0.5,
512585
+ maxIterations: 50,
512586
+ convergenceThreshold: 1e-6,
512587
+ topK: 10
512588
+ };
512589
+ }
512590
+ });
512591
+
512281
512592
  // packages/memory/dist/episodeStore.js
512282
512593
  import { join as join66 } from "node:path";
512283
512594
  import { mkdirSync as mkdirSync26, existsSync as existsSync49 } from "node:fs";
@@ -512349,6 +512660,8 @@ var init_episodeStore = __esm({
512349
512660
  "packages/memory/dist/episodeStore.js"() {
512350
512661
  "use strict";
512351
512662
  init_db();
512663
+ init_zettelkasten();
512664
+ init_pprRetrieval();
512352
512665
  DECAY_TAU = {
512353
512666
  session: 36e5,
512354
512667
  // 1 hour
@@ -512360,11 +512673,19 @@ var init_episodeStore = __esm({
512360
512673
  };
512361
512674
  EpisodeStore = class {
512362
512675
  db;
512363
- constructor(dbPath) {
512676
+ /** WO-AM-05: Optional TemporalGraph for Zettelkasten auto-linking */
512677
+ graph = null;
512678
+ /** WO-AM-05: Zettelkasten config for neighbor linking */
512679
+ zettelConfig = { topK: 5, minSimilarity: 0.7, evolutionThreshold: 0.6, maxLinksPerEpisode: 10 };
512680
+ constructor(dbPath, graph, zettelConfig) {
512364
512681
  const dir = dbPath === ":memory:" ? null : join66(dbPath, "..");
512365
512682
  if (dir && !existsSync49(dir))
512366
512683
  mkdirSync26(dir, { recursive: true });
512367
512684
  this.db = initDb(dbPath);
512685
+ this.graph = graph ?? null;
512686
+ if (zettelConfig) {
512687
+ this.zettelConfig = { ...this.zettelConfig, ...zettelConfig };
512688
+ }
512368
512689
  this.db.exec(`
512369
512690
  CREATE TABLE IF NOT EXISTS episodes (
512370
512691
  id TEXT PRIMARY KEY,
@@ -512414,6 +512735,15 @@ var init_episodeStore = __esm({
512414
512735
  INSERT INTO episodes (id, timestamp, session_id, task_id, turn_number, modality, tool_name, content, content_hash, metadata, importance, decay_class, strength)
512415
512736
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1.0)
512416
512737
  `).run(id, now, ep.sessionId ?? null, ep.taskId ?? null, ep.turnNumber ?? null, modality, ep.toolName ?? null, ep.content, contentHash, ep.metadata ? JSON.stringify(ep.metadata) : null, importance, decayClass);
512738
+ if (this.graph) {
512739
+ try {
512740
+ const episode = this.get(id);
512741
+ if (episode) {
512742
+ linkEpisode(episode, this, this.graph, this.zettelConfig);
512743
+ }
512744
+ } catch {
512745
+ }
512746
+ }
512417
512747
  return id;
512418
512748
  }
512419
512749
  /** Retrieve episodes matching a query, scored by recency + importance + (lexical + embedding). */
@@ -512527,6 +512857,34 @@ var init_episodeStore = __esm({
512527
512857
  }
512528
512858
  return topK.map((s2) => s2.episode);
512529
512859
  }
512860
+ /** WO-AM-05: PPR-enhanced search for multi-hop retrieval (HippoRAG).
512861
+ * Falls back to standard search if graph is unavailable or no entities found.
512862
+ */
512863
+ searchWithPPR(query, opts = {}) {
512864
+ const standardResults = this.search(query, opts);
512865
+ if (!this.graph || !query.query) {
512866
+ return standardResults;
512867
+ }
512868
+ try {
512869
+ const pprResult = retrieveByPPR(query.query, this.graph, this, {
512870
+ damping: 0.5,
512871
+ maxIterations: 50,
512872
+ convergenceThreshold: 1e-6,
512873
+ topK: query.limit ?? 20
512874
+ });
512875
+ const merged = [...standardResults];
512876
+ const seenIds = new Set(merged.map((e2) => e2.id));
512877
+ for (const { episode } of pprResult.episodes) {
512878
+ if (!seenIds.has(episode.id)) {
512879
+ merged.push(episode);
512880
+ seenIds.add(episode.id);
512881
+ }
512882
+ }
512883
+ return merged.slice(0, query.limit ?? 20);
512884
+ } catch {
512885
+ return standardResults;
512886
+ }
512887
+ }
512530
512888
  /** Get a single episode by ID. */
512531
512889
  get(id) {
512532
512890
  const row = this.db.prepare("SELECT * FROM episodes WHERE id = ?").get(id);
@@ -512829,104 +513187,6 @@ var init_temporalGraph = __esm({
512829
513187
  }
512830
513188
  });
512831
513189
 
512832
- // packages/memory/dist/zettelkasten.js
512833
- function cosineSimilarity2(a2, b) {
512834
- if (a2.length !== b.length || a2.length === 0)
512835
- return 0;
512836
- let dot = 0, normA = 0, normB = 0;
512837
- for (let i2 = 0; i2 < a2.length; i2++) {
512838
- dot += a2[i2] * b[i2];
512839
- normA += a2[i2] * a2[i2];
512840
- normB += b[i2] * b[i2];
512841
- }
512842
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
512843
- return denom > 0 ? dot / denom : 0;
512844
- }
512845
- function findNeighbors(episode, candidates, topK, minSimilarity) {
512846
- if (!episode.embedding)
512847
- return [];
512848
- const scored = [];
512849
- for (const candidate of candidates) {
512850
- if (candidate.id === episode.id)
512851
- continue;
512852
- if (!candidate.embedding)
512853
- continue;
512854
- let sim;
512855
- if (episode.embedding.length === candidate.embedding.length) {
512856
- sim = cosineSimilarity2(episode.embedding, candidate.embedding);
512857
- } else if (episode.clipEmbedding && candidate.clipEmbedding && episode.clipEmbedding.length === candidate.clipEmbedding.length) {
512858
- sim = cosineSimilarity2(episode.clipEmbedding, candidate.clipEmbedding);
512859
- } else {
512860
- continue;
512861
- }
512862
- if (sim >= minSimilarity) {
512863
- scored.push({ episode: candidate, similarity: sim });
512864
- }
512865
- }
512866
- scored.sort((a2, b) => b.similarity - a2.similarity);
512867
- return scored.slice(0, topK);
512868
- }
512869
- function linkEpisode(episode, episodeStore, graph, config) {
512870
- const cfg = { ...DEFAULT_CONFIG3, ...config };
512871
- const result = { episodeId: episode.id, linkedTo: [], linksCreated: 0 };
512872
- if (!episode.embedding)
512873
- return result;
512874
- const candidates = episodeStore.recent(500).filter((e2) => e2.embedding != null);
512875
- const neighbors2 = findNeighbors(episode, candidates, cfg.topK, cfg.minSimilarity);
512876
- for (const { episode: neighbor, similarity } of neighbors2) {
512877
- if (result.linksCreated >= cfg.maxLinksPerEpisode)
512878
- break;
512879
- const srcNodeId = graph.upsertNode({
512880
- text: episode.content.slice(0, 100),
512881
- nodeType: "event"
512882
- });
512883
- const dstNodeId = graph.upsertNode({
512884
- text: neighbor.content.slice(0, 100),
512885
- nodeType: "event"
512886
- });
512887
- graph.addEdge({
512888
- srcId: srcNodeId,
512889
- dstId: dstNodeId,
512890
- relation: "related_to",
512891
- fact: `Associated: ${episode.content.slice(0, 50)} ↔ ${neighbor.content.slice(0, 50)}`,
512892
- edgeType: "synonym",
512893
- confidence: similarity,
512894
- sourceEpisodeId: episode.id
512895
- });
512896
- result.linkedTo.push({ neighborId: neighbor.id, similarity, linked: true });
512897
- result.linksCreated++;
512898
- if (similarity >= cfg.evolutionThreshold && neighbor.gist !== episode.content.slice(0, 200)) {
512899
- const evolvedGist = neighbor.gist ? `${neighbor.gist} | Also related: ${episode.content.slice(0, 100)}` : `Related context: ${episode.content.slice(0, 150)}`;
512900
- episodeStore.setGist(neighbor.id, evolvedGist.slice(0, 500));
512901
- }
512902
- }
512903
- return result;
512904
- }
512905
- function batchLink(episodeStore, graph, config) {
512906
- const episodes = episodeStore.recent(200).filter((e2) => e2.embedding != null);
512907
- let totalLinks = 0;
512908
- let processed = 0;
512909
- for (const ep of episodes) {
512910
- const result = linkEpisode(ep, episodeStore, graph, config);
512911
- totalLinks += result.linksCreated;
512912
- if (result.linksCreated > 0)
512913
- processed++;
512914
- }
512915
- return { processed, linksCreated: totalLinks };
512916
- }
512917
- var DEFAULT_CONFIG3;
512918
- var init_zettelkasten = __esm({
512919
- "packages/memory/dist/zettelkasten.js"() {
512920
- "use strict";
512921
- DEFAULT_CONFIG3 = {
512922
- topK: 3,
512923
- minSimilarity: 0.3,
512924
- evolutionThreshold: 0.6,
512925
- maxLinksPerEpisode: 10
512926
- };
512927
- }
512928
- });
512929
-
512930
513190
  // packages/memory/dist/gistCompressor.js
512931
513191
  function compressToGist(episodes, taskDescription) {
512932
513192
  if (episodes.length === 0) {
@@ -513027,219 +513287,6 @@ var init_gistCompressor = __esm({
513027
513287
  }
513028
513288
  });
513029
513289
 
513030
- // packages/memory/dist/pprRetrieval.js
513031
- function extractQueryEntities(query) {
513032
- const entities = [];
513033
- const seen = /* @__PURE__ */ new Set();
513034
- const add2 = (text) => {
513035
- const normalized = text.trim().toLowerCase();
513036
- if (normalized.length < 2 || seen.has(normalized))
513037
- return;
513038
- seen.add(normalized);
513039
- entities.push(text.trim());
513040
- };
513041
- const filePattern = /(?:[\w./-]+\/[\w./-]+|[\w-]+\.\w{1,5})\b/g;
513042
- for (const match of query.matchAll(filePattern)) {
513043
- if (match[0].includes(".") || match[0].includes("/"))
513044
- add2(match[0]);
513045
- }
513046
- const errorPattern = /\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b|\b\w+(?:Error|Exception|Failed|Failure)\b/g;
513047
- for (const match of query.matchAll(errorPattern))
513048
- add2(match[0]);
513049
- const camelPattern = /\b[a-z][a-z0-9]*(?:[A-Z][a-zA-Z0-9]+)+\b/g;
513050
- for (const match of query.matchAll(camelPattern))
513051
- add2(match[0]);
513052
- const toolPattern = /\b(?:file_read|file_write|file_edit|shell|grep_search|find_files|memory_\w+|web_\w+|camera_\w+|audio_\w+|wifi_\w+|bluetooth_\w+|sdr_\w+|flipper_\w+|meshtastic|gps_\w+|visual_\w+)\b/g;
513053
- for (const match of query.matchAll(toolPattern))
513054
- add2(match[0]);
513055
- const quotedPattern = /["']([^"']{2,50})["']/g;
513056
- for (const match of query.matchAll(quotedPattern))
513057
- add2(match[1]);
513058
- const STOP_WORDS2 = /* @__PURE__ */ new Set([
513059
- "the",
513060
- "and",
513061
- "for",
513062
- "was",
513063
- "are",
513064
- "not",
513065
- "but",
513066
- "had",
513067
- "has",
513068
- "have",
513069
- "this",
513070
- "that",
513071
- "with",
513072
- "from",
513073
- "what",
513074
- "when",
513075
- "where",
513076
- "how",
513077
- "why",
513078
- "which",
513079
- "who",
513080
- "did",
513081
- "does",
513082
- "can",
513083
- "could",
513084
- "would",
513085
- "should",
513086
- "will",
513087
- "been",
513088
- "being",
513089
- "were",
513090
- "about",
513091
- "than",
513092
- "then",
513093
- "also",
513094
- "just",
513095
- "more",
513096
- "some",
513097
- "only",
513098
- "into",
513099
- "over"
513100
- ]);
513101
- const words = query.split(/\s+/).filter((w) => w.length >= 4 && !STOP_WORDS2.has(w.toLowerCase()));
513102
- for (const word2 of words) {
513103
- if (/^[A-Z]/.test(word2) || /[._-]/.test(word2) || /\d/.test(word2)) {
513104
- add2(word2.replace(/[.,;:!?]+$/, ""));
513105
- }
513106
- }
513107
- return entities;
513108
- }
513109
- function personalizedPageRank(graph, seedNodeIds, config) {
513110
- const cfg = { ...DEFAULT_CONFIG4, ...config };
513111
- const scores = /* @__PURE__ */ new Map();
513112
- if (seedNodeIds.length === 0)
513113
- return scores;
513114
- const nodeCount = graph.nodeCount();
513115
- if (nodeCount === 0)
513116
- return scores;
513117
- const teleportProb = 1 / seedNodeIds.length;
513118
- const seedSet = new Set(seedNodeIds);
513119
- const allNodes = /* @__PURE__ */ new Set();
513120
- for (const seedId of seedNodeIds) {
513121
- allNodes.add(seedId);
513122
- const neighbors2 = graph.neighbors(seedId);
513123
- for (const { node } of neighbors2) {
513124
- allNodes.add(node.id);
513125
- const hop2 = graph.neighbors(node.id);
513126
- for (const { node: n2 } of hop2)
513127
- allNodes.add(n2.id);
513128
- }
513129
- }
513130
- for (const nodeId of allNodes) {
513131
- scores.set(nodeId, seedSet.has(nodeId) ? teleportProb : 0);
513132
- }
513133
- for (let iter = 0; iter < cfg.maxIterations; iter++) {
513134
- const newScores = /* @__PURE__ */ new Map();
513135
- let maxDelta = 0;
513136
- for (const nodeId of allNodes) {
513137
- const teleport = seedSet.has(nodeId) ? (1 - cfg.damping) * teleportProb : 0;
513138
- let walkSum = 0;
513139
- const inEdges = graph.currentEdges(nodeId);
513140
- for (const edge of inEdges) {
513141
- const neighborId = edge.srcId === nodeId ? edge.dstId : edge.srcId;
513142
- const neighborScore = scores.get(neighborId) ?? 0;
513143
- const neighborOutDegree = graph.currentEdges(neighborId).length || 1;
513144
- walkSum += neighborScore / neighborOutDegree;
513145
- }
513146
- const newScore = teleport + cfg.damping * walkSum;
513147
- newScores.set(nodeId, newScore);
513148
- const delta = Math.abs(newScore - (scores.get(nodeId) ?? 0));
513149
- if (delta > maxDelta)
513150
- maxDelta = delta;
513151
- }
513152
- for (const [id, score] of newScores)
513153
- scores.set(id, score);
513154
- if (maxDelta < cfg.convergenceThreshold) {
513155
- return scores;
513156
- }
513157
- }
513158
- return scores;
513159
- }
513160
- function retrieveByPPR(query, graph, episodeStore, config) {
513161
- const cfg = { ...DEFAULT_CONFIG4, ...config };
513162
- const queryEntities = extractQueryEntities(query);
513163
- if (queryEntities.length === 0) {
513164
- return { episodes: [], queryEntities: [], seedNodes: [], iterations: 0 };
513165
- }
513166
- const seedNodes = [];
513167
- for (const entity of queryEntities) {
513168
- const seedsBefore = seedNodes.length;
513169
- const node = graph.findNode(entity);
513170
- if (node) {
513171
- seedNodes.push(node.id);
513172
- }
513173
- const symbolNodes = graph.findSymbolNodesByName(entity, 10);
513174
- for (const s2 of symbolNodes)
513175
- seedNodes.push(s2.id);
513176
- if (seedNodes.length > seedsBefore)
513177
- continue;
513178
- for (const nodeType of ["file", "error", "tool", "entity", "event", "concept", "person"]) {
513179
- const candidates = graph.nodesByType(nodeType, 50);
513180
- for (const candidate of candidates) {
513181
- if (candidate.text.toLowerCase().includes(entity.toLowerCase()) || entity.toLowerCase().includes(candidate.text.toLowerCase())) {
513182
- seedNodes.push(candidate.id);
513183
- break;
513184
- }
513185
- }
513186
- if (seedNodes.length > seedsBefore)
513187
- break;
513188
- }
513189
- }
513190
- if (seedNodes.length === 0) {
513191
- return { episodes: [], queryEntities, seedNodes: [], iterations: 0 };
513192
- }
513193
- const pprScores = personalizedPageRank(graph, seedNodes, cfg);
513194
- const episodeScores = /* @__PURE__ */ new Map();
513195
- for (const [nodeId, score] of pprScores) {
513196
- if (score < 1e-3)
513197
- continue;
513198
- const node = graph.getNode(nodeId);
513199
- if (!node)
513200
- continue;
513201
- const edges = graph.currentEdges(nodeId);
513202
- for (const edge of edges) {
513203
- if (edge.sourceEpisodeId) {
513204
- const existing = episodeScores.get(edge.sourceEpisodeId);
513205
- if (existing) {
513206
- existing.score += score;
513207
- existing.matchedNodes.push(node.text);
513208
- } else {
513209
- episodeScores.set(edge.sourceEpisodeId, { score, matchedNodes: [node.text] });
513210
- }
513211
- }
513212
- }
513213
- }
513214
- const ranked = [...episodeScores.entries()].sort((a2, b) => b[1].score - a2[1].score).slice(0, cfg.topK);
513215
- const episodes = [];
513216
- for (const [epId, { score, matchedNodes }] of ranked) {
513217
- const episode = episodeStore.get(epId);
513218
- if (episode) {
513219
- episodes.push({ episode, pprScore: score, matchedNodes: [...new Set(matchedNodes)] });
513220
- }
513221
- }
513222
- return {
513223
- episodes,
513224
- queryEntities,
513225
- seedNodes,
513226
- iterations: cfg.maxIterations
513227
- };
513228
- }
513229
- var DEFAULT_CONFIG4;
513230
- var init_pprRetrieval = __esm({
513231
- "packages/memory/dist/pprRetrieval.js"() {
513232
- "use strict";
513233
- init_zettelkasten();
513234
- DEFAULT_CONFIG4 = {
513235
- damping: 0.5,
513236
- maxIterations: 50,
513237
- convergenceThreshold: 1e-6,
513238
- topK: 10
513239
- };
513240
- }
513241
- });
513242
-
513243
513290
  // packages/memory/dist/embeddings.js
513244
513291
  async function generateEmbedding(text, config) {
513245
513292
  const cfg = { ...DEFAULT_CONFIG5, ...config };
@@ -526073,6 +526120,8 @@ var init_listen = __esm({
526073
526120
  liveTranscriber = null;
526074
526121
  // TranscribeLive from transcribe-cli or WhisperFallbackTranscriber
526075
526122
  active = false;
526123
+ paused = false;
526124
+ // Pause state for voicechat (stops mic but keeps transcriber)
526076
526125
  silenceTimer = null;
526077
526126
  countdownInterval = null;
526078
526127
  lastTranscriptTime = 0;
@@ -526094,6 +526143,9 @@ var init_listen = __esm({
526094
526143
  get isBlinking() {
526095
526144
  return this.active && this.blinkState;
526096
526145
  }
526146
+ get isPaused() {
526147
+ return this.paused;
526148
+ }
526097
526149
  get currentModel() {
526098
526150
  return this.config.model;
526099
526151
  }
@@ -526320,7 +526372,7 @@ transcribe-cli error: ${transcribeCliError}` : "";
526320
526372
  env: { ...process.env }
526321
526373
  });
526322
526374
  this.micProcess.stdout?.on("data", (chunk) => {
526323
- if (this.active && this.liveTranscriber) {
526375
+ if (this.active && !this.paused && this.liveTranscriber) {
526324
526376
  this.liveTranscriber.write(chunk);
526325
526377
  }
526326
526378
  });
@@ -526396,6 +526448,67 @@ transcribe-cli error: ${transcribeCliError}` : "";
526396
526448
  this.pendingText = "";
526397
526449
  return text;
526398
526450
  }
526451
+ /**
526452
+ * Pause microphone capture (for voicechat during TTS output).
526453
+ * Keeps transcriber alive but stops feeding it audio.
526454
+ */
526455
+ pause() {
526456
+ if (!this.active || this.paused) return;
526457
+ this.paused = true;
526458
+ if (this.blinkTimer) {
526459
+ clearInterval(this.blinkTimer);
526460
+ this.blinkTimer = null;
526461
+ }
526462
+ this.blinkState = false;
526463
+ if (this.micProcess) {
526464
+ try {
526465
+ this.micProcess.kill("SIGTERM");
526466
+ } catch {
526467
+ }
526468
+ this.micProcess = null;
526469
+ }
526470
+ this.emit("paused");
526471
+ this.emit("recording", false);
526472
+ }
526473
+ /**
526474
+ * Resume microphone capture after pause.
526475
+ */
526476
+ async resume() {
526477
+ if (!this.active || !this.paused) return "Not paused.";
526478
+ this.paused = false;
526479
+ const micCmd = findMicCaptureCommand();
526480
+ if (!micCmd) {
526481
+ return "No microphone capture tool found.";
526482
+ }
526483
+ this.micProcess = spawn18(micCmd.cmd, micCmd.args, {
526484
+ stdio: ["pipe", "pipe", "pipe"],
526485
+ env: { ...process.env }
526486
+ });
526487
+ this.micProcess.stdout?.on("data", (chunk) => {
526488
+ if (this.active && !this.paused && this.liveTranscriber) {
526489
+ this.liveTranscriber.write(chunk);
526490
+ }
526491
+ });
526492
+ this.micProcess.stderr?.on("data", () => {
526493
+ });
526494
+ onChildError(this.micProcess, (err) => {
526495
+ this.emit("error", new Error(`Mic capture failed: ${err.message}`));
526496
+ this.stop();
526497
+ });
526498
+ onChildClose(this.micProcess, () => {
526499
+ if (this.active && !this.paused) {
526500
+ this.stop();
526501
+ }
526502
+ });
526503
+ this.blinkState = true;
526504
+ this.blinkTimer = setInterval(() => {
526505
+ this.blinkState = !this.blinkState;
526506
+ this.emit("recording", this.blinkState);
526507
+ }, 500);
526508
+ this.emit("resumed");
526509
+ this.emit("recording", true);
526510
+ return "Resumed listening.";
526511
+ }
526399
526512
  /**
526400
526513
  * Create a standalone transcriber for call sessions.
526401
526514
  * No microphone capture — just an ASR engine you feed PCM chunks to.
@@ -582941,6 +583054,10 @@ ${toolOutput}` });
582941
583054
  this.context.push({ role: "assistant", content: finalSpoken });
582942
583055
  this.setState("SPEAKING");
582943
583056
  this.onAgentSpeech(finalSpoken);
583057
+ try {
583058
+ this.listen.pause();
583059
+ } catch {
583060
+ }
582944
583061
  this.voice.speak(finalSpoken);
582945
583062
  this.voiceTranscript.push({ role: "assistant", content: finalSpoken, ts: Date.now() });
582946
583063
  this.voiceTranscript.push({ role: "assistant", content: response.trim(), ts: Date.now() });
@@ -582967,6 +583084,10 @@ ${toolOutput}` });
582967
583084
  this.abortController = null;
582968
583085
  }
582969
583086
  if (this.active) {
583087
+ try {
583088
+ await this.listen.resume();
583089
+ } catch {
583090
+ }
582970
583091
  this.setState("LISTENING");
582971
583092
  if (this.verbose) this.onStatus("LISTENING...");
582972
583093
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.439",
3
+ "version": "0.187.441",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.439",
9
+ "version": "0.187.441",
10
10
  "hasInstallScript": true,
11
11
  "license": "CC-BY-NC-4.0",
12
12
  "dependencies": {
@@ -2630,9 +2630,9 @@
2630
2630
  }
2631
2631
  },
2632
2632
  "node_modules/ajv": {
2633
- "version": "8.18.0",
2634
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
2635
- "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
2633
+ "version": "8.20.0",
2634
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
2635
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
2636
2636
  "license": "MIT",
2637
2637
  "dependencies": {
2638
2638
  "fast-deep-equal": "^3.1.3",
@@ -4207,9 +4207,9 @@
4207
4207
  }
4208
4208
  },
4209
4209
  "node_modules/express-rate-limit": {
4210
- "version": "8.3.2",
4211
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz",
4212
- "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==",
4210
+ "version": "8.4.1",
4211
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz",
4212
+ "integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==",
4213
4213
  "license": "MIT",
4214
4214
  "dependencies": {
4215
4215
  "ip-address": "10.1.0"
@@ -4794,9 +4794,9 @@
4794
4794
  }
4795
4795
  },
4796
4796
  "node_modules/hono": {
4797
- "version": "4.12.14",
4798
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz",
4799
- "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==",
4797
+ "version": "4.12.15",
4798
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.15.tgz",
4799
+ "integrity": "sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==",
4800
4800
  "license": "MIT",
4801
4801
  "engines": {
4802
4802
  "node": ">=16.9.0"
@@ -8622,9 +8622,9 @@
8622
8622
  }
8623
8623
  },
8624
8624
  "node_modules/terser": {
8625
- "version": "5.46.1",
8626
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
8627
- "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
8625
+ "version": "5.46.2",
8626
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz",
8627
+ "integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==",
8628
8628
  "license": "BSD-2-Clause",
8629
8629
  "peer": true,
8630
8630
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.439",
3
+ "version": "0.187.441",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",