open-agents-ai 0.187.153 → 0.187.155

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 +209 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -264709,6 +264709,19 @@ var init_agenticRunner = __esm({
264709
264709
  // WO-KG-15
264710
264710
  _retrievalContextCache = null;
264711
264711
  // WO-KG-15: cache per-run
264712
+ // ── WO-NC-07: Error pattern learning → pre-action guidance injection ──
264713
+ // Records error patterns (tool + error signature → learned guidance).
264714
+ // When the same tool+context is about to be called again, injects the
264715
+ // learned guidance BEFORE execution to prevent repeating the same mistake.
264716
+ //
264717
+ // Structure: Map<errorSignature, { count, guidance, lastSeen }>
264718
+ // errorSignature = `${toolName}:${errorType}` (e.g. "file_read:ENOENT", "shell:permission_denied")
264719
+ //
264720
+ // Research: Kumaran et al. (2016) — complementary learning systems
264721
+ // Fast learning from errors → immediate behavioral change
264722
+ _errorPatterns = /* @__PURE__ */ new Map();
264723
+ _errorGuidanceInjected = /* @__PURE__ */ new Set();
264724
+ // prevent duplicate injection per turn
264712
264725
  _loopBlockedTools;
264713
264726
  // Loop intervention: tools removed from schema
264714
264727
  // -- Session Checkpointing (Priority 5) --
@@ -265234,6 +265247,18 @@ Respond with your assessment, then take action.`;
265234
265247
  const toolCallLog = [];
265235
265248
  this.aborted = false;
265236
265249
  this._paused = false;
265250
+ try {
265251
+ const fs4 = await import("node:fs");
265252
+ const path5 = await import("node:path");
265253
+ const patternsFile = path5.join(process.cwd(), ".oa", "error-patterns.json");
265254
+ if (fs4.existsSync(patternsFile)) {
265255
+ const saved = JSON.parse(fs4.readFileSync(patternsFile, "utf8"));
265256
+ for (const [sig, pattern] of Object.entries(saved)) {
265257
+ this._errorPatterns.set(sig, pattern);
265258
+ }
265259
+ }
265260
+ } catch {
265261
+ }
265237
265262
  this._pauseResolve = null;
265238
265263
  this.pendingUserMessages.length = 0;
265239
265264
  this._taskState = {
@@ -265520,8 +265545,19 @@ ${top.map((t2) => `- ${t2.name}: ${t2.desc}`).join("\n")}`
265520
265545
  const turnBudget = turnTier === "small" ? 5 : turnTier === "medium" ? 8 : 0;
265521
265546
  if (turnBudget > 0 && turn > 0 && turn % turnBudget === 0) {
265522
265547
  const repetitionRatio = this.detectRepetition(toolCallLog);
265548
+ const recentToolResults2 = messages2.slice(-16).filter((m2) => m2.role === "tool" && typeof m2.content === "string");
265549
+ const recentErrors = recentToolResults2.filter((m2) => /error|failed|not found|permission denied/i.test(String(m2.content))).length;
265550
+ const errorRatio = recentToolResults2.length > 0 ? recentErrors / recentToolResults2.length : 0;
265523
265551
  const noProgress = this._taskState.completedSteps.length === 0 && turn >= turnBudget;
265524
265552
  const isLooping = repetitionRatio > 0.4;
265553
+ const highArousal = errorRatio > 0.5 || isLooping;
265554
+ if (highArousal && !isLooping && !noProgress) {
265555
+ this.pendingUserMessages.push(`[COGNITIVE STATE: HIGH AROUSAL] Recent error rate is ${Math.round(errorRatio * 100)}%. Your current approach may be hitting a wall. Consider:
265556
+ 1. Read related files you haven't examined yet
265557
+ 2. Search for similar patterns in the codebase
265558
+ 3. Check if your assumptions about the API/framework are correct
265559
+ State what you've learned from the errors before trying again.`);
265560
+ }
265525
265561
  if (isLooping || noProgress) {
265526
265562
  const progress = this._taskState.completedSteps.slice(-3).join("; ");
265527
265563
  const failures = this._taskState.failedApproaches.slice(-2).join("; ");
@@ -265792,10 +265828,18 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
265792
265828
  const executeSingle = async (tc) => {
265793
265829
  if (this.aborted)
265794
265830
  return null;
265831
+ if (this._errorPatterns.size > 0) {
265832
+ for (const [sig, pattern] of this._errorPatterns) {
265833
+ if (pattern.tool === tc.name && pattern.count >= 2 && !this._errorGuidanceInjected.has(sig)) {
265834
+ this._errorGuidanceInjected.add(sig);
265835
+ this.pendingUserMessages.push(`[LEARNED FROM EXPERIENCE] ${tc.name} has failed ${pattern.count} times with "${pattern.errorType}" errors. Guidance: ${pattern.guidance}`);
265836
+ }
265837
+ }
265838
+ }
265795
265839
  const toolStart = performance.now();
265796
265840
  toolCallCount++;
265797
265841
  const argsKey = Object.entries(tc.arguments ?? {}).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 80) : JSON.stringify(v)}`).join(",");
265798
- toolCallLog.push({ name: tc.name, argsKey });
265842
+ toolCallLog.push({ name: tc.name, argsKey, turn, timestampMs: Date.now() });
265799
265843
  const budgetRemaining = toolCallBudget.get(tc.name);
265800
265844
  if (budgetRemaining !== void 0) {
265801
265845
  if (budgetRemaining <= 0) {
@@ -265956,6 +266000,58 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
265956
266000
  }
265957
266001
  }
265958
266002
  }
266003
+ if (!result.success && result.error) {
266004
+ const errorText = result.error;
266005
+ let errorType = "unknown";
266006
+ if (/not found|ENOENT|no such file/i.test(errorText))
266007
+ errorType = "not_found";
266008
+ else if (/permission|denied|EACCES/i.test(errorText))
266009
+ errorType = "permission";
266010
+ else if (/timeout|timed out|ETIMEDOUT/i.test(errorText))
266011
+ errorType = "timeout";
266012
+ else if (/busy|EBUSY|in use/i.test(errorText))
266013
+ errorType = "busy";
266014
+ else if (/syntax|parse|invalid/i.test(errorText))
266015
+ errorType = "syntax";
266016
+ else if (/connect|ECONNREFUSED|network/i.test(errorText))
266017
+ errorType = "network";
266018
+ const sig = `${tc.name}:${errorType}`;
266019
+ const existing = this._errorPatterns.get(sig);
266020
+ const count = (existing?.count ?? 0) + 1;
266021
+ let guidance = "";
266022
+ switch (errorType) {
266023
+ case "not_found":
266024
+ guidance = `File/resource not found. Before retrying: use list_directory or find_files to verify the path exists. Common causes: wrong directory, typo in filename, file was moved/renamed.`;
266025
+ break;
266026
+ case "permission":
266027
+ guidance = `Permission denied. The command may need sudo/elevated privileges. Use shell with sudo, or check file ownership with 'ls -la'.`;
266028
+ break;
266029
+ case "timeout":
266030
+ guidance = `Operation timed out. The target may be unreachable or overloaded. Try: shorter timeout, different endpoint, or verify network connectivity first.`;
266031
+ break;
266032
+ case "busy":
266033
+ guidance = `Resource is busy/locked. Another process may be using it. Check with 'lsof' or 'fuser', or wait and retry.`;
266034
+ break;
266035
+ case "syntax":
266036
+ guidance = `Syntax/parse error. Review the command or input for typos, missing quotes, or incorrect parameter format.`;
266037
+ break;
266038
+ case "network":
266039
+ guidance = `Network error. Check connectivity: ping the target, verify the URL/host is correct, check if a proxy is needed.`;
266040
+ break;
266041
+ default:
266042
+ guidance = `This tool failed previously with a similar error. Review the error message carefully and adjust your approach before retrying.`;
266043
+ }
266044
+ this._errorPatterns.set(sig, { count, guidance, lastSeen: Date.now(), tool: tc.name, errorType });
266045
+ const lastLog = toolCallLog[toolCallLog.length - 1];
266046
+ if (lastLog) {
266047
+ lastLog.success = false;
266048
+ lastLog.outputPreview = errorText.slice(0, 100);
266049
+ }
266050
+ } else if (result.success) {
266051
+ const lastLog = toolCallLog[toolCallLog.length - 1];
266052
+ if (lastLog)
266053
+ lastLog.success = true;
266054
+ }
265959
266055
  if (isReadLike && result.success) {
265960
266056
  recentToolResults.set(toolFingerprint, (result.output ?? "").slice(0, 2e3));
265961
266057
  if (recentToolResults.size > 50) {
@@ -266629,6 +266725,118 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
266629
266725
  success: completed,
266630
266726
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
266631
266727
  });
266728
+ if (this._errorPatterns.size > 0) {
266729
+ try {
266730
+ const fs4 = await import("node:fs");
266731
+ const path5 = await import("node:path");
266732
+ const patternsFile = path5.join(process.cwd(), ".oa", "error-patterns.json");
266733
+ fs4.mkdirSync(path5.join(process.cwd(), ".oa"), { recursive: true });
266734
+ let existing = {};
266735
+ try {
266736
+ existing = JSON.parse(fs4.readFileSync(patternsFile, "utf8"));
266737
+ } catch {
266738
+ }
266739
+ for (const [sig, pattern] of this._errorPatterns) {
266740
+ const prev = existing[sig];
266741
+ existing[sig] = {
266742
+ ...pattern,
266743
+ count: (prev?.count ?? 0) + pattern.count,
266744
+ firstSeen: prev?.firstSeen ?? pattern.lastSeen
266745
+ };
266746
+ }
266747
+ fs4.writeFileSync(patternsFile, JSON.stringify(existing, null, 2));
266748
+ } catch {
266749
+ }
266750
+ }
266751
+ try {
266752
+ const extractPaths = (entries, toolNames) => {
266753
+ return [...new Set(entries.filter((tc) => toolNames.includes(tc.name)).map((tc) => {
266754
+ const pathMatch = tc.argsKey.match(/path=([^,]+)/);
266755
+ return pathMatch?.[1] || "";
266756
+ }).filter(Boolean))];
266757
+ };
266758
+ const consolidation = {
266759
+ sessionId: this._sessionId,
266760
+ task: task.slice(0, 500),
266761
+ outcome: completed ? "success" : this.aborted ? "aborted" : "timeout",
266762
+ turns: messages2.filter((m2) => m2.role === "assistant").length,
266763
+ toolsUsed: [...new Set(toolCallLog.map((tc) => tc.name))],
266764
+ filesModified: extractPaths(toolCallLog, ["file_write", "file_edit", "file_patch", "batch_edit"]),
266765
+ filesRead: extractPaths(toolCallLog, ["file_read"]),
266766
+ totalToolCalls: toolCallLog.length,
266767
+ durationMs,
266768
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
266769
+ };
266770
+ const fs4 = await import("node:fs");
266771
+ const path5 = await import("node:path");
266772
+ const consolidDir = path5.join(process.cwd(), ".oa", "consolidations");
266773
+ fs4.mkdirSync(consolidDir, { recursive: true });
266774
+ fs4.writeFileSync(path5.join(consolidDir, `${this._sessionId}.json`), JSON.stringify(consolidation, null, 2));
266775
+ const provenanceDir = path5.join(process.cwd(), ".oa", "provenance");
266776
+ fs4.mkdirSync(provenanceDir, { recursive: true });
266777
+ const provenanceGraph = {
266778
+ sessionId: this._sessionId,
266779
+ task: task.slice(0, 500),
266780
+ outcome: consolidation.outcome,
266781
+ timestamp: consolidation.timestamp,
266782
+ // Full action trace — every tool call with sequence ordering
266783
+ actions: toolCallLog.map((tc, idx) => ({
266784
+ id: `${this._sessionId}-${idx}`,
266785
+ tool: tc.name,
266786
+ args_fingerprint: tc.argsKey.slice(0, 200),
266787
+ turn: tc.turn,
266788
+ timestamp_ms: tc.timestampMs,
266789
+ // Causal links: which earlier action informed this one?
266790
+ // Heuristic: if this action reads a file that a previous action wrote, they're linked
266791
+ caused_by: toolCallLog.slice(0, idx).filter((prev) => {
266792
+ const prevPath = prev.argsKey.match(/path=([^,]+)/)?.[1];
266793
+ const thisPath = tc.argsKey.match(/path=([^,]+)/)?.[1];
266794
+ return prevPath && thisPath && prevPath === thisPath && ["file_write", "file_edit"].includes(prev.name) && ["file_read", "grep_search"].includes(tc.name);
266795
+ }).map((_, prevIdx) => `${this._sessionId}-${prevIdx}`)
266796
+ })),
266797
+ // File dependency graph: which files were read before being modified?
266798
+ file_provenance: (() => {
266799
+ const readTurns = /* @__PURE__ */ new Map();
266800
+ const writeTurns = /* @__PURE__ */ new Map();
266801
+ for (const tc of toolCallLog) {
266802
+ const filePath = tc.argsKey.match(/path=([^,]+)/)?.[1];
266803
+ if (!filePath)
266804
+ continue;
266805
+ if (tc.name === "file_read" && !readTurns.has(filePath))
266806
+ readTurns.set(filePath, tc.turn ?? 0);
266807
+ if (["file_write", "file_edit"].includes(tc.name))
266808
+ writeTurns.set(filePath, tc.turn ?? 0);
266809
+ }
266810
+ const deps = [];
266811
+ for (const [file, writeTurn] of writeTurns) {
266812
+ const readTurn = readTurns.get(file);
266813
+ if (readTurn !== void 0 && readTurn < writeTurn) {
266814
+ deps.push({ file, read_turn: readTurn, write_turn: writeTurn });
266815
+ }
266816
+ }
266817
+ return deps;
266818
+ })()
266819
+ };
266820
+ fs4.writeFileSync(path5.join(provenanceDir, `${this._sessionId}.json`), JSON.stringify(provenanceGraph, null, 2));
266821
+ if (completed && this.tools.has("memory_write")) {
266822
+ const memTool = this.tools.get("memory_write");
266823
+ const lessonContent = `Task "${task.slice(0, 100)}" completed successfully. Tools: ${consolidation.toolsUsed.join(", ")}. Files: ${consolidation.filesModified.slice(0, 3).join(", ")}. Duration: ${Math.round(durationMs / 1e3)}s, ${consolidation.turns} turns.`;
266824
+ try {
266825
+ await memTool.execute({
266826
+ topic: "task_lessons",
266827
+ key: this._sessionId,
266828
+ value: lessonContent
266829
+ });
266830
+ } catch {
266831
+ }
266832
+ }
266833
+ this.emit({
266834
+ type: "status",
266835
+ content: `Task consolidation: ${consolidation.outcome}, ${consolidation.toolsUsed.length} tools, ${consolidation.filesModified.length} files modified`,
266836
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
266837
+ });
266838
+ } catch {
266839
+ }
266632
266840
  this._hookManager.runSessionHook("session_end", this._sessionId);
266633
266841
  try {
266634
266842
  const fs4 = await import("node:fs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.153",
3
+ "version": "0.187.155",
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",