open-agents-ai 0.187.13 → 0.187.15

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
@@ -260201,12 +260201,19 @@ ${this.options.identityInjection}
260201
260201
  });
260202
260202
  }
260203
260203
  if (this.options.dynamicContext) {
260204
+ const tier = this.options.modelTier ?? "large";
260205
+ const useXmlTags = tier === "small" || tier === "medium";
260206
+ const ctx3 = this.options.dynamicContext;
260204
260207
  sections.push({
260205
260208
  label: "c_know",
260206
- content: `
260209
+ content: useXmlTags ? `
260210
+
260211
+ <project-context>
260212
+ ${ctx3}
260213
+ </project-context>` : `
260207
260214
 
260208
- ${this.options.dynamicContext}`,
260209
- tokenEstimate: Math.ceil(this.options.dynamicContext.length / 4)
260215
+ ${ctx3}`,
260216
+ tokenEstimate: Math.ceil(ctx3.length / 4)
260210
260217
  });
260211
260218
  }
260212
260219
  const assembled = sections.map((s2) => s2.content).join("");
@@ -260278,6 +260285,49 @@ ${this.options.dynamicContext}`,
260278
260285
  repetitionWindow
260279
260286
  };
260280
260287
  }
260288
+ /**
260289
+ * WO-CE-02: Microcompact — lightweight per-turn function result clearing.
260290
+ *
260291
+ * Scans messages array and replaces old tool results with a compact marker.
260292
+ * Keeps the most recent K results intact (K scales by model tier).
260293
+ * This is NOT summarization — just replacement. No LLM call needed.
260294
+ *
260295
+ * Hannover reference: services/compact/apiMicrocompact.ts
260296
+ * Research: arXiv:2307.03172 (Lost in the Middle — recent context matters most)
260297
+ */
260298
+ microcompact(messages2) {
260299
+ const tier = this.options.modelTier ?? "large";
260300
+ const keepResults = tier === "small" ? 6 : tier === "medium" ? 10 : 20;
260301
+ const toolResultIndices = [];
260302
+ for (let i2 = 0; i2 < messages2.length; i2++) {
260303
+ if (messages2[i2].role === "tool") {
260304
+ toolResultIndices.push(i2);
260305
+ }
260306
+ }
260307
+ if (toolResultIndices.length <= keepResults)
260308
+ return;
260309
+ const clearCount = toolResultIndices.length - keepResults;
260310
+ const toClear = toolResultIndices.slice(0, clearCount);
260311
+ let cleared = 0;
260312
+ for (const idx of toClear) {
260313
+ const msg = messages2[idx];
260314
+ const content = typeof msg.content === "string" ? msg.content : "";
260315
+ if (content.startsWith("[Tool result cleared") || content.length < 100)
260316
+ continue;
260317
+ messages2[idx] = {
260318
+ ...msg,
260319
+ content: `[Tool result cleared \u2014 write down important findings from earlier results as they may be cleared]`
260320
+ };
260321
+ cleared++;
260322
+ }
260323
+ if (cleared > 0) {
260324
+ this.emit({
260325
+ type: "status",
260326
+ content: `Microcompact: cleared ${cleared} old tool result(s), keeping ${keepResults} recent`,
260327
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
260328
+ });
260329
+ }
260330
+ }
260281
260331
  /** Register a tool for the agent to use */
260282
260332
  registerTool(tool) {
260283
260333
  this.tools.set(tool.name, tool);
@@ -260751,6 +260801,7 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
260751
260801
  } catch {
260752
260802
  }
260753
260803
  }
260804
+ this.microcompact(compacted);
260754
260805
  const { maxOutputTokens: effectiveMaxTokens } = this.contextLimits();
260755
260806
  const chatRequest = {
260756
260807
  messages: compacted,
@@ -261530,6 +261581,7 @@ Integrate this guidance into your current approach. Continue working on the task
261530
261581
  } else {
261531
261582
  compactedMsgs = await this.compactMessages(messages2, this._skillCompactionStrategy ?? "default");
261532
261583
  }
261584
+ this.microcompact(compactedMsgs);
261533
261585
  const chatRequest = { messages: compactedMsgs, tools: toolDefs, temperature: this.options.temperature, maxTokens: this.options.maxTokens, timeoutMs: this.options.requestTimeoutMs };
261534
261586
  let response;
261535
261587
  try {
@@ -262044,8 +262096,11 @@ ${tail}`;
262044
262096
  }
