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 +185 -17
- package/package.json +1 -1
- package/prompts/agentic/system-medium.md +2 -0
- package/prompts/agentic/system-small.md +2 -0
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
|
-
${
|
|
260209
|
-
tokenEstimate: Math.ceil(
|
|
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(
|
|
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
|
-
|
|
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 (["
|
|
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
|
|
262929
|
-
const
|
|
262930
|
-
const
|
|
262931
|
-
...allTools.filter((t2) => CORE_TOOLS2.has(t2.name)),
|
|
262932
|
-
...
|
|
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
|
|
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
|
-
|
|
263047
|
+
const compressDesc = tier === "small" || tier === "medium";
|
|
263048
|
+
const defs = dedupedInline.map((tool) => ({
|
|
262942
263049
|
type: "function",
|
|
262943
|
-
function: {
|
|
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
|
@@ -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].
|