open-agents-ai 0.12.4 → 0.12.5
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 +80 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7421,10 +7421,49 @@ If you notice you're performing the SAME multi-step sequence for the 3rd time or
|
|
|
7421
7421
|
}
|
|
7422
7422
|
}
|
|
7423
7423
|
}
|
|
7424
|
+
/**
|
|
7425
|
+
* Detect repetition in recent tool calls.
|
|
7426
|
+
* Returns a score 0-1 where 1 = fully repetitive (stuck in a loop).
|
|
7427
|
+
*/
|
|
7428
|
+
detectRepetition(recentToolCalls) {
|
|
7429
|
+
if (recentToolCalls.length < 4)
|
|
7430
|
+
return 0;
|
|
7431
|
+
const window = recentToolCalls.slice(-8);
|
|
7432
|
+
const uniqueKeys = new Set(window.map((tc) => `${tc.name}:${tc.argsKey}`));
|
|
7433
|
+
const ratio = 1 - uniqueKeys.size / window.length;
|
|
7434
|
+
return ratio;
|
|
7435
|
+
}
|
|
7436
|
+
/**
|
|
7437
|
+
* Build a self-eval prompt for the agent when approaching timeout.
|
|
7438
|
+
* Returns the prompt to inject. The agent will respond with a plan.
|
|
7439
|
+
*/
|
|
7440
|
+
buildTimeoutSelfEvalPrompt(elapsedMs2, toolCallCount, repetitionScore, remainingMs) {
|
|
7441
|
+
const elapsedMin = (elapsedMs2 / 6e4).toFixed(1);
|
|
7442
|
+
const remainingMin = (remainingMs / 6e4).toFixed(1);
|
|
7443
|
+
const stuckWarning = repetitionScore > 0.5 ? `
|
|
7444
|
+
\u26A0 REPETITION DETECTED: Your recent tool calls are ${Math.round(repetitionScore * 100)}% repetitive. You may be stuck in a loop.` : "";
|
|
7445
|
+
return `[TIMEOUT APPROACHING \u2014 Self-Assessment Required]
|
|
7446
|
+
|
|
7447
|
+
You have been working for ${elapsedMin} minutes with ${toolCallCount} tool calls. You have approximately ${remainingMin} minutes remaining.${stuckWarning}
|
|
7448
|
+
|
|
7449
|
+
ASSESS YOUR SITUATION and choose ONE action:
|
|
7450
|
+
|
|
7451
|
+
1. CONTINUE \u2014 If you are making genuine progress on a long task, say "CONTINUE" and briefly explain what progress you've made and what remains. You will get an extended time window.
|
|
7452
|
+
|
|
7453
|
+
2. PIVOT \u2014 If your current approach isn't working, say "PIVOT" and describe a completely different strategy. Then immediately try that new approach.
|
|
7454
|
+
|
|
7455
|
+
3. CHECKPOINT \u2014 If you've made partial progress, say "CHECKPOINT" and call task_complete with a summary of what you accomplished so far. The user can continue from where you left off.
|
|
7456
|
+
|
|
7457
|
+
Respond with your assessment, then take action. Do NOT just say you'll continue without explaining concrete progress. Be honest about whether you're stuck.`;
|
|
7458
|
+
}
|
|
7424
7459
|
/** Run a task through the agentic loop */
|
|
7425
7460
|
async run(task, context) {
|
|
7426
7461
|
const start = Date.now();
|
|
7427
|
-
const
|
|
7462
|
+
const taskTimeoutMs = this.options.taskTimeoutMs;
|
|
7463
|
+
const softDeadline = start + Math.floor(taskTimeoutMs * 0.8);
|
|
7464
|
+
const hardDeadline = start + Math.floor(taskTimeoutMs * 1.5);
|
|
7465
|
+
let softTimeoutTriggered = false;
|
|
7466
|
+
const toolCallLog = [];
|
|
7428
7467
|
this.aborted = false;
|
|
7429
7468
|
this.pendingUserMessages.length = 0;
|
|
7430
7469
|
const systemPrompt = this.options.dynamicContext ? `${SYSTEM_PROMPT}
|
|
@@ -7448,10 +7487,26 @@ TASK: ${task}` : task }
|
|
|
7448
7487
|
this.emit({ type: "error", content: "Task aborted by user", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7449
7488
|
break;
|
|
7450
7489
|
}
|
|
7451
|
-
|
|
7452
|
-
|
|
7490
|
+
const now = Date.now();
|
|
7491
|
+
if (now > hardDeadline) {
|
|
7492
|
+
this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7453
7493
|
break;
|
|
7454
7494
|
}
|
|
7495
|
+
if (!softTimeoutTriggered && now > softDeadline) {
|
|
7496
|
+
softTimeoutTriggered = true;
|
|
7497
|
+
const elapsed = now - start;
|
|
7498
|
+
const remaining = hardDeadline - now;
|
|
7499
|
+
const repetitionScore = this.detectRepetition(toolCallLog);
|
|
7500
|
+
this.emit({
|
|
7501
|
+
type: "compaction",
|
|
7502
|
+
content: `Timeout approaching (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 injecting self-assessment`,
|
|
7503
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7504
|
+
});
|
|
7505
|
+
messages.push({
|
|
7506
|
+
role: "user",
|
|
7507
|
+
content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
|
|
7508
|
+
});
|
|
7509
|
+
}
|
|
7455
7510
|
while (this.pendingUserMessages.length > 0) {
|
|
7456
7511
|
const userMsg = this.pendingUserMessages.shift();
|
|
7457
7512
|
const imagePattern = /\[IMAGE_BASE64:([^:]+):([^\]]+)\]/;
|
|
@@ -7516,6 +7571,8 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
7516
7571
|
if (this.aborted)
|
|
7517
7572
|
break;
|
|
7518
7573
|
toolCallCount++;
|
|
7574
|
+
const argsKey = Object.keys(tc.arguments ?? {}).sort().join(",");
|
|
7575
|
+
toolCallLog.push({ name: tc.name, argsKey });
|
|
7519
7576
|
this.emit({
|
|
7520
7577
|
type: "tool_call",
|
|
7521
7578
|
toolName: tc.name,
|
|
@@ -7576,7 +7633,7 @@ ${result.output}`;
|
|
|
7576
7633
|
});
|
|
7577
7634
|
}
|
|
7578
7635
|
}
|
|
7579
|
-
if (!completed && !this.aborted && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles && Date.now() <
|
|
7636
|
+
if (!completed && !this.aborted && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles && Date.now() < hardDeadline) {
|
|
7580
7637
|
bruteForceCycle++;
|
|
7581
7638
|
const totalTurns = messages.filter((m) => m.role === "assistant").length;
|
|
7582
7639
|
this.emit({
|
|
@@ -7606,10 +7663,26 @@ You have ${this.options.maxTurns} more turns. Be creative and investigative. If
|
|
|
7606
7663
|
this.emit({ type: "error", content: "Task aborted by user", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7607
7664
|
break;
|
|
7608
7665
|
}
|
|
7609
|
-
|
|
7610
|
-
|
|
7666
|
+
const bfNow = Date.now();
|
|
7667
|
+
if (bfNow > hardDeadline) {
|
|
7668
|
+
this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7611
7669
|
break;
|
|
7612
7670
|
}
|
|
7671
|
+
if (!softTimeoutTriggered && bfNow > softDeadline) {
|
|
7672
|
+
softTimeoutTriggered = true;
|
|
7673
|
+
const elapsed = bfNow - start;
|
|
7674
|
+
const remaining = hardDeadline - bfNow;
|
|
7675
|
+
const repetitionScore = this.detectRepetition(toolCallLog);
|
|
7676
|
+
this.emit({
|
|
7677
|
+
type: "compaction",
|
|
7678
|
+
content: `Timeout approaching (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 injecting self-assessment`,
|
|
7679
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7680
|
+
});
|
|
7681
|
+
messages.push({
|
|
7682
|
+
role: "user",
|
|
7683
|
+
content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
|
|
7684
|
+
});
|
|
7685
|
+
}
|
|
7613
7686
|
while (this.pendingUserMessages.length > 0) {
|
|
7614
7687
|
const userMsg = this.pendingUserMessages.shift();
|
|
7615
7688
|
const imagePattern = /\[IMAGE_BASE64:([^:]+):([^\]]+)\]/;
|
|
@@ -7652,6 +7725,7 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
7652
7725
|
if (this.aborted)
|
|
7653
7726
|
break;
|
|
7654
7727
|
toolCallCount++;
|
|
7728
|
+
toolCallLog.push({ name: tc.name, argsKey: Object.keys(tc.arguments ?? {}).sort().join(",") });
|
|
7655
7729
|
this.emit({ type: "tool_call", toolName: tc.name, toolArgs: tc.arguments, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7656
7730
|
const tool = this.tools.get(tc.name);
|
|
7657
7731
|
let result;
|
package/package.json
CHANGED