262045
262097
  const enrichments = [combinedSummary];
262046
262098
  const taskStateStr = this.formatTaskState();
262047
- if (taskStateStr)
262048
- enrichments.push(taskStateStr);
262099
+ if (taskStateStr) {
262100
+ enrichments.push(tier === "small" || tier === "medium" ? `<task-state>
262101
+ ${taskStateStr}
262102
+ </task-state>` : taskStateStr);
262103
+ }
262049
262104
  const fileRegistryStr = this.formatFileRegistry();
262050
262105
  if (fileRegistryStr)
262051
262106
  enrichments.push(fileRegistryStr);
@@ -262082,7 +262137,12 @@ ${tail}`;
262082
262137
  **DO NOT RE-READ THESE FILES** (you already have their contents): ${readFilesList.join(", ")}. Use the information above to make progress. Reading the same file again wastes a turn.` : "";
262083
262138
  const compactionMsg = {
262084
262139
  role: "system",
262085
- content: `[Context compacted${strategyLabel} \u2014 summary of earlier work]
262140
+ // WO-CE-03: XML tags for structural clarity on small/medium models
262141
+ content: tier === "small" || tier === "medium" ? `<compaction-summary>
262142
+ ${fullSummary}
262143
+ </compaction-summary>
262144
+
262145
+ [Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${antiRepetitionReminder}${toolCallingReminder}` : `[Context compacted${strategyLabel} \u2014 summary of earlier work]
262086
262146
 
262087
262147
  ${fullSummary}
262088
262148
 
@@ -262104,6 +262164,44 @@ System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
262104
262164
  narrowedHead = [{ ...head[0], content: stripped }, ...head.slice(1)];
262105
262165
  }
262106
262166
  let result = [...narrowedHead, compactionMsg, ...recent];
262167
+ const fileRecoveryBudget = Math.floor((this.options.contextWindowSize || 32768) * 0.15);
262168
+ const maxRecoverFiles = tier === "small" ? 3 : tier === "medium" ? 4 : 5;
262169
+ const recoveredFiles = [];
262170
+ if (this._fileRegistry.size > 0) {
262171
+ const entries = Array.from(this._fileRegistry.entries()).sort((a2, b) => {
262172
+ if (a2[1].modified && !b[1].modified)
262173
+ return -1;
262174
+ if (!a2[1].modified && b[1].modified)
262175
+ return 1;
262176
+ return b[1].lastSeenTurn - a2[1].lastSeenTurn;
262177
+ }).slice(0, maxRecoverFiles);
262178
+ let recoveredTokens = 0;
262179
+ for (const [filePath, entry] of entries) {
262180
+ try {
262181
+ const { readFileSync: readFileSync52 } = await import("node:fs");
262182
+ const content = readFileSync52(filePath, "utf8");
262183
+ const tokenEst = Math.ceil(content.length / 4);
262184
+ if (recoveredTokens + tokenEst > fileRecoveryBudget)
262185
+ break;
262186
+ result.push({
262187
+ role: "system",
262188
+ content: `<recovered-file path="${filePath}" status="${entry.modified ? "modified" : "read"}">
262189
+ ${content.slice(0, 8e3)}
262190
+ </recovered-file>`
262191
+ });
262192
+ recoveredFiles.push(filePath);
262193
+ recoveredTokens += tokenEst;
262194
+ } catch {
262195
+ }
262196
+ }
262197
+ if (recoveredFiles.length > 0) {
262198
+ this.emit({
262199
+ type: "status",
262200
+ content: `Post-compaction recovery: restored ${recoveredFiles.length} file(s) (~${recoveredTokens} tokens)`,
262201
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
262202
+ });
262203
+ }
262204
+ }
262107
262205
  const ctxWindow = this.options.contextWindowSize;
262108
262206
  if (ctxWindow > 0) {
262109
262207
  const estimateResult = (msgs) => msgs.reduce((sum, m2) => {
@@ -262897,7 +262995,13 @@ ${transcript}`
262897
262995
  "grep_search",
262898
262996
  "find_files",
262899
262997
  "list_directory",
262900
- "file_explore"
262998
+ "file_explore",
262999
+ "web_search",
263000
+ "web_fetch",
263001
+ "memory_read",
263002
+ "memory_write",
263003
+ "working_notes",
263004
+ "batch_edit"
262901
263005
  ]);
262902
263006
  const taskText = (this._taskState.goal || "").toLowerCase();
262903
263007
  const taskWords = new Set(taskText.split(/\s+/).filter((w) => w.length > 2));
@@ -262919,29 +263023,93 @@ ${transcript}`
262919
263023
  if (taskText.includes(tool.name.replace(/_/g, " ")) || taskText.includes(tool.name)) {
262920
263024
  score += 10;
262921
263025
  }
262922
- if (["web_search", "web_fetch", "memory_read", "memory_write", "memory_search"].includes(tool.name)) {
263026
+ if (["explore_tools", "memory_search", "skill_list", "skill_execute", "agent", "sub_agent"].includes(tool.name)) {
262923
263027
  score += 1;
262924
263028
  }
262925
263029
  scored.push({ tool, score });
262926
263030
  }
262927
263031
  scored.sort((a2, b) => b.score - a2.score);
262928
- const maxExtra = tier === "small" ? 6 : 12;
262929
- const relevantTools = scored.slice(0, maxExtra).filter((s2) => s2.score > 0);
262930
- const selectedTools = [
262931
- ...allTools.filter((t2) => CORE_TOOLS2.has(t2.name)),
262932
- ...relevantTools.map((s2) => s2.tool)
262933
- ];
263032
+ const maxInlineExtra = tier === "small" ? 4 : 8;
263033
+ const inlineExtras = scored.slice(0, maxInlineExtra).filter((s2) => s2.score > 0);
263034
+ const inlineNames = /* @__PURE__ */ new Set([
263035
+ ...allTools.filter((t2) => CORE_TOOLS2.has(t2.name)).map((t2) => t2.name),
263036
+ ...inlineExtras.map((s2) => s2.tool.name)
263037
+ ]);
263038
+ const deferred = allTools.filter((t2) => !inlineNames.has(t2.name));
263039
+ const inlineTools = allTools.filter((t2) => inlineNames.has(t2.name));
262934
263040
  const seen = /* @__PURE__ */ new Set();
262935
- const deduped = selectedTools.filter((t2) => {
263041
+ const dedupedInline = inlineTools.filter((t2) => {
262936
263042
  if (seen.has(t2.name))
262937
263043
  return false;
262938
263044
  seen.add(t2.name);
262939
263045
  return true;
262940
263046
  });
262941
- return deduped.map((tool) => ({
263047
+ const compressDesc = tier === "small" || tier === "medium";
263048
+ const defs = dedupedInline.map((tool) => ({
262942
263049
  type: "function",
262943
- function: { name: tool.name, description: tool.description, parameters: tool.parameters }
263050
+ function: {
263051
+ name: tool.name,
263052
+ description: compressDesc ? (tool.description.split(/\.\s/)[0]?.slice(0, 120) ?? tool.description.slice(0, 120)) + "." : tool.description,
263053
+ parameters: tool.parameters
263054
+ }
262944
263055
  }));
263056
+ if (deferred.length > 0) {
263057
+ const catalog = deferred.map((t2) => {
263058
+ const shortDesc = t2.description.split(/\.\s/)[0]?.slice(0, 80) ?? t2.description.slice(0, 80);
263059
+ return `- ${t2.name}: ${shortDesc}`;
263060
+ }).join("\n");
263061
+ defs.push({
263062
+ type: "function",
263063
+ function: {
263064
+ name: "tool_search",
263065
+ description: `Search for and activate additional tools not in your current tool list. Call this when your task needs a tool you don't have. Pass a search query describing what you need.
263066
+
263067
+ Available tools (${deferred.length}):
263068
+ ${catalog}`,
263069
+ parameters: {
263070
+ type: "object",
263071
+ properties: {
263072
+ query: { type: "string", description: "Search query (tool name or capability description)" }
263073
+ },
263074
+ required: ["query"]
263075
+ }
263076
+ }
263077
+ });
263078
+ if (!this.tools.has("tool_search")) {
263079
+ this.tools.set("tool_search", {
263080
+ name: "tool_search",
263081
+ description: "Search for deferred tools",
263082
+ parameters: {},
263083
+ execute: async (args) => {
263084
+ const query = String(args["query"] ?? "").toLowerCase();
263085
+ const matches = deferred.filter((t2) => t2.name.toLowerCase().includes(query) || t2.description.toLowerCase().includes(query)).slice(0, 5);
263086
+ if (matches.length === 0) {
263087
+ return { success: false, output: "", error: `No tools matching "${query}". Try a broader search.` };
263088
+ }
263089
+ const result = matches.map((t2) => {
263090
+ const paramsStr = JSON.stringify(t2.parameters, null, 2);
263091
+ return `## ${t2.name}
263092
+ ${t2.description}
263093
+
263094
+ Parameters:
263095
+ ${paramsStr}`;
263096
+ }).join("\n\n---\n\n");
263097
+ return {
263098
+ success: true,
263099
+ output: `Found ${matches.length} tool(s). You can now call them directly:
263100
+
263101
+ ${result}`
263102
+ };
263103
+ }
263104
+ });
263105
+ }
263106
+ }
263107
+ this.emit({
263108
+ type: "status",
263109
+ content: `Tool deferral: ${dedupedInline.length} inline + ${deferred.length} deferred (${tier} tier)`,
263110
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
263111
+ });
263112
+ return defs;
262945
263113
  }
262946
263114
  // -------------------------------------------------------------------------
262947
263115
  // Transient error recovery — retry on 502, fetch failed, timeouts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.13",
3
+ "version": "0.187.15",
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",
@@ -103,6 +103,8 @@ When asked "how do you work?" or "what can you do?", answer from this list and u
103
103
 
104
104
  The <environment> block contains LIVE hardware metrics updated every turn. When asked about system specs, hardware, battery, CPU, RAM, GPU, disk space, or processes — read and report those values directly. You CAN see them.
105
105
 
106
+ When working with tool results, write down any important information you might need later in your response, as older tool results may be cleared to save context space.
107
+
106
108
  ## Calculations — Always Execute, Never Guess
107
109
 
108
110
  For ANY numerical calculation involving 2+ operations, write Python and execute it with `repl_exec` or `shell`. In-head arithmetic is error-prone across all model sizes. Python is exact.
@@ -33,6 +33,8 @@ Rules:
33
33
  - When asked "what can you do?", use explore_tools() and skill_list() to discover and report your actual capabilities. Do NOT hallucinate.
34
34
  - The <environment> block contains LIVE system metrics. When asked about hardware, battery, CPU, RAM, GPU, disk, or system info — read and report those values directly.
35
35
 
36
+ When working with tool results, write down any important information you might need later in your response, as older tool results may be cleared to save context space.
37
+
36
38
  Calculations — EXECUTE, never guess:
37
39
  - For ANY math with 2+ operations: use `repl_exec(code="print(847.50 * 0.15)")` or `shell`. Python is exact. In-head arithmetic is not.
38
40
  - Currency, percentages, statistics, dates — ALWAYS execute code. If execution fails, reason step-by-step and mark [ESTIMATED].