open-agents-ai 0.101.5 → 0.101.7
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 +282 -34
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5952,35 +5952,35 @@ function discoverSkills(repoRoot) {
|
|
|
5952
5952
|
if (pkgRoot) {
|
|
5953
5953
|
const pkgFrameworks = join14(pkgRoot, "agentic", "code", "frameworks");
|
|
5954
5954
|
if (existsSync11(pkgFrameworks)) {
|
|
5955
|
-
for (const fw of safeReaddir(pkgFrameworks)) {
|
|
5955
|
+
for (const fw of safeReaddir(pkgFrameworks, true)) {
|
|
5956
5956
|
loadComponent(join14(pkgFrameworks, fw), `framework:${fw}`);
|
|
5957
5957
|
}
|
|
5958
5958
|
}
|
|
5959
5959
|
const pkgAddons = join14(pkgRoot, "agentic", "code", "addons");
|
|
5960
5960
|
if (existsSync11(pkgAddons)) {
|
|
5961
|
-
for (const addon of safeReaddir(pkgAddons)) {
|
|
5961
|
+
for (const addon of safeReaddir(pkgAddons, true)) {
|
|
5962
5962
|
loadComponent(join14(pkgAddons, addon), `addon:${addon}`);
|
|
5963
5963
|
}
|
|
5964
5964
|
}
|
|
5965
5965
|
const pkgPlugins = join14(pkgRoot, "plugins");
|
|
5966
5966
|
if (existsSync11(pkgPlugins)) {
|
|
5967
|
-
for (const plugin of safeReaddir(pkgPlugins)) {
|
|
5967
|
+
for (const plugin of safeReaddir(pkgPlugins, true)) {
|
|
5968
5968
|
loadComponent(join14(pkgPlugins, plugin), `plugin:${plugin}`);
|
|
5969
5969
|
}
|
|
5970
5970
|
}
|
|
5971
5971
|
}
|
|
5972
5972
|
if (existsSync11(frameworksDir)) {
|
|
5973
|
-
for (const framework of safeReaddir(frameworksDir)) {
|
|
5973
|
+
for (const framework of safeReaddir(frameworksDir, true)) {
|
|
5974
5974
|
loadComponent(join14(frameworksDir, framework), `framework:${framework}`);
|
|
5975
5975
|
}
|
|
5976
5976
|
}
|
|
5977
5977
|
if (existsSync11(addonsDir)) {
|
|
5978
|
-
for (const addon of safeReaddir(addonsDir)) {
|
|
5978
|
+
for (const addon of safeReaddir(addonsDir, true)) {
|
|
5979
5979
|
loadComponent(join14(addonsDir, addon), `addon:${addon}`);
|
|
5980
5980
|
}
|
|
5981
5981
|
}
|
|
5982
5982
|
if (existsSync11(pluginsDir)) {
|
|
5983
|
-
for (const plugin of safeReaddir(pluginsDir)) {
|
|
5983
|
+
for (const plugin of safeReaddir(pluginsDir, true)) {
|
|
5984
5984
|
loadComponent(join14(pluginsDir, plugin), `plugin:${plugin}`);
|
|
5985
5985
|
}
|
|
5986
5986
|
}
|
|
@@ -6025,9 +6025,10 @@ function buildSkillsSummary(skills) {
|
|
|
6025
6025
|
lines.push("Use `skill_execute` with a skill name to load its full instructions when a task matches a trigger pattern.");
|
|
6026
6026
|
return lines.join("\n");
|
|
6027
6027
|
}
|
|
6028
|
-
function safeReaddir(dir) {
|
|
6028
|
+
function safeReaddir(dir, dirsOnly = false) {
|
|
6029
6029
|
try {
|
|
6030
|
-
|
|
6030
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
6031
|
+
return dirsOnly ? entries.filter((d) => d.isDirectory()).map((d) => d.name) : entries.map((d) => d.name);
|
|
6031
6032
|
} catch {
|
|
6032
6033
|
return [];
|
|
6033
6034
|
}
|
|
@@ -6039,18 +6040,34 @@ function loadSkillsFromDir(dir, source, out) {
|
|
|
6039
6040
|
const entries = safeReaddir(dir);
|
|
6040
6041
|
for (const entry of entries) {
|
|
6041
6042
|
const skillMd = join14(dir, entry, "SKILL.md");
|
|
6042
|
-
if (
|
|
6043
|
+
if (existsSync11(skillMd)) {
|
|
6044
|
+
const manifestEntry = manifest.get(entry);
|
|
6045
|
+
const info = {
|
|
6046
|
+
name: entry,
|
|
6047
|
+
description: manifestEntry?.description ?? parseDescription(skillMd),
|
|
6048
|
+
triggers: manifestEntry?.triggers ?? parseTriggers(skillMd),
|
|
6049
|
+
source,
|
|
6050
|
+
filePath: skillMd,
|
|
6051
|
+
compactionStrategy: parseCompactionStrategy(skillMd)
|
|
6052
|
+
};
|
|
6053
|
+
out.set(entry, info);
|
|
6043
6054
|
continue;
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6055
|
+
}
|
|
6056
|
+
if (entry.endsWith(".md") && entry !== "manifest.json") {
|
|
6057
|
+
const flatPath = join14(dir, entry);
|
|
6058
|
+
const name = entry.replace(/\.md$/, "");
|
|
6059
|
+
if (out.has(name))
|
|
6060
|
+
continue;
|
|
6061
|
+
const info = {
|
|
6062
|
+
name,
|
|
6063
|
+
description: parseDescription(flatPath),
|
|
6064
|
+
triggers: parseTriggers(flatPath),
|
|
6065
|
+
source,
|
|
6066
|
+
filePath: flatPath,
|
|
6067
|
+
compactionStrategy: parseCompactionStrategy(flatPath)
|
|
6068
|
+
};
|
|
6069
|
+
out.set(name, info);
|
|
6070
|
+
}
|
|
6054
6071
|
}
|
|
6055
6072
|
}
|
|
6056
6073
|
function loadCommandsFromDir(dir, source, out) {
|
|
@@ -16883,6 +16900,59 @@ var init_personality = __esm({
|
|
|
16883
16900
|
});
|
|
16884
16901
|
|
|
16885
16902
|
// packages/orchestrator/dist/agenticRunner.js
|
|
16903
|
+
function repairJson(raw) {
|
|
16904
|
+
if (!raw || typeof raw !== "string")
|
|
16905
|
+
return null;
|
|
16906
|
+
let s = raw.trim();
|
|
16907
|
+
if (s.startsWith("```")) {
|
|
16908
|
+
s = s.replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
|
|
16909
|
+
}
|
|
16910
|
+
const lastBrace = s.lastIndexOf("}");
|
|
16911
|
+
if (lastBrace > 0 && lastBrace < s.length - 1) {
|
|
16912
|
+
s = s.slice(0, lastBrace + 1);
|
|
16913
|
+
}
|
|
16914
|
+
if (!s.includes('"') && s.includes("'")) {
|
|
16915
|
+
s = s.replace(/'/g, '"');
|
|
16916
|
+
}
|
|
16917
|
+
s = s.replace(/([{,]\s*)([a-zA-Z_]\w*)(\s*:)/g, '$1"$2"$3');
|
|
16918
|
+
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
16919
|
+
let openBraces = 0;
|
|
16920
|
+
let openBrackets = 0;
|
|
16921
|
+
for (const ch of s) {
|
|
16922
|
+
if (ch === "{")
|
|
16923
|
+
openBraces++;
|
|
16924
|
+
else if (ch === "}")
|
|
16925
|
+
openBraces--;
|
|
16926
|
+
else if (ch === "[")
|
|
16927
|
+
openBrackets++;
|
|
16928
|
+
else if (ch === "]")
|
|
16929
|
+
openBrackets--;
|
|
16930
|
+
}
|
|
16931
|
+
while (openBrackets > 0) {
|
|
16932
|
+
s += "]";
|
|
16933
|
+
openBrackets--;
|
|
16934
|
+
}
|
|
16935
|
+
while (openBraces > 0) {
|
|
16936
|
+
s += "}";
|
|
16937
|
+
openBraces--;
|
|
16938
|
+
}
|
|
16939
|
+
s = s.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "");
|
|
16940
|
+
try {
|
|
16941
|
+
const parsed = JSON.parse(s);
|
|
16942
|
+
if (typeof parsed === "object" && parsed !== null)
|
|
16943
|
+
return parsed;
|
|
16944
|
+
} catch {
|
|
16945
|
+
}
|
|
16946
|
+
const kvPattern = /["']?(\w+)["']?\s*[:=]\s*["']([^"']*?)["']/g;
|
|
16947
|
+
const result = {};
|
|
16948
|
+
let match;
|
|
16949
|
+
let found = false;
|
|
16950
|
+
while ((match = kvPattern.exec(raw)) !== null) {
|
|
16951
|
+
result[match[1]] = match[2];
|
|
16952
|
+
found = true;
|
|
16953
|
+
}
|
|
16954
|
+
return found ? result : null;
|
|
16955
|
+
}
|
|
16886
16956
|
function getSystemPromptForTier(tier) {
|
|
16887
16957
|
switch (tier) {
|
|
16888
16958
|
case "small":
|
|
@@ -16921,6 +16991,9 @@ var init_agenticRunner = __esm({
|
|
|
16921
16991
|
goal: "",
|
|
16922
16992
|
completedSteps: [],
|
|
16923
16993
|
pendingSteps: [],
|
|
16994
|
+
currentStep: "",
|
|
16995
|
+
failedApproaches: [],
|
|
16996
|
+
nextAction: "",
|
|
16924
16997
|
modifiedFiles: /* @__PURE__ */ new Map(),
|
|
16925
16998
|
toolCallCount: 0
|
|
16926
16999
|
};
|
|
@@ -17246,6 +17319,9 @@ Respond with your assessment, then take action.`;
|
|
|
17246
17319
|
goal: task.slice(0, 500),
|
|
17247
17320
|
completedSteps: [],
|
|
17248
17321
|
pendingSteps: [],
|
|
17322
|
+
currentStep: "",
|
|
17323
|
+
failedApproaches: [],
|
|
17324
|
+
nextAction: "",
|
|
17249
17325
|
modifiedFiles: /* @__PURE__ */ new Map(),
|
|
17250
17326
|
toolCallCount: 0
|
|
17251
17327
|
};
|
|
@@ -17276,6 +17352,7 @@ TASK: ${task}` : task }
|
|
|
17276
17352
|
let bruteForceCycle = 0;
|
|
17277
17353
|
let consecutiveTextOnly = 0;
|
|
17278
17354
|
const MAX_CONSECUTIVE_TEXT_ONLY = 3;
|
|
17355
|
+
let narratedToolCallCount = 0;
|
|
17279
17356
|
for (let turn = 0; turn < this.options.maxTurns; turn++) {
|
|
17280
17357
|
if (this._paused) {
|
|
17281
17358
|
const shouldContinue = await this.waitIfPaused();
|
|
@@ -17337,6 +17414,21 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
17337
17414
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
17338
17415
|
});
|
|
17339
17416
|
}
|
|
17417
|
+
const turnTier = this.options.modelTier ?? "large";
|
|
17418
|
+
const turnBudget = turnTier === "small" ? 5 : turnTier === "medium" ? 8 : 0;
|
|
17419
|
+
if (turnBudget > 0 && turn > 0 && turn % turnBudget === 0) {
|
|
17420
|
+
const progress = this._taskState.completedSteps.slice(-3).join("; ");
|
|
17421
|
+
const failures = this._taskState.failedApproaches.slice(-2).join("; ");
|
|
17422
|
+
messages.push({
|
|
17423
|
+
role: "user",
|
|
17424
|
+
content: `[PROGRESS CHECK \u2014 Turn ${turn}]
|
|
17425
|
+
**Goal:** ${this._taskState.goal}
|
|
17426
|
+
` + (progress ? `**Done so far:** ${progress}
|
|
17427
|
+
` : "") + (failures ? `**Failed (don't repeat):** ${failures}
|
|
17428
|
+
` : "") + `**What is the ONE next step you need to take?** State it, then do it with a tool call.
|
|
17429
|
+
If you're stuck, try a completely different approach. Do NOT repeat what failed above.`
|
|
17430
|
+
});
|
|
17431
|
+
}
|
|
17340
17432
|
let compacted;
|
|
17341
17433
|
if (this._pendingCompaction) {
|
|
17342
17434
|
const strategy = this._pendingCompaction;
|
|
@@ -17430,7 +17522,14 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
17430
17522
|
});
|
|
17431
17523
|
const tool = this.tools.get(tc.name);
|
|
17432
17524
|
let result;
|
|
17433
|
-
if (
|
|
17525
|
+
if (tc.arguments && "_raw" in tc.arguments) {
|
|
17526
|
+
const rawStr = String(tc.arguments._raw).slice(0, 200);
|
|
17527
|
+
result = {
|
|
17528
|
+
success: false,
|
|
17529
|
+
output: "",
|
|
17530
|
+
error: `Your tool call had malformed JSON arguments that could not be parsed: "${rawStr}". Please call ${tc.name} again with valid JSON arguments. Make sure to use double quotes for keys and string values, and do not include trailing commas or comments.`
|
|
17531
|
+
};
|
|
17532
|
+
} else if (!tool) {
|
|
17434
17533
|
result = { success: false, output: "", error: `Unknown tool: ${tc.name}` };
|
|
17435
17534
|
} else {
|
|
17436
17535
|
try {
|
|
@@ -17476,6 +17575,24 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17476
17575
|
});
|
|
17477
17576
|
this._taskState.toolCallCount++;
|
|
17478
17577
|
const filePath = typeof tc.arguments?.path === "string" ? tc.arguments.path : "";
|
|
17578
|
+
if (tc.name === "file_read" || tc.name === "list_directory" || tc.name === "find_files" || tc.name === "grep_search") {
|
|
17579
|
+
this._taskState.currentStep = `exploring: ${filePath || String(tc.arguments?.pattern ?? tc.arguments?.path ?? "").slice(0, 60)}`;
|
|
17580
|
+
} else if (tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit") {
|
|
17581
|
+
this._taskState.currentStep = `editing: ${filePath}`;
|
|
17582
|
+
} else if (tc.name === "shell" || tc.name === "background_run") {
|
|
17583
|
+
this._taskState.currentStep = `running: ${String(tc.arguments?.command ?? "").slice(0, 60)}`;
|
|
17584
|
+
} else if (tc.name !== "task_complete") {
|
|
17585
|
+
this._taskState.currentStep = `${tc.name}(${filePath || "..."})`;
|
|
17586
|
+
}
|
|
17587
|
+
if (!result.success && tc.name !== "task_complete") {
|
|
17588
|
+
const failDesc = `${tc.name}(${filePath || "..."}): ${(result.error || "").slice(0, 100)}`;
|
|
17589
|
+
if (!this._taskState.failedApproaches.includes(failDesc)) {
|
|
17590
|
+
this._taskState.failedApproaches.push(failDesc);
|
|
17591
|
+
if (this._taskState.failedApproaches.length > 10) {
|
|
17592
|
+
this._taskState.failedApproaches.shift();
|
|
17593
|
+
}
|
|
17594
|
+
}
|
|
17595
|
+
}
|
|
17479
17596
|
if (filePath && (tc.name === "file_read" || tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit" || tc.name === "file_patch")) {
|
|
17480
17597
|
const isModify = tc.name !== "file_read";
|
|
17481
17598
|
const existing = this._fileRegistry.get(filePath);
|
|
@@ -17509,12 +17626,21 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17509
17626
|
});
|
|
17510
17627
|
}
|
|
17511
17628
|
const enoentTools = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
|
|
17512
|
-
const
|
|
17629
|
+
const fsErrorStr = result.error ?? result.output;
|
|
17630
|
+
const isEnoent = !result.success && enoentTools.has(tc.name) && /ENOENT|no such file|does not exist|not found/i.test(fsErrorStr);
|
|
17631
|
+
const isEisdir = !result.success && tc.name === "file_read" && /EISDIR|illegal operation on a directory|is a directory/i.test(fsErrorStr);
|
|
17513
17632
|
if (enoentTools.has(tc.name)) {
|
|
17514
|
-
this._recentEnoents.push(isEnoent);
|
|
17633
|
+
this._recentEnoents.push(isEnoent || isEisdir);
|
|
17515
17634
|
if (this._recentEnoents.length > 8)
|
|
17516
17635
|
this._recentEnoents.shift();
|
|
17517
17636
|
}
|
|
17637
|
+
if (isEisdir) {
|
|
17638
|
+
const failedPath = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
|
|
17639
|
+
this.pendingUserMessages.push(`[SYSTEM] "${failedPath}" is a DIRECTORY, not a file. You CANNOT use file_read on directories.
|
|
17640
|
+
Use list_directory("${failedPath}") to see its contents instead.
|
|
17641
|
+
Then use file_read on individual FILES inside it.`);
|
|
17642
|
+
this._taskState.failedApproaches.push(`file_read("${failedPath}"): EISDIR \u2014 this is a directory, use list_directory`);
|
|
17643
|
+
}
|
|
17518
17644
|
if (isEnoent) {
|
|
17519
17645
|
this._consecutiveEnoent++;
|
|
17520
17646
|
const windowEnoents = this._recentEnoents.filter(Boolean).length;
|
|
@@ -17527,7 +17653,7 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17527
17653
|
3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
|
|
17528
17654
|
4. Use the FULL relative paths shown in list_directory output \u2014 they are copy-paste ready`);
|
|
17529
17655
|
}
|
|
17530
|
-
} else {
|
|
17656
|
+
} else if (!isEisdir) {
|
|
17531
17657
|
this._consecutiveEnoent = 0;
|
|
17532
17658
|
}
|
|
17533
17659
|
return { tc, output };
|
|
@@ -17626,10 +17752,32 @@ Take a DIFFERENT action now.`
|
|
|
17626
17752
|
});
|
|
17627
17753
|
break;
|
|
17628
17754
|
}
|
|
17629
|
-
|
|
17630
|
-
|
|
17631
|
-
|
|
17632
|
-
|
|
17755
|
+
const narratedToolPattern = /(?:```(?:bash|json|sh)?\s*\n?)?\b(file_read|file_write|file_edit|shell|skill_execute|skill_list|skill_build|grep_search|find_files|web_search|web_fetch|task_complete|memory_read|memory_write|list_directory)\b[\s(=\-]/;
|
|
17756
|
+
const narratedMatch = content.match(narratedToolPattern);
|
|
17757
|
+
if (narratedMatch) {
|
|
17758
|
+
narratedToolCallCount++;
|
|
17759
|
+
const toolName = narratedMatch[1];
|
|
17760
|
+
let correction;
|
|
17761
|
+
if (narratedToolCallCount <= 1) {
|
|
17762
|
+
correction = `You wrote "${toolName}" as text but did NOT actually call the tool. Do NOT write tool names as text or code blocks. Instead, invoke the tool directly through the function calling interface. Try again \u2014 call ${toolName}() as an actual tool call.`;
|
|
17763
|
+
} else if (narratedToolCallCount <= 3) {
|
|
17764
|
+
correction = `STOP. You have written tool names as text ${narratedToolCallCount} times now. Writing "${toolName} --arg value" or "\`\`\`bash\\n${toolName}(...)\`\`\`" does NOTHING. You MUST invoke ${toolName} through the function calling API, not as text. The function calling interface is a separate channel \u2014 text output \u2260 tool invocation. Call the tool NOW through the proper interface.`;
|
|
17765
|
+
} else {
|
|
17766
|
+
correction = `CRITICAL: You cannot use ${toolName} by writing text. After ${narratedToolCallCount} attempts, it is clear this approach will not work. Try a COMPLETELY DIFFERENT tool or approach. For example: if trying skill_execute, try shell("cat .oa/skills/*/SKILL.md") instead. If trying file_read, try shell("cat path/to/file") instead.`;
|
|
17767
|
+
}
|
|
17768
|
+
messages.push({ role: "user", content: correction });
|
|
17769
|
+
this.emit({
|
|
17770
|
+
type: "status",
|
|
17771
|
+
content: `Narrated tool call #${narratedToolCallCount}: model wrote ${toolName} as text`,
|
|
17772
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
17773
|
+
});
|
|
17774
|
+
} else {
|
|
17775
|
+
narratedToolCallCount = 0;
|
|
17776
|
+
messages.push({
|
|
17777
|
+
role: "user",
|
|
17778
|
+
content: "Continue working. Use tools to read files, make changes, and run validation. Call task_complete when done."
|
|
17779
|
+
});
|
|
17780
|
+
}
|
|
17633
17781
|
}
|
|
17634
17782
|
}
|
|
17635
17783
|
let prevCycleToolCalls = toolCallCount;
|
|
@@ -17942,16 +18090,27 @@ ${marker}` : marker);
|
|
|
17942
18090
|
const parts = [];
|
|
17943
18091
|
parts.push(`### Task State`);
|
|
17944
18092
|
parts.push(`**Goal:** ${ts.goal}`);
|
|
18093
|
+
if (ts.currentStep) {
|
|
18094
|
+
parts.push(`**Currently doing:** ${ts.currentStep}`);
|
|
18095
|
+
}
|
|
17945
18096
|
if (ts.completedSteps.length > 0) {
|
|
17946
18097
|
parts.push(`**Completed:**`);
|
|
17947
18098
|
for (const s of ts.completedSteps.slice(-10))
|
|
17948
18099
|
parts.push(`- ${s}`);
|
|
17949
18100
|
}
|
|
18101
|
+
if (ts.failedApproaches.length > 0) {
|
|
18102
|
+
parts.push(`**Failed approaches (DO NOT repeat):**`);
|
|
18103
|
+
for (const s of ts.failedApproaches.slice(-5))
|
|
18104
|
+
parts.push(`- ${s}`);
|
|
18105
|
+
}
|
|
17950
18106
|
if (ts.pendingSteps.length > 0) {
|
|
17951
18107
|
parts.push(`**Pending:**`);
|
|
17952
18108
|
for (const s of ts.pendingSteps.slice(-5))
|
|
17953
18109
|
parts.push(`- ${s}`);
|
|
17954
18110
|
}
|
|
18111
|
+
if (ts.nextAction) {
|
|
18112
|
+
parts.push(`**NEXT ACTION:** ${ts.nextAction}`);
|
|
18113
|
+
}
|
|
17955
18114
|
if (ts.modifiedFiles.size > 0) {
|
|
17956
18115
|
parts.push(`**Modified files:**`);
|
|
17957
18116
|
for (const [path, action] of ts.modifiedFiles) {
|
|
@@ -18141,6 +18300,10 @@ ${tail}`;
|
|
|
18141
18300
|
content: `Compacted ${middle.length} messages${strategyLabel}${forceLabel}${previousSummary ? " (progressive)" : ""} | ~${preTokens.toLocaleString()} \u2192 ~${postTokens.toLocaleString()} tokens (saved ~${savedTokens.toLocaleString()})`,
|
|
18142
18301
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18143
18302
|
});
|
|
18303
|
+
const tier = this.options.modelTier ?? "large";
|
|
18304
|
+
if (tier === "small" || tier === "medium") {
|
|
18305
|
+
this._taskState.nextAction = this.inferNextAction(recent);
|
|
18306
|
+
}
|
|
18144
18307
|
const enrichments = [combinedSummary];
|
|
18145
18308
|
const taskStateStr = this.formatTaskState();
|
|
18146
18309
|
if (taskStateStr)
|
|
@@ -18148,23 +18311,45 @@ ${tail}`;
|
|
|
18148
18311
|
const fileRegistryStr = this.formatFileRegistry();
|
|
18149
18312
|
if (fileRegistryStr)
|
|
18150
18313
|
enrichments.push(fileRegistryStr);
|
|
18151
|
-
|
|
18152
|
-
|
|
18153
|
-
|
|
18314
|
+
if (tier === "large") {
|
|
18315
|
+
const memexIndexStr = this.formatMemexIndex();
|
|
18316
|
+
if (memexIndexStr)
|
|
18317
|
+
enrichments.push(memexIndexStr);
|
|
18318
|
+
}
|
|
18154
18319
|
const fullSummary = enrichments.join("\n\n");
|
|
18155
18320
|
const goalReminder = this._taskState.goal ? `
|
|
18156
18321
|
|
|
18157
18322
|
**YOUR ACTIVE TASK:** ${this._taskState.goal}` : "";
|
|
18323
|
+
const nextActionDirective = (tier === "small" || tier === "medium") && this._taskState.nextAction ? `
|
|
18324
|
+
|
|
18325
|
+
**DO THIS NEXT:** ${this._taskState.nextAction}` : "";
|
|
18326
|
+
const toolCallingReminder = tier === "small" ? `
|
|
18327
|
+
|
|
18328
|
+
**IMPORTANT:** You MUST invoke tools through the function calling interface. Do NOT write tool names as text, markdown, or code blocks \u2014 that does nothing. Call tools using the structured tool/function API.` : "";
|
|
18158
18329
|
const compactionMsg = {
|
|
18159
18330
|
role: "system",
|
|
18160
18331
|
content: `[Context compacted${strategyLabel} \u2014 summary of earlier work]
|
|
18161
18332
|
|
|
18162
18333
|
${fullSummary}
|
|
18163
18334
|
|
|
18164
|
-
[Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}`
|
|
18335
|
+
[Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}${nextActionDirective}${toolCallingReminder}`
|
|
18165
18336
|
};
|
|
18166
18337
|
this.persistCheckpoint(fullSummary);
|
|
18167
|
-
let
|
|
18338
|
+
let narrowedHead = [...head];
|
|
18339
|
+
if (tier === "small" && head.length > 0 && typeof head[0].content === "string") {
|
|
18340
|
+
narrowedHead = [{
|
|
18341
|
+
...head[0],
|
|
18342
|
+
content: `You are a coding agent. ALWAYS call tools \u2014 NEVER reply with only text.
|
|
18343
|
+
Rules: Read before edit. Run tests after changes. Call task_complete when done.
|
|
18344
|
+
If ENOENT: call list_directory("."). Entries are RELATIVE to the listed directory.
|
|
18345
|
+
System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
|
|
18346
|
+
}, ...head.slice(1)];
|
|
18347
|
+
} else if (tier === "medium" && head.length > 0 && typeof head[0].content === "string") {
|
|
18348
|
+
const sysContent = head[0].content;
|
|
18349
|
+
const stripped = sysContent.replace(/\n\n<project-context>[\s\S]*?<\/project-context>/g, "").replace(/\n\n<memory-context>[\s\S]*?<\/memory-context>/g, "").replace(/\n\n<git-state>[\s\S]*?<\/git-state>/g, "");
|
|
18350
|
+
narrowedHead = [{ ...head[0], content: stripped }, ...head.slice(1)];
|
|
18351
|
+
}
|
|
18352
|
+
let result = [...narrowedHead, compactionMsg, ...recent];
|
|
18168
18353
|
const ctxWindow = this.options.contextWindowSize;
|
|
18169
18354
|
if (ctxWindow > 0) {
|
|
18170
18355
|
const estimateResult = (msgs) => msgs.reduce((sum, m) => {
|
|
@@ -18227,6 +18412,67 @@ ${newerSummary}` : newerSummary;
|
|
|
18227
18412
|
}
|
|
18228
18413
|
return result;
|
|
18229
18414
|
}
|
|
18415
|
+
/**
|
|
18416
|
+
* Infer what the model should do next from the most recent messages.
|
|
18417
|
+
* Analyzes the last few tool calls, errors, and assistant text to produce
|
|
18418
|
+
* a concrete, actionable directive for small models after compaction.
|
|
18419
|
+
*/
|
|
18420
|
+
inferNextAction(recentMessages) {
|
|
18421
|
+
let lastError = "";
|
|
18422
|
+
let lastToolName = "";
|
|
18423
|
+
let lastFilePath = "";
|
|
18424
|
+
let lastAssistantText = "";
|
|
18425
|
+
let lastToolSuccess = true;
|
|
18426
|
+
for (let i = recentMessages.length - 1; i >= 0; i--) {
|
|
18427
|
+
const msg = recentMessages[i];
|
|
18428
|
+
if (msg.role === "tool" && typeof msg.content === "string") {
|
|
18429
|
+
if (!lastToolName) {
|
|
18430
|
+
lastToolSuccess = !msg.content.startsWith("Error:");
|
|
18431
|
+
if (!lastToolSuccess)
|
|
18432
|
+
lastError = msg.content.slice(0, 200);
|
|
18433
|
+
}
|
|
18434
|
+
}
|
|
18435
|
+
if (msg.role === "assistant") {
|
|
18436
|
+
if (msg.tool_calls && msg.tool_calls.length > 0 && !lastToolName) {
|
|
18437
|
+
const tc = msg.tool_calls[msg.tool_calls.length - 1];
|
|
18438
|
+
lastToolName = tc.function.name;
|
|
18439
|
+
try {
|
|
18440
|
+
const args = JSON.parse(tc.function.arguments);
|
|
18441
|
+
lastFilePath = args.path || args.file || "";
|
|
18442
|
+
} catch {
|
|
18443
|
+
}
|
|
18444
|
+
}
|
|
18445
|
+
if (typeof msg.content === "string" && msg.content.trim() && !lastAssistantText) {
|
|
18446
|
+
lastAssistantText = msg.content.trim().slice(0, 200);
|
|
18447
|
+
}
|
|
18448
|
+
}
|
|
18449
|
+
}
|
|
18450
|
+
if (!lastToolSuccess && lastError) {
|
|
18451
|
+
if (/ENOENT|no such file|not found/i.test(lastError)) {
|
|
18452
|
+
return `The path "${lastFilePath}" does not exist. Call list_directory(".") to find correct paths, then try again.`;
|
|
18453
|
+
}
|
|
18454
|
+
if (/FAIL|test.*fail|assert/i.test(lastError)) {
|
|
18455
|
+
return `Tests are failing. Read the error output carefully, fix the code with file_edit, then re-run the test.`;
|
|
18456
|
+
}
|
|
18457
|
+
if (/permission denied/i.test(lastError)) {
|
|
18458
|
+
return `Permission denied on "${lastFilePath}". Try a different approach or check file permissions.`;
|
|
18459
|
+
}
|
|
18460
|
+
return `Your last ${lastToolName} call failed: ${lastError.slice(0, 100)}. Fix the issue and try a different approach.`;
|
|
18461
|
+
}
|
|
18462
|
+
if (lastToolName === "file_read") {
|
|
18463
|
+
return `You just read "${lastFilePath}". Now make the needed changes with file_edit, or if exploring, decide what to modify.`;
|
|
18464
|
+
}
|
|
18465
|
+
if (lastToolName === "file_edit" || lastToolName === "file_write") {
|
|
18466
|
+
return `You just edited "${lastFilePath}". Run tests or validation to verify your change works.`;
|
|
18467
|
+
}
|
|
18468
|
+
if (lastToolName === "shell") {
|
|
18469
|
+
return `Review the command output above. If tests passed, continue to the next subtask or call task_complete. If they failed, fix the code.`;
|
|
18470
|
+
}
|
|
18471
|
+
if (lastToolName === "grep_search" || lastToolName === "find_files") {
|
|
18472
|
+
return `You found results from search. Use what you found to read the relevant file and make changes.`;
|
|
18473
|
+
}
|
|
18474
|
+
return `Continue working on the task. Use tools to make progress \u2014 do not reply with text only.`;
|
|
18475
|
+
}
|
|
18230
18476
|
/**
|
|
18231
18477
|
* Condense a summary by keeping section headings and only first 2 items under each.
|
|
18232
18478
|
*/
|
|
@@ -18964,7 +19210,8 @@ ${transcript}`
|
|
|
18964
19210
|
try {
|
|
18965
19211
|
args = JSON.parse(tc.args);
|
|
18966
19212
|
} catch {
|
|
18967
|
-
|
|
19213
|
+
const repaired = repairJson(tc.args);
|
|
19214
|
+
args = repaired ?? { _raw: tc.args };
|
|
18968
19215
|
}
|
|
18969
19216
|
return { id: tc.id, name: tc.name, arguments: args };
|
|
18970
19217
|
}) : void 0;
|
|
@@ -19027,7 +19274,8 @@ ${transcript}`
|
|
|
19027
19274
|
try {
|
|
19028
19275
|
args = typeof fn.arguments === "string" ? JSON.parse(fn.arguments) : fn.arguments ?? {};
|
|
19029
19276
|
} catch {
|
|
19030
|
-
|
|
19277
|
+
const repaired = repairJson(fn.arguments);
|
|
19278
|
+
args = repaired ?? { _raw: fn.arguments };
|
|
19031
19279
|
}
|
|
19032
19280
|
return {
|
|
19033
19281
|
id: tc.id || crypto.randomUUID(),
|
package/package.json
CHANGED