open-agents-ai 0.187.162 → 0.187.163

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 (2) hide show
  1. package/dist/index.js +311 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3680,7 +3680,7 @@ var init_memory_search = __esm({
3680
3680
  "use strict";
3681
3681
  MemorySearchTool = class {
3682
3682
  name = "memory_search";
3683
- description = "Search across all memory entries by relevance. Unlike memory_read (exact topic+key), this finds relevant memories using text matching. Use when you don't know the exact topic or key, or want to find all memories related to a concept.";
3683
+ description = "Search across all memory entries by relevance. Unlike memory_read (exact topic+key), this finds relevant memories using text matching or associative graph traversal. Use when you don't know the exact topic or key, or want to find all memories related to a concept. Mode 'graph' uses knowledge graph traversal for multi-hop associative queries (e.g. 'what caused the auth error?').";
3684
3684
  parameters = {
3685
3685
  type: "object",
3686
3686
  properties: {
@@ -3691,6 +3691,11 @@ var init_memory_search = __esm({
3691
3691
  max_results: {
3692
3692
  type: "number",
3693
3693
  description: "Maximum number of results to return (default: 10)"
3694
+ },
3695
+ mode: {
3696
+ type: "string",
3697
+ enum: ["text", "graph", "episodes"],
3698
+ description: "Search mode: 'text' (default \u2014 TF-IDF), 'graph' (PPR traversal), 'episodes' (recent episodes). Default: text."
3694
3699
  }
3695
3700
  },
3696
3701
  required: ["query"]
@@ -3702,7 +3707,87 @@ var init_memory_search = __esm({
3702
3707
  async execute(args) {
3703
3708
  const query = args["query"];
3704
3709
  const maxResults = args["max_results"] ?? 10;
3710
+ const mode = args["mode"] ?? "text";
3705
3711
  const start2 = performance.now();
3712
+ if (mode === "graph" || mode === "episodes") {
3713
+ try {
3714
+ const { join: pathJoin } = await import("node:path");
3715
+ const { existsSync: pathExists } = await import("node:fs");
3716
+ const { createRequire: createRequire7 } = await import("node:module");
3717
+ const oaDir = pathJoin(this.workingDir, ".oa");
3718
+ const epDbPath = pathJoin(oaDir, "episodes.db");
3719
+ const kgDbPath = pathJoin(oaDir, "knowledge.db");
3720
+ if (!pathExists(epDbPath)) {
3721
+ return { success: true, output: `No episode store found. Run tasks to build the knowledge graph.`, durationMs: performance.now() - start2 };
3722
+ }
3723
+ const req2 = createRequire7(import.meta.url);
3724
+ const Database2 = req2("better-sqlite3");
3725
+ if (mode === "episodes") {
3726
+ const db = new Database2(epDbPath, { readonly: true });
3727
+ const queryLower = query.toLowerCase();
3728
+ const rows = db.prepare(`
3729
+ SELECT * FROM episodes
3730
+ WHERE content LIKE ? OR tool_name LIKE ?
3731
+ ORDER BY importance DESC, timestamp DESC
3732
+ LIMIT ?
3733
+ `).all(`%${queryLower}%`, `%${queryLower}%`, maxResults);
3734
+ db.close();
3735
+ if (rows.length === 0) {
3736
+ return { success: true, output: `No episodes found for "${query}".`, durationMs: performance.now() - start2 };
3737
+ }
3738
+ const lines = rows.map((r2) => `[${r2.tool_name ?? r2.modality}] (importance: ${r2.importance}, decay: ${r2.decay_class})
3739
+ ${r2.content.slice(0, 200)}`);
3740
+ return {
3741
+ success: true,
3742
+ output: `Episode search for "${query}" (${rows.length} results):
3743
+
3744
+ ${lines.join("\n\n")}`,
3745
+ durationMs: performance.now() - start2
3746
+ };
3747
+ }
3748
+ if (mode === "graph" && pathExists(kgDbPath)) {
3749
+ const db = new Database2(kgDbPath, { readonly: true });
3750
+ const queryLower = query.toLowerCase();
3751
+ const nodes = db.prepare(`
3752
+ SELECT * FROM kg_nodes WHERE text LIKE ? ORDER BY mention_count DESC LIMIT 20
3753
+ `).all(`%${queryLower}%`);
3754
+ const episodeIds = /* @__PURE__ */ new Set();
3755
+ const nodeInfo = [];
3756
+ for (const node of nodes.slice(0, 5)) {
3757
+ const edges = db.prepare(`
3758
+ SELECT * FROM kg_edges WHERE (src_id = ? OR dst_id = ?) AND valid_until IS NULL
3759
+ `).all(node.id, node.id);
3760
+ nodeInfo.push(`${node.text} (${node.node_type}, ${node.mention_count} mentions, ${edges.length} edges)`);
3761
+ for (const edge of edges) {
3762
+ if (edge.source_episode_id)
3763
+ episodeIds.add(edge.source_episode_id);
3764
+ }
3765
+ }
3766
+ db.close();
3767
+ const epLines = [];
3768
+ if (episodeIds.size > 0 && pathExists(epDbPath)) {
3769
+ const epDb = new Database2(epDbPath, { readonly: true });
3770
+ for (const epId of [...episodeIds].slice(0, maxResults)) {
3771
+ const ep = epDb.prepare("SELECT * FROM episodes WHERE id = ?").get(epId);
3772
+ if (ep)
3773
+ epLines.push(`[${ep.tool_name ?? ep.modality}] ${ep.content.slice(0, 150)}`);
3774
+ }
3775
+ epDb.close();
3776
+ }
3777
+ const output = [
3778
+ `Knowledge graph search for "${query}":`,
3779
+ `
3780
+ Nodes found: ${nodes.length}`,
3781
+ nodeInfo.length > 0 ? nodeInfo.map((n2) => ` - ${n2}`).join("\n") : " (no matching nodes)",
3782
+ epLines.length > 0 ? `
3783
+ Linked episodes (${epLines.length}):
3784
+ ${epLines.map((l2) => ` ${l2}`).join("\n")}` : ""
3785
+ ].filter(Boolean).join("\n");
3786
+ return { success: true, output, durationMs: performance.now() - start2 };
3787
+ }
3788
+ } catch {
3789
+ }
3790
+ }
3706
3791
  try {
3707
3792
  const allEntries = await this.loadAllMemories();
3708
3793
  if (allEntries.length === 0) {
@@ -265232,9 +265317,214 @@ var init_gistCompressor = __esm({
265232
265317
  }
265233
265318
  });
265234
265319
 
265320
+ // packages/memory/dist/pprRetrieval.js
265321
+ function extractQueryEntities(query) {
265322
+ const entities = [];
265323
+ const seen = /* @__PURE__ */ new Set();
265324
+ const add2 = (text) => {
265325
+ const normalized = text.trim().toLowerCase();
265326
+ if (normalized.length < 2 || seen.has(normalized))
265327
+ return;
265328
+ seen.add(normalized);
265329
+ entities.push(text.trim());
265330
+ };
265331
+ const filePattern = /(?:[\w./-]+\/[\w./-]+|[\w-]+\.\w{1,5})\b/g;
265332
+ for (const match of query.matchAll(filePattern)) {
265333
+ if (match[0].includes(".") || match[0].includes("/"))
265334
+ add2(match[0]);
265335
+ }
265336
+ const errorPattern = /\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b|\b\w+(?:Error|Exception|Failed|Failure)\b/g;
265337
+ for (const match of query.matchAll(errorPattern))
265338
+ add2(match[0]);
265339
+ 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;
265340
+ for (const match of query.matchAll(toolPattern))
265341
+ add2(match[0]);
265342
+ const quotedPattern = /["']([^"']{2,50})["']/g;
265343
+ for (const match of query.matchAll(quotedPattern))
265344
+ add2(match[1]);
265345
+ const STOP_WORDS2 = /* @__PURE__ */ new Set([
265346
+ "the",
265347
+ "and",
265348
+ "for",
265349
+ "was",
265350
+ "are",
265351
+ "not",
265352
+ "but",
265353
+ "had",
265354
+ "has",
265355
+ "have",
265356
+ "this",
265357
+ "that",
265358
+ "with",
265359
+ "from",
265360
+ "what",
265361
+ "when",
265362
+ "where",
265363
+ "how",
265364
+ "why",
265365
+ "which",
265366
+ "who",
265367
+ "did",
265368
+ "does",
265369
+ "can",
265370
+ "could",
265371
+ "would",
265372
+ "should",
265373
+ "will",
265374
+ "been",
265375
+ "being",
265376
+ "were",
265377
+ "about",
265378
+ "than",
265379
+ "then",
265380
+ "also",
265381
+ "just",
265382
+ "more",
265383
+ "some",
265384
+ "only",
265385
+ "into",
265386
+ "over"
265387
+ ]);
265388
+ const words = query.split(/\s+/).filter((w) => w.length >= 4 && !STOP_WORDS2.has(w.toLowerCase()));
265389
+ for (const word2 of words) {
265390
+ if (/^[A-Z]/.test(word2) || /[._-]/.test(word2) || /\d/.test(word2)) {
265391
+ add2(word2.replace(/[.,;:!?]+$/, ""));
265392
+ }
265393
+ }
265394
+ return entities;
265395
+ }
265396
+ function personalizedPageRank(graph, seedNodeIds, config) {
265397
+ const cfg = { ...DEFAULT_CONFIG5, ...config };
265398
+ const scores = /* @__PURE__ */ new Map();
265399
+ if (seedNodeIds.length === 0)
265400
+ return scores;
265401
+ const nodeCount = graph.nodeCount();
265402
+ if (nodeCount === 0)
265403
+ return scores;
265404
+ const teleportProb = 1 / seedNodeIds.length;
265405
+ const seedSet = new Set(seedNodeIds);
265406
+ const allNodes = /* @__PURE__ */ new Set();
265407
+ for (const seedId of seedNodeIds) {
265408
+ allNodes.add(seedId);
265409
+ const neighbors2 = graph.neighbors(seedId);
265410
+ for (const { node } of neighbors2) {
265411
+ allNodes.add(node.id);
265412
+ const hop2 = graph.neighbors(node.id);
265413
+ for (const { node: n2 } of hop2)
265414
+ allNodes.add(n2.id);
265415
+ }
265416
+ }
265417
+ for (const nodeId of allNodes) {
265418
+ scores.set(nodeId, seedSet.has(nodeId) ? teleportProb : 0);
265419
+ }
265420
+ for (let iter = 0; iter < cfg.maxIterations; iter++) {
265421
+ const newScores = /* @__PURE__ */ new Map();
265422
+ let maxDelta = 0;
265423
+ for (const nodeId of allNodes) {
265424
+ const teleport = seedSet.has(nodeId) ? (1 - cfg.damping) * teleportProb : 0;
265425
+ let walkSum = 0;
265426
+ const inEdges = graph.currentEdges(nodeId);
265427
+ for (const edge of inEdges) {
265428
+ const neighborId = edge.srcId === nodeId ? edge.dstId : edge.srcId;
265429
+ const neighborScore = scores.get(neighborId) ?? 0;
265430
+ const neighborOutDegree = graph.currentEdges(neighborId).length || 1;
265431
+ walkSum += neighborScore / neighborOutDegree;
265432
+ }
265433
+ const newScore = teleport + cfg.damping * walkSum;
265434
+ newScores.set(nodeId, newScore);
265435
+ const delta = Math.abs(newScore - (scores.get(nodeId) ?? 0));
265436
+ if (delta > maxDelta)
265437
+ maxDelta = delta;
265438
+ }
265439
+ for (const [id, score] of newScores)
265440
+ scores.set(id, score);
265441
+ if (maxDelta < cfg.convergenceThreshold) {
265442
+ return scores;
265443
+ }
265444
+ }
265445
+ return scores;
265446
+ }
265447
+ function retrieveByPPR(query, graph, episodeStore, config) {
265448
+ const cfg = { ...DEFAULT_CONFIG5, ...config };
265449
+ const queryEntities = extractQueryEntities(query);
265450
+ if (queryEntities.length === 0) {
265451
+ return { episodes: [], queryEntities: [], seedNodes: [], iterations: 0 };
265452
+ }
265453
+ const seedNodes = [];
265454
+ for (const entity of queryEntities) {
265455
+ const node = graph.findNode(entity);
265456
+ if (node) {
265457
+ seedNodes.push(node.id);
265458
+ continue;
265459
+ }
265460
+ for (const nodeType of ["file", "error", "tool", "entity", "event", "concept", "person"]) {
265461
+ const candidates = graph.nodesByType(nodeType, 50);
265462
+ for (const candidate of candidates) {
265463
+ if (candidate.text.toLowerCase().includes(entity.toLowerCase()) || entity.toLowerCase().includes(candidate.text.toLowerCase())) {
265464
+ seedNodes.push(candidate.id);
265465
+ break;
265466
+ }
265467
+ }
265468
+ if (seedNodes.length > queryEntities.indexOf(entity))
265469
+ break;
265470
+ }
265471
+ }
265472
+ if (seedNodes.length === 0) {
265473
+ return { episodes: [], queryEntities, seedNodes: [], iterations: 0 };
265474
+ }
265475
+ const pprScores = personalizedPageRank(graph, seedNodes, cfg);
265476
+ const episodeScores = /* @__PURE__ */ new Map();
265477
+ for (const [nodeId, score] of pprScores) {
265478
+ if (score < 1e-3)
265479
+ continue;
265480
+ const node = graph.getNode(nodeId);
265481
+ if (!node)
265482
+ continue;
265483
+ const edges = graph.currentEdges(nodeId);
265484
+ for (const edge of edges) {
265485
+ if (edge.sourceEpisodeId) {
265486
+ const existing = episodeScores.get(edge.sourceEpisodeId);
265487
+ if (existing) {
265488
+ existing.score += score;
265489
+ existing.matchedNodes.push(node.text);
265490
+ } else {
265491
+ episodeScores.set(edge.sourceEpisodeId, { score, matchedNodes: [node.text] });
265492
+ }
265493
+ }
265494
+ }
265495
+ }
265496
+ const ranked = [...episodeScores.entries()].sort((a2, b) => b[1].score - a2[1].score).slice(0, cfg.topK);
265497
+ const episodes = [];
265498
+ for (const [epId, { score, matchedNodes }] of ranked) {
265499
+ const episode = episodeStore.get(epId);
265500
+ if (episode) {
265501
+ episodes.push({ episode, pprScore: score, matchedNodes: [...new Set(matchedNodes)] });
265502
+ }
265503
+ }
265504
+ return {
265505
+ episodes,
265506
+ queryEntities,
265507
+ seedNodes,
265508
+ iterations: cfg.maxIterations
265509
+ };
265510
+ }
265511
+ var DEFAULT_CONFIG5;
265512
+ var init_pprRetrieval = __esm({
265513
+ "packages/memory/dist/pprRetrieval.js"() {
265514
+ "use strict";
265515
+ init_zettelkasten();
265516
+ DEFAULT_CONFIG5 = {
265517
+ damping: 0.5,
265518
+ maxIterations: 50,
265519
+ convergenceThreshold: 1e-6,
265520
+ topK: 10
265521
+ };
265522
+ }
265523
+ });
265524
+
265235
265525
  // packages/memory/dist/embeddings.js
265236
265526
  async function generateEmbedding(text, config) {
265237
- const cfg = { ...DEFAULT_CONFIG5, ...config };
265527
+ const cfg = { ...DEFAULT_CONFIG6, ...config };
265238
265528
  try {
265239
265529
  const url = `${cfg.baseUrl}/api/embed`;
265240
265530
  const resp = await fetch(url, {
@@ -265269,7 +265559,7 @@ async function generateEmbedding(text, config) {
265269
265559
  }
265270
265560
  }
265271
265561
  async function checkEmbeddingAvailable(config) {
265272
- const cfg = { ...DEFAULT_CONFIG5, ...config };
265562
+ const cfg = { ...DEFAULT_CONFIG6, ...config };
265273
265563
  try {
265274
265564
  const resp = await fetch(`${cfg.baseUrl}/api/tags`, {
265275
265565
  signal: AbortSignal.timeout(5e3)
@@ -265296,11 +265586,11 @@ async function pullEmbeddingModel(baseUrl, model, timeoutMs) {
265296
265586
  return false;
265297
265587
  }
265298
265588
  }
265299
- var DEFAULT_CONFIG5;
265589
+ var DEFAULT_CONFIG6;
265300
265590
  var init_embeddings = __esm({
265301
265591
  "packages/memory/dist/embeddings.js"() {
265302
265592
  "use strict";
265303
- DEFAULT_CONFIG5 = {
265593
+ DEFAULT_CONFIG6 = {
265304
265594
  baseUrl: "http://localhost:11434",
265305
265595
  model: "nomic-embed-text",
265306
265596
  timeoutMs: 3e4
@@ -265332,6 +265622,7 @@ var init_dist7 = __esm({
265332
265622
  init_temporalGraph();
265333
265623
  init_zettelkasten();
265334
265624
  init_gistCompressor();
265625
+ init_pprRetrieval();
265335
265626
  init_embeddings();
265336
265627
  init_proceduralMemoryStore();
265337
265628
  }
@@ -267000,6 +267291,21 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
267000
267291
  if (compacted.length < prevLen)
267001
267292
  recentToolResults.clear();
267002
267293
  }
267294
+ if (turn > 0 && turn % 3 === 0 && this._temporalGraph && this._episodeStore) {
267295
+ try {
267296
+ const taskGoal = this._taskState.goal || task.slice(0, 200);
267297
+ const pprResult = retrieveByPPR(taskGoal, this._temporalGraph, this._episodeStore, { topK: 3 });
267298
+ if (pprResult.episodes.length > 0) {
267299
+ const memoryLines = pprResult.episodes.map(({ episode, pprScore, matchedNodes }) => `- [${episode.toolName ?? episode.modality}] ${episode.content.slice(0, 120)} (via: ${matchedNodes.slice(0, 2).join(", ")})`);
267300
+ compacted.push({
267301
+ role: "system",
267302
+ content: `[Associative Memory \u2014 related prior experience]
267303
+ ${memoryLines.join("\n")}`
267304
+ });
267305
+ }
267306
+ } catch {
267307
+ }
267308
+ }
267003
267309
  if (this.options.environmentProvider) {
267004
267310
  try {
267005
267311
  const envStr = this.options.environmentProvider();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.162",
3
+ "version": "0.187.163",
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",