open-agents-ai 0.187.573 → 0.187.575
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 +297 -53
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -252600,7 +252600,9 @@ async function probeService() {
|
|
|
252600
252600
|
try {
|
|
252601
252601
|
const controller = new AbortController();
|
|
252602
252602
|
const timeout2 = setTimeout(() => controller.abort(), 3e3);
|
|
252603
|
-
const res = await fetch(`${BASE_URL}/health`, {
|
|
252603
|
+
const res = await fetch(`${BASE_URL}/health`, {
|
|
252604
|
+
signal: controller.signal
|
|
252605
|
+
});
|
|
252604
252606
|
clearTimeout(timeout2);
|
|
252605
252607
|
return res.ok;
|
|
252606
252608
|
} catch {
|
|
@@ -252610,7 +252612,10 @@ async function probeService() {
|
|
|
252610
252612
|
function findPython3() {
|
|
252611
252613
|
for (const cmd of ["python3", "python"]) {
|
|
252612
252614
|
try {
|
|
252613
|
-
const ver = execSync19(`${cmd} --version 2>&1`, {
|
|
252615
|
+
const ver = execSync19(`${cmd} --version 2>&1`, {
|
|
252616
|
+
stdio: "pipe",
|
|
252617
|
+
timeout: 5e3
|
|
252618
|
+
}).toString().trim();
|
|
252614
252619
|
if (ver.includes("Python 3"))
|
|
252615
252620
|
return cmd;
|
|
252616
252621
|
} catch {
|
|
@@ -252682,7 +252687,10 @@ async function ensureSession() {
|
|
|
252682
252687
|
});
|
|
252683
252688
|
const data = await res.json();
|
|
252684
252689
|
if (!data.ok)
|
|
252685
|
-
return {
|
|
252690
|
+
return {
|
|
252691
|
+
error: String(data.message ?? "Failed to start browser session"),
|
|
252692
|
+
sessionId: ""
|
|
252693
|
+
};
|
|
252686
252694
|
activeSessionId = data.session_id;
|
|
252687
252695
|
return { sessionId: activeSessionId };
|
|
252688
252696
|
}
|
|
@@ -252828,7 +252836,22 @@ var init_browser_action = __esm({
|
|
|
252828
252836
|
properties: {
|
|
252829
252837
|
action: {
|
|
252830
252838
|
type: "string",
|
|
252831
|
-
enum: [
|
|
252839
|
+
enum: [
|
|
252840
|
+
"navigate",
|
|
252841
|
+
"click",
|
|
252842
|
+
"click_xy",
|
|
252843
|
+
"type",
|
|
252844
|
+
"screenshot",
|
|
252845
|
+
"dom",
|
|
252846
|
+
"dom_summary",
|
|
252847
|
+
"vision_click",
|
|
252848
|
+
"scroll",
|
|
252849
|
+
"scroll_up",
|
|
252850
|
+
"scroll_down",
|
|
252851
|
+
"back",
|
|
252852
|
+
"forward",
|
|
252853
|
+
"close"
|
|
252854
|
+
],
|
|
252832
252855
|
description: "Browser action to perform. Key actions:\n- 'dom_summary': compact view of interactive elements (~1KB vs 200KB raw DOM)\n- 'vision_click': screenshot the page, use Moondream vision to find an element by description, then click it. Pass the element description in 'text' parameter (e.g. text='the login button'). This is the visual grounding loop from SeeAct.\n- 'click': click by CSS selector (fastest when you know the selector)\n- 'click_xy': click at pixel coordinates (when you have exact coords)"
|
|
252833
252856
|
},
|
|
252834
252857
|
url: {
|
|
@@ -252858,12 +252881,44 @@ var init_browser_action = __esm({
|
|
|
252858
252881
|
},
|
|
252859
252882
|
required: ["action"]
|
|
252860
252883
|
};
|
|
252884
|
+
/** TASK-CLEANUP: gracefully close the browser session when the task completes. */
|
|
252885
|
+
async cleanup() {
|
|
252886
|
+
if (activeSessionId) {
|
|
252887
|
+
try {
|
|
252888
|
+
const res = await fetch(`${BASE_URL}/session/close`, {
|
|
252889
|
+
method: "POST",
|
|
252890
|
+
headers: { "Content-Type": "application/json" },
|
|
252891
|
+
body: JSON.stringify({ sid: activeSessionId }),
|
|
252892
|
+
signal: AbortSignal.timeout(5e3)
|
|
252893
|
+
});
|
|
252894
|
+
await res.json();
|
|
252895
|
+
} catch {
|
|
252896
|
+
}
|
|
252897
|
+
activeSessionId = null;
|
|
252898
|
+
}
|
|
252899
|
+
if (serviceProcess && serviceProcess.pid && !serviceProcess.killed) {
|
|
252900
|
+
try {
|
|
252901
|
+
process.kill(-serviceProcess.pid, "SIGKILL");
|
|
252902
|
+
} catch {
|
|
252903
|
+
}
|
|
252904
|
+
try {
|
|
252905
|
+
serviceProcess.kill("SIGKILL");
|
|
252906
|
+
} catch {
|
|
252907
|
+
}
|
|
252908
|
+
serviceProcess = null;
|
|
252909
|
+
}
|
|
252910
|
+
}
|
|
252861
252911
|
async execute(args) {
|
|
252862
252912
|
const start2 = Date.now();
|
|
252863
252913
|
const action = args.action;
|
|
252864
252914
|
const launchErr = await launchService();
|
|
252865
252915
|
if (launchErr) {
|
|
252866
|
-
return {
|
|
252916
|
+
return {
|
|
252917
|
+
success: false,
|
|
252918
|
+
output: "",
|
|
252919
|
+
error: launchErr,
|
|
252920
|
+
durationMs: Date.now() - start2
|
|
252921
|
+
};
|
|
252867
252922
|
}
|
|
252868
252923
|
if (action === "close") {
|
|
252869
252924
|
if (activeSessionId) {
|
|
@@ -252873,21 +252928,41 @@ var init_browser_action = __esm({
|
|
|
252873
252928
|
}
|
|
252874
252929
|
activeSessionId = null;
|
|
252875
252930
|
}
|
|
252876
|
-
return {
|
|
252931
|
+
return {
|
|
252932
|
+
success: true,
|
|
252933
|
+
output: "Browser session closed.",
|
|
252934
|
+
durationMs: Date.now() - start2
|
|
252935
|
+
};
|
|
252877
252936
|
}
|
|
252878
252937
|
const session = await ensureSession();
|
|
252879
252938
|
if (session.error) {
|
|
252880
|
-
return {
|
|
252939
|
+
return {
|
|
252940
|
+
success: false,
|
|
252941
|
+
output: "",
|
|
252942
|
+
error: session.error,
|
|
252943
|
+
durationMs: Date.now() - start2
|
|
252944
|
+
};
|
|
252881
252945
|
}
|
|
252882
252946
|
try {
|
|
252883
252947
|
let result;
|
|
252884
252948
|
switch (action) {
|
|
252885
252949
|
case "navigate": {
|
|
252886
252950
|
if (!args.url)
|
|
252887
|
-
return {
|
|
252888
|
-
|
|
252951
|
+
return {
|
|
252952
|
+
success: false,
|
|
252953
|
+
output: "",
|
|
252954
|
+
error: "url is required for navigate action",
|
|
252955
|
+
durationMs: Date.now() - start2
|
|
252956
|
+
};
|
|
252957
|
+
result = await apiCall("/navigate", "POST", {
|
|
252958
|
+
url: args.url
|
|
252959
|
+
});
|
|
252889
252960
|
if (result.ok) {
|
|
252890
|
-
return {
|
|
252961
|
+
return {
|
|
252962
|
+
success: true,
|
|
252963
|
+
output: `Navigated to ${args.url}`,
|
|
252964
|
+
durationMs: Date.now() - start2
|
|
252965
|
+
};
|
|
252891
252966
|
}
|
|
252892
252967
|
const navMsg = String(result.message ?? "Navigation failed");
|
|
252893
252968
|
const navHint = navMsg.toLowerCase().includes("connection") || navMsg.toLowerCase().includes("refused") || navMsg.toLowerCase().includes("err_connection") ? " (the URL appears unreachable — check if the target server is running and accepting connections)" : navMsg.toLowerCase().includes("timeout") ? " (page load timed out — try again or use a different URL)" : "";
|
|
@@ -252900,10 +252975,21 @@ var init_browser_action = __esm({
|
|
|
252900
252975
|
}
|
|
252901
252976
|
case "click": {
|
|
252902
252977
|
if (!args.selector)
|
|
252903
|
-
return {
|
|
252904
|
-
|
|
252978
|
+
return {
|
|
252979
|
+
success: false,
|
|
252980
|
+
output: "",
|
|
252981
|
+
error: "selector is required for click action",
|
|
252982
|
+
durationMs: Date.now() - start2
|
|
252983
|
+
};
|
|
252984
|
+
result = await apiCall("/click", "POST", {
|
|
252985
|
+
selector: args.selector
|
|
252986
|
+
});
|
|
252905
252987
|
if (result.ok) {
|
|
252906
|
-
return {
|
|
252988
|
+
return {
|
|
252989
|
+
success: true,
|
|
252990
|
+
output: `Clicked element: ${args.selector}`,
|
|
252991
|
+
durationMs: Date.now() - start2
|
|
252992
|
+
};
|
|
252907
252993
|
}
|
|
252908
252994
|
const clickMsg = String(result.message ?? "Click failed");
|
|
252909
252995
|
return {
|
|
@@ -252915,10 +253001,19 @@ var init_browser_action = __esm({
|
|
|
252915
253001
|
}
|
|
252916
253002
|
case "click_xy": {
|
|
252917
253003
|
if (args.x == null || args.y == null)
|
|
252918
|
-
return {
|
|
253004
|
+
return {
|
|
253005
|
+
success: false,
|
|
253006
|
+
output: "",
|
|
253007
|
+
error: "x and y are required for click_xy action",
|
|
253008
|
+
durationMs: Date.now() - start2
|
|
253009
|
+
};
|
|
252919
253010
|
result = await apiCall("/click_xy", "POST", { x: args.x, y: args.y });
|
|
252920
253011
|
if (result.ok) {
|
|
252921
|
-
return {
|
|
253012
|
+
return {
|
|
253013
|
+
success: true,
|
|
253014
|
+
output: `Clicked at (${args.x}, ${args.y})`,
|
|
253015
|
+
durationMs: Date.now() - start2
|
|
253016
|
+
};
|
|
252922
253017
|
}
|
|
252923
253018
|
const xyMsg = String(result.message ?? "Click failed");
|
|
252924
253019
|
return {
|
|
@@ -252930,10 +253025,22 @@ var init_browser_action = __esm({
|
|
|
252930
253025
|
}
|
|
252931
253026
|
case "type": {
|
|
252932
253027
|
if (!args.selector || !args.text)
|
|
252933
|
-
return {
|
|
252934
|
-
|
|
253028
|
+
return {
|
|
253029
|
+
success: false,
|
|
253030
|
+
output: "",
|
|
253031
|
+
error: "selector and text are required for type action",
|
|
253032
|
+
durationMs: Date.now() - start2
|
|
253033
|
+
};
|
|
253034
|
+
result = await apiCall("/type", "POST", {
|
|
253035
|
+
selector: args.selector,
|
|
253036
|
+
text: args.text
|
|
253037
|
+
});
|
|
252935
253038
|
if (result.ok) {
|
|
252936
|
-
return {
|
|
253039
|
+
return {
|
|
253040
|
+
success: true,
|
|
253041
|
+
output: `Typed "${args.text.slice(0, 50)}" into ${args.selector}`,
|
|
253042
|
+
durationMs: Date.now() - start2
|
|
253043
|
+
};
|
|
252937
253044
|
}
|
|
252938
253045
|
const typeMsg = String(result.message ?? "Type failed");
|
|
252939
253046
|
return {
|
|
@@ -252981,16 +253088,30 @@ var init_browser_action = __esm({
|
|
|
252981
253088
|
durationMs: Date.now() - start2
|
|
252982
253089
|
};
|
|
252983
253090
|
}
|
|
252984
|
-
return {
|
|
253091
|
+
return {
|
|
253092
|
+
success: false,
|
|
253093
|
+
output: "",
|
|
253094
|
+
error: "Screenshot failed",
|
|
253095
|
+
durationMs: Date.now() - start2
|
|
253096
|
+
};
|
|
252985
253097
|
}
|
|
252986
253098
|
case "dom": {
|
|
252987
253099
|
result = await apiCall("/dom", "GET");
|
|
252988
253100
|
const dom = result.dom;
|
|
252989
253101
|
if (dom) {
|
|
252990
253102
|
const truncated = dom.length > 5e4 ? dom.slice(0, 5e4) + "\n... (truncated)" : dom;
|
|
252991
|
-
return {
|
|
253103
|
+
return {
|
|
253104
|
+
success: true,
|
|
253105
|
+
output: truncated,
|
|
253106
|
+
durationMs: Date.now() - start2
|
|
253107
|
+
};
|
|
252992
253108
|
}
|
|
252993
|
-
return {
|
|
253109
|
+
return {
|
|
253110
|
+
success: false,
|
|
253111
|
+
output: "",
|
|
253112
|
+
error: "DOM capture failed",
|
|
253113
|
+
durationMs: Date.now() - start2
|
|
253114
|
+
};
|
|
252994
253115
|
}
|
|
252995
253116
|
// dom_summary: Research-grounded DOM downsampling
|
|
252996
253117
|
// Paper: AgentOccam (arXiv:2410.13825, ICLR 2025) — pivotal node extraction
|
|
@@ -253004,9 +253125,18 @@ var init_browser_action = __esm({
|
|
|
253004
253125
|
result = await apiCall("/dom", "GET");
|
|
253005
253126
|
const rawDom = result.dom;
|
|
253006
253127
|
if (!rawDom)
|
|
253007
|
-
return {
|
|
253128
|
+
return {
|
|
253129
|
+
success: false,
|
|
253130
|
+
output: "",
|
|
253131
|
+
error: "DOM capture failed",
|
|
253132
|
+
durationMs: Date.now() - start2
|
|
253133
|
+
};
|
|
253008
253134
|
const summary = downsampleDom(rawDom);
|
|
253009
|
-
return {
|
|
253135
|
+
return {
|
|
253136
|
+
success: true,
|
|
253137
|
+
output: summary,
|
|
253138
|
+
durationMs: Date.now() - start2
|
|
253139
|
+
};
|
|
253010
253140
|
}
|
|
253011
253141
|
// vision_click: Screenshot → Moondream point detection → Click
|
|
253012
253142
|
// Paper: SeeAct (arXiv:2401.01614) — visual grounding for web agents
|
|
@@ -253019,14 +253149,24 @@ var init_browser_action = __esm({
|
|
|
253019
253149
|
case "vision_click": {
|
|
253020
253150
|
const target = args.text;
|
|
253021
253151
|
if (!target)
|
|
253022
|
-
return {
|
|
253152
|
+
return {
|
|
253153
|
+
success: false,
|
|
253154
|
+
output: "",
|
|
253155
|
+
error: "text parameter is required for vision_click — describe what to click (e.g. 'the login button')",
|
|
253156
|
+
durationMs: Date.now() - start2
|
|
253157
|
+
};
|
|
253023
253158
|
const ssResult = await apiCall("/screenshot", "GET");
|
|
253024
253159
|
const ssB64 = ssResult.b64;
|
|
253025
253160
|
const ssWidth = ssResult.width || 1280;
|
|
253026
253161
|
const ssHeight = ssResult.height || 720;
|
|
253027
253162
|
const ssFile = ssResult.file;
|
|
253028
253163
|
if (!ssB64 && !ssFile) {
|
|
253029
|
-
return {
|
|
253164
|
+
return {
|
|
253165
|
+
success: false,
|
|
253166
|
+
output: "",
|
|
253167
|
+
error: "Screenshot failed — cannot perform vision click",
|
|
253168
|
+
durationMs: Date.now() - start2
|
|
253169
|
+
};
|
|
253030
253170
|
}
|
|
253031
253171
|
let imagePath = "";
|
|
253032
253172
|
if (ssFile) {
|
|
@@ -253038,7 +253178,12 @@ var init_browser_action = __esm({
|
|
|
253038
253178
|
wfs(tmpPath, fileBuffer);
|
|
253039
253179
|
imagePath = tmpPath;
|
|
253040
253180
|
} catch (e2) {
|
|
253041
|
-
return {
|
|
253181
|
+
return {
|
|
253182
|
+
success: false,
|
|
253183
|
+
output: "",
|
|
253184
|
+
error: `Failed to save screenshot: ${e2}`,
|
|
253185
|
+
durationMs: Date.now() - start2
|
|
253186
|
+
};
|
|
253042
253187
|
}
|
|
253043
253188
|
} else if (ssB64) {
|
|
253044
253189
|
const tmpPath = join41(process.env["TMPDIR"] || "/tmp", `oa-vision-click-${Date.now()}.png`);
|
|
@@ -253056,7 +253201,12 @@ var init_browser_action = __esm({
|
|
|
253056
253201
|
prompt: target
|
|
253057
253202
|
});
|
|
253058
253203
|
if (!visionResult.success) {
|
|
253059
|
-
return {
|
|
253204
|
+
return {
|
|
253205
|
+
success: false,
|
|
253206
|
+
output: `Vision could not find "${target}" on the page. Try using dom_summary to find the CSS selector instead.`,
|
|
253207
|
+
error: visionResult.error,
|
|
253208
|
+
durationMs: Date.now() - start2
|
|
253209
|
+
};
|
|
253060
253210
|
}
|
|
253061
253211
|
const coordMatch = visionResult.output.match(/\((\d+\.?\d*),\s*(\d+\.?\d*)\)/);
|
|
253062
253212
|
if (coordMatch) {
|
|
@@ -253066,10 +253216,19 @@ var init_browser_action = __esm({
|
|
|
253066
253216
|
pointY = Math.round(normY * ssHeight);
|
|
253067
253217
|
}
|
|
253068
253218
|
} catch (e2) {
|
|
253069
|
-
return {
|
|
253219
|
+
return {
|
|
253220
|
+
success: false,
|
|
253221
|
+
output: "",
|
|
253222
|
+
error: `Vision detection failed: ${e2}`,
|
|
253223
|
+
durationMs: Date.now() - start2
|
|
253224
|
+
};
|
|
253070
253225
|
}
|
|
253071
253226
|
if (pointX < 0 || pointY < 0) {
|
|
253072
|
-
return {
|
|
253227
|
+
return {
|
|
253228
|
+
success: false,
|
|
253229
|
+
output: `Could not determine click coordinates for "${target}". Vision returned no valid points.`,
|
|
253230
|
+
durationMs: Date.now() - start2
|
|
253231
|
+
};
|
|
253073
253232
|
}
|
|
253074
253233
|
const clickResult = await apiCall("/click_xy", "POST", {
|
|
253075
253234
|
x: pointX,
|
|
@@ -253092,22 +253251,49 @@ var init_browser_action = __esm({
|
|
|
253092
253251
|
};
|
|
253093
253252
|
}
|
|
253094
253253
|
case "scroll":
|
|
253095
|
-
result = await apiCall("/scroll", "POST", {
|
|
253096
|
-
|
|
253254
|
+
result = await apiCall("/scroll", "POST", {
|
|
253255
|
+
amount: args.amount ?? 600
|
|
253256
|
+
});
|
|
253257
|
+
return {
|
|
253258
|
+
success: !!result.ok,
|
|
253259
|
+
output: `Scrolled ${args.amount ?? 600}px`,
|
|
253260
|
+
durationMs: Date.now() - start2
|
|
253261
|
+
};
|
|
253097
253262
|
case "scroll_up":
|
|
253098
253263
|
result = await apiCall("/scroll/up", "POST");
|
|
253099
|
-
return {
|
|
253264
|
+
return {
|
|
253265
|
+
success: !!result.ok,
|
|
253266
|
+
output: "Scrolled up",
|
|
253267
|
+
durationMs: Date.now() - start2
|
|
253268
|
+
};
|
|
253100
253269
|
case "scroll_down":
|
|
253101
253270
|
result = await apiCall("/scroll/down", "POST");
|
|
253102
|
-
return {
|
|
253271
|
+
return {
|
|
253272
|
+
success: !!result.ok,
|
|
253273
|
+
output: "Scrolled down",
|
|
253274
|
+
durationMs: Date.now() - start2
|
|
253275
|
+
};
|
|
253103
253276
|
case "back":
|
|
253104
253277
|
result = await apiCall("/history/back", "POST");
|
|
253105
|
-
return {
|
|
253278
|
+
return {
|
|
253279
|
+
success: !!result.ok,
|
|
253280
|
+
output: "Navigated back",
|
|
253281
|
+
durationMs: Date.now() - start2
|
|
253282
|
+
};
|
|
253106
253283
|
case "forward":
|
|
253107
253284
|
result = await apiCall("/history/forward", "POST");
|
|
253108
|
-
return {
|
|
253285
|
+
return {
|
|
253286
|
+
success: !!result.ok,
|
|
253287
|
+
output: "Navigated forward",
|
|
253288
|
+
durationMs: Date.now() - start2
|
|
253289
|
+
};
|
|
253109
253290
|
default:
|
|
253110
|
-
return {
|
|
253291
|
+
return {
|
|
253292
|
+
success: false,
|
|
253293
|
+
output: "",
|
|
253294
|
+
error: `Unknown action: ${action}. Available: navigate, click, click_xy, type, screenshot, dom, scroll, scroll_up, scroll_down, back, forward, close`,
|
|
253295
|
+
durationMs: Date.now() - start2
|
|
253296
|
+
};
|
|
253111
253297
|
}
|
|
253112
253298
|
} catch (err) {
|
|
253113
253299
|
return {
|
|
@@ -527925,6 +528111,10 @@ var init_agenticRunner = __esm({
|
|
|
527925
528111
|
// can re-emit the same plan a second time (plan-replay) and execute
|
|
527926
528112
|
// duplicate work because PROGRESS NUDGE alone is informational.
|
|
527927
528113
|
_progressGateActive = false;
|
|
528114
|
+
// Consecutive gate blocks count. When the model ignores the gate and
|
|
528115
|
+
// retries a blocked tool, this counter increments. ≥2 triggers a system
|
|
528116
|
+
// message escalation to break pattern-lock loops.
|
|
528117
|
+
_consecutiveGateBlocks = 0;
|
|
527928
528118
|
// REG-5: Rolling buffer of recent tool failures with their error output.
|
|
527929
528119
|
// Surfaced before every LLM call so the agent can't ignore "I just ran this
|
|
527930
528120
|
// and it errored". Detects same-fingerprint failure repetition and escalates
|
|
@@ -528174,15 +528364,19 @@ var init_agenticRunner = __esm({
|
|
|
528174
528364
|
// DECOMP-2 (root-cause from batch531-midi-decomp, 2026-05-03): compelling
|
|
528175
528365
|
// sub_agent delegation. DECOMP-1's informational directive was ignored
|
|
528176
528366
|
// (0 sub_agent calls in 466 tool-call run despite directive at turn 1).
|
|
528177
|
-
// Mirrors the BFC-61.G escalation arc
|
|
528178
|
-
// ≥
|
|
528367
|
+
// Mirrors the BFC-61.G escalation arc, but must not deadlock delivery:
|
|
528368
|
+
// when the agent has edited ≥adaptive-threshold distinct files in main context
|
|
528369
|
+
// WITHOUT successful sub_agent,
|
|
528179
528370
|
// the dispatcher BLOCKS edits to NEW files (paths not yet edited) until
|
|
528180
528371
|
// sub_agent succeeds. Edits to already-touched files are still allowed
|
|
528181
|
-
// (current-module finishing work).
|
|
528182
|
-
//
|
|
528372
|
+
// (current-module finishing work). Repeated failed delegation attempts
|
|
528373
|
+
// unlock a main-context fallback so the guardrail cannot become a hard
|
|
528374
|
+
// write-deadlock when sub_agent itself is broken or unavailable.
|
|
528183
528375
|
// Kill switch: OA_DISABLE_DECOMP2=1.
|
|
528184
528376
|
_decomp2MainContextFiles = /* @__PURE__ */ new Set();
|
|
528185
528377
|
_decomp2SubAgentCalls = 0;
|
|
528378
|
+
_decomp2FailedDelegationCalls = 0;
|
|
528379
|
+
_decomp2FallbackAllowed = false;
|
|
528186
528380
|
_decomp2GateActive = false;
|
|
528187
528381
|
// MEM_PATH item #9: adaptive retrieval cache. When the (goalHash, recent-tool-sig)
|
|
528188
528382
|
// hasn't changed since last retrieval, skip the PPR call entirely and reuse
|
|
@@ -528727,6 +528921,8 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528727
528921
|
_maybeDecomp2Block(tc, turn) {
|
|
528728
528922
|
if (!this._decomp2GateActive)
|
|
528729
528923
|
return null;
|
|
528924
|
+
if (this._decomp2FallbackAllowed)
|
|
528925
|
+
return null;
|
|
528730
528926
|
if (process.env["OA_DISABLE_DECOMP2"] === "1")
|
|
528731
528927
|
return null;
|
|
528732
528928
|
const _editTools = /* @__PURE__ */ new Set([
|
|
@@ -528747,7 +528943,7 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528747
528943
|
const decomp2Msg = [
|
|
528748
528944
|
`[BLOCKED — DECOMP-2 main-context exhaustion]`,
|
|
528749
528945
|
``,
|
|
528750
|
-
`You have already edited ${this._decomp2MainContextFiles.size} distinct files in main context without
|
|
528946
|
+
`You have already edited ${this._decomp2MainContextFiles.size} distinct files in main context without a successful sub_agent. Continuing to edit another new file ('${_editPath}') may keep your context window saturated and trigger compaction thrashing.`,
|
|
528751
528947
|
``,
|
|
528752
528948
|
`Files you've already edited (will accept further edits to these):`,
|
|
528753
528949
|
_filesList,
|
|
@@ -528763,13 +528959,15 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528763
528959
|
` })`,
|
|
528764
528960
|
` 3. After sub_agent returns, mark the todo completed.`,
|
|
528765
528961
|
``,
|
|
528962
|
+
`If sub_agent keeps failing for reasons outside the module work, retry it once with corrected arguments. After repeated failed delegation attempts, OA will downgrade this from a hard block to an advisory fallback so file writes can continue.`,
|
|
528963
|
+
``,
|
|
528766
528964
|
`Why this matters: spreading edits across N files in main context burns ~N × file_size tokens. sub_agent gives the next module a focused context window.`,
|
|
528767
528965
|
``,
|
|
528768
528966
|
`If you have ALREADY edited '${_editPath}' (this is a continuation), the orchestrator's set must have missed it — call file_read to verify, then re-edit. Otherwise, dispatch sub_agent now.`
|
|
528769
528967
|
].join("\n");
|
|
528770
528968
|
this.emit({
|
|
528771
528969
|
type: "status",
|
|
528772
|
-
content: `DECOMP-2 NEW-FILE BLOCK — rejected ${tc.name}('${_editPath}') at turn ${turn}; gate stays active until sub_agent succeeds`,
|
|
528970
|
+
content: `DECOMP-2 NEW-FILE BLOCK — rejected ${tc.name}('${_editPath}') at turn ${turn}; gate stays active until sub_agent succeeds or repeated delegation failure unlocks fallback`,
|
|
528773
528971
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
528774
528972
|
});
|
|
528775
528973
|
this._tagSyntheticFailure({
|
|
@@ -528778,6 +528976,12 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528778
528976
|
});
|
|
528779
528977
|
return decomp2Msg;
|
|
528780
528978
|
}
|
|
528979
|
+
_decomp2FileSpreadThreshold() {
|
|
528980
|
+
const ctx3 = this.options.contextWindowSize ?? 0;
|
|
528981
|
+
if (ctx3 <= 0)
|
|
528982
|
+
return 5;
|
|
528983
|
+
return Math.max(5, Math.min(30, Math.round(ctx3 / 6400)));
|
|
528984
|
+
}
|
|
528781
528985
|
/**
|
|
528782
528986
|
* DECOMP-2 post-dispatch tracking. Refactored from inline so both the
|
|
528783
528987
|
* main turn loop AND the brute-force re-engagement inner loop record
|
|
@@ -528804,12 +529008,12 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528804
529008
|
const _editPaths = this._extractToolTargetPaths(tc.name, tc.arguments, result);
|
|
528805
529009
|
for (const _editPath of _editPaths) {
|
|
528806
529010
|
this._decomp2MainContextFiles.add(_editPath);
|
|
528807
|
-
const DECOMP2_FILE_SPREAD_THRESHOLD =
|
|
528808
|
-
if (!this._decomp2GateActive && this._decomp2MainContextFiles.size >= DECOMP2_FILE_SPREAD_THRESHOLD && this._decomp2SubAgentCalls === 0) {
|
|
529011
|
+
const DECOMP2_FILE_SPREAD_THRESHOLD = this._decomp2FileSpreadThreshold();
|
|
529012
|
+
if (!this._decomp2GateActive && !this._decomp2FallbackAllowed && this._decomp2MainContextFiles.size >= DECOMP2_FILE_SPREAD_THRESHOLD && this._decomp2SubAgentCalls === 0) {
|
|
528809
529013
|
this._decomp2GateActive = true;
|
|
528810
529014
|
this.emit({
|
|
528811
529015
|
type: "status",
|
|
528812
|
-
content: `DECOMP-2 NEW-FILE GATE ACTIVATED — ${this._decomp2MainContextFiles.size} distinct files edited in main context, 0 successful sub_agent calls; further edits to NEW files will be blocked until sub_agent succeeds`,
|
|
529016
|
+
content: `DECOMP-2 NEW-FILE GATE ACTIVATED — ${this._decomp2MainContextFiles.size} distinct files edited in main context, 0 successful sub_agent calls, threshold=${DECOMP2_FILE_SPREAD_THRESHOLD}; further edits to NEW files will be blocked until sub_agent succeeds or repeated delegation failure unlocks fallback`,
|
|
528813
529017
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
528814
529018
|
});
|
|
528815
529019
|
}
|
|
@@ -528818,15 +529022,27 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
|
|
|
528818
529022
|
if (tc.name === "sub_agent" || tc.name === "priority_delegate" || tc.name === "background_run") {
|
|
528819
529023
|
if (result?.success !== true) {
|
|
528820
529024
|
if (this._decomp2GateActive) {
|
|
529025
|
+
this._decomp2FailedDelegationCalls++;
|
|
528821
529026
|
this.emit({
|
|
528822
529027
|
type: "status",
|
|
528823
529028
|
content: `DECOMP-2 DELEGATION FAILED — '${tc.name}' did not clear gate at turn ${turn}; fix delegation arguments/result before editing another new file`,
|
|
528824
529029
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
528825
529030
|
});
|
|
529031
|
+
if (this._decomp2FailedDelegationCalls >= 2) {
|
|
529032
|
+
this._decomp2FallbackAllowed = true;
|
|
529033
|
+
this._decomp2GateActive = false;
|
|
529034
|
+
this.emit({
|
|
529035
|
+
type: "status",
|
|
529036
|
+
content: `DECOMP-2 FALLBACK UNLOCKED — ${this._decomp2FailedDelegationCalls} failed delegation attempts while gate was active; allowing main-context new-file edits so work can continue`,
|
|
529037
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
529038
|
+
});
|
|
529039
|
+
}
|
|
528826
529040
|
}
|
|
528827
529041
|
return;
|
|
528828
529042
|
}
|
|
528829
529043
|
this._decomp2SubAgentCalls++;
|
|
529044
|
+
this._decomp2FailedDelegationCalls = 0;
|
|
529045
|
+
this._decomp2FallbackAllowed = false;
|
|
528830
529046
|
if (this._decomp2GateActive) {
|
|
528831
529047
|
this._decomp2GateActive = false;
|
|
528832
529048
|
this.emit({
|
|
@@ -530914,6 +531130,8 @@ Respond with your assessment, then take action.`;
|
|
|
530914
531130
|
this._reg61PerpetualGateActive = false;
|
|
530915
531131
|
this._decomp2MainContextFiles = /* @__PURE__ */ new Set();
|
|
530916
531132
|
this._decomp2SubAgentCalls = 0;
|
|
531133
|
+
this._decomp2FailedDelegationCalls = 0;
|
|
531134
|
+
this._decomp2FallbackAllowed = false;
|
|
530917
531135
|
this._decomp2GateActive = false;
|
|
530918
531136
|
if (!globalThis.__oa_rca1_sigterm_installed) {
|
|
530919
531137
|
globalThis.__oa_rca1_sigterm_installed = true;
|
|
@@ -533252,22 +533470,25 @@ ${memoryLines.join("\n")}`
|
|
|
533252
533470
|
recentWrites.push({ path: path11, turn: info.lastWriteTurn ?? 0 });
|
|
533253
533471
|
}
|
|
533254
533472
|
}
|
|
533473
|
+
this._consecutiveGateBlocks++;
|
|
533255
533474
|
recentWrites.sort((a2, b) => b.turn - a2.turn);
|
|
533256
533475
|
const showWrites = recentWrites.slice(0, 16);
|
|
533476
|
+
const isRepeat = this._consecutiveGateBlocks >= 2;
|
|
533257
533477
|
const gateMsg = [
|
|
533258
|
-
`[
|
|
533478
|
+
`[BLOCKED — PROGRESS GATE active]`,
|
|
533259
533479
|
``,
|
|
533260
|
-
`
|
|
533261
|
-
`
|
|
533480
|
+
`CAUSE: ${this._writesSinceLastTodoWrite} file writes since last todo_write call. Without progress tracking, the next turn re-plans the same work (plan-replay).`,
|
|
533481
|
+
`EFFECT: All non-todo tool calls are now blocked at the runtime level.`,
|
|
533482
|
+
`ACTION REQUIRED: Call todo_write with updated progress to release the gate.`,
|
|
533483
|
+
`CONSEQUENCE OF IGNORING: Retrying blocked tools does NOT work — only todo_write is accepted while the gate is active.`,
|
|
533484
|
+
isRepeat ? `
|
|
533485
|
+
[ESCALATION: This is block #${this._consecutiveGateBlocks}. You keep calling blocked tools instead of todo_write. The gate cannot be bypassed. You MUST call todo_write next.]` : "",
|
|
533262
533486
|
``,
|
|
533263
533487
|
`Recent file modifications (use these to decide what's done):`,
|
|
533264
533488
|
...showWrites.map((w) => ` • ${w.path} (turn ${w.turn})`),
|
|
533265
533489
|
recentWrites.length > showWrites.length ? ` • ... +${recentWrites.length - showWrites.length} more` : "",
|
|
533266
533490
|
``,
|
|
533267
|
-
`
|
|
533268
|
-
`After todo_write succeeds, this gate releases and you can continue normal work.`,
|
|
533269
|
-
``,
|
|
533270
|
-
`Why this exists: without the explicit progress update, your next turn will see the same in_progress todo, re-plan the same work, and re-emit identical tool calls (the "plan replay" failure mode that causes byte-identical writes to appear twice).`
|
|
533491
|
+
`Format: todo_write with todos array — mark items completed that these writes satisfy, advance next to in_progress. After todo_write succeeds, normal tools resume.`
|
|
533271
533492
|
].filter(Boolean).join("\n");
|
|
533272
533493
|
this.emit({
|
|
533273
533494
|
type: "tool_result",
|
|
@@ -533753,6 +533974,7 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
533753
533974
|
}
|
|
533754
533975
|
this._writesSinceLastTodoWrite = 0;
|
|
533755
533976
|
this._progressGateActive = false;
|
|
533977
|
+
this._consecutiveGateBlocks = 0;
|
|
533756
533978
|
}
|
|
533757
533979
|
if (tc.name === "file_read") {
|
|
533758
533980
|
const p2 = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "");
|
|
@@ -534761,6 +534983,12 @@ Then use file_read on individual FILES inside it.`);
|
|
|
534761
534983
|
const output = sr.result.success ? sr.result.output : `Error: ${sr.result.error || "unknown"}
|
|
534762
534984
|
${sr.result.output}`;
|
|
534763
534985
|
messages2.push(this.buildToolMessage(output, matchTc.id, matchTc.name));
|
|
534986
|
+
if (this._consecutiveGateBlocks >= 2 && this._progressGateActive) {
|
|
534987
|
+
messages2.push({
|
|
534988
|
+
role: "system",
|
|
534989
|
+
content: `[PROGRESS GATE ESCALATION] You have made ${this._consecutiveGateBlocks} consecutive blocked tool calls without calling todo_write. The gate is enforced at the runtime level — retrying the same blocked tool will never work. Your NEXT call MUST be todo_write(todos=[...]) with updated progress. No other tool will be accepted until the gate is released.`
|
|
534990
|
+
});
|
|
534991
|
+
}
|
|
534764
534992
|
if (matchTc.name === "task_complete") {
|
|
534765
534993
|
const open2 = this.getOpenTodoItems();
|
|
534766
534994
|
if (open2.length > 0) {
|
|
@@ -534801,6 +535029,12 @@ ${sr.result.output}`;
|
|
|
534801
535029
|
const r2 = await executeSingle(tc);
|
|
534802
535030
|
if (r2) {
|
|
534803
535031
|
messages2.push(this.buildToolMessage(r2.output, r2.tc.id, r2.tc.name));
|
|
535032
|
+
if (this._consecutiveGateBlocks >= 2 && this._progressGateActive) {
|
|
535033
|
+
messages2.push({
|
|
535034
|
+
role: "system",
|
|
535035
|
+
content: `[PROGRESS GATE ESCALATION] You have made ${this._consecutiveGateBlocks} consecutive blocked tool calls without calling todo_write. The gate is enforced at the runtime level — retrying the same blocked tool will never work. Your NEXT call MUST be todo_write(todos=[...]) with updated progress. No other tool will be accepted until the gate is released.`
|
|
535036
|
+
});
|
|
535037
|
+
}
|
|
534804
535038
|
if (r2.tc.name === "task_complete") {
|
|
534805
535039
|
const open2 = this.getOpenTodoItems();
|
|
534806
535040
|
if (open2.length > 0) {
|
|
@@ -534893,6 +535127,14 @@ ${sr.result.output}`;
|
|
|
534893
535127
|
} else {
|
|
534894
535128
|
completed = true;
|
|
534895
535129
|
summary = extractTaskCompleteSummary(r2.tc.arguments);
|
|
535130
|
+
for (const tool of this.tools.values()) {
|
|
535131
|
+
if (tool.cleanup) {
|
|
535132
|
+
try {
|
|
535133
|
+
await tool.cleanup();
|
|
535134
|
+
} catch {
|
|
535135
|
+
}
|
|
535136
|
+
}
|
|
535137
|
+
}
|
|
534896
535138
|
if (summary && !this._assistantTextEmitted) {
|
|
534897
535139
|
this.emit({
|
|
534898
535140
|
type: "assistant_text",
|
|
@@ -613853,7 +614095,9 @@ function adaptTool6(tool) {
|
|
|
613853
614095
|
output: result.output,
|
|
613854
614096
|
error: result.error
|
|
613855
614097
|
};
|
|
613856
|
-
}
|
|
614098
|
+
},
|
|
614099
|
+
// Pass through lifecycle hooks from the underlying Tool implementation
|
|
614100
|
+
cleanup: tool.cleanup
|
|
613857
614101
|
};
|
|
613858
614102
|
}
|
|
613859
614103
|
function scanForSessionSignals(toolOutput) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.575",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.575",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
package/package.json
CHANGED