open-agents-ai 0.28.0 → 0.29.0

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.
Files changed (2) hide show
  1. package/dist/index.js +50 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10413,32 +10413,31 @@ Rules:
10413
10413
  * Build a self-eval prompt for the agent when approaching timeout.
10414
10414
  * Returns the prompt to inject. The agent will respond with a plan.
10415
10415
  */
10416
- buildTimeoutSelfEvalPrompt(elapsedMs2, toolCallCount, repetitionScore, remainingMs) {
10416
+ buildHealthCheckPrompt(elapsedMs2, toolCallCount, repetitionScore, checkNumber) {
10417
10417
  const elapsedMin = (elapsedMs2 / 6e4).toFixed(1);
10418
- const remainingMin = (remainingMs / 6e4).toFixed(1);
10419
10418
  const stuckWarning = repetitionScore > 0.5 ? `
10420
10419
  \u26A0 REPETITION DETECTED: Your recent tool calls are ${Math.round(repetitionScore * 100)}% repetitive. You may be stuck in a loop.` : "";
10421
- return `[TIMEOUT APPROACHING \u2014 Self-Assessment Required]
10420
+ return `[HEALTH CHECK #${checkNumber} \u2014 Progress Assessment]
10422
10421
 
10423
- You have been working for ${elapsedMin} minutes with ${toolCallCount} tool calls. You have approximately ${remainingMin} minutes remaining.${stuckWarning}
10422
+ You have been working for ${elapsedMin} minutes with ${toolCallCount} tool calls. There is no time limit \u2014 take as long as you need.${stuckWarning}
10424
10423
 
10425
- ASSESS YOUR SITUATION and choose ONE action:
10424
+ Briefly assess your situation and choose ONE action:
10426
10425
 
10427
- 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.
10426
+ 1. CONTINUE \u2014 If you are making progress, briefly note what you've done and what remains. Keep working.
10428
10427
 
10429
- 2. PIVOT \u2014 If your current approach isn't working, say "PIVOT" and describe a completely different strategy. Then immediately try that new approach.
10428
+ 2. PIVOT \u2014 If your current approach isn't working, describe a different strategy and immediately try it.
10430
10429
 
10431
- 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.
10430
+ 3. CHECKPOINT \u2014 If you've made partial progress and want to save it, call task_complete with a summary. The user can continue later.
10432
10431
 
10433
- 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.`;
10432
+ Respond with your assessment, then take action.`;
10434
10433
  }
10435
10434
  /** Run a task through the agentic loop */
10436
10435
  async run(task, context) {
10437
10436
  const start = Date.now();
10438
10437
  const taskTimeoutMs = this.options.taskTimeoutMs;
10439
- const softDeadline = start + Math.floor(taskTimeoutMs * 0.8);
10440
- const hardDeadline = start + Math.floor(taskTimeoutMs * 1.5);
10441
- let softTimeoutTriggered = false;
10438
+ const selfEvalInterval = taskTimeoutMs;
10439
+ let nextSelfEval = start + selfEvalInterval;
10440
+ let selfEvalCount = 0;
10442
10441
  const toolCallLog = [];
10443
10442
  this.aborted = false;
10444
10443
  this.pendingUserMessages.length = 0;
@@ -10467,24 +10466,20 @@ TASK: ${task}` : task }
10467
10466
  break;
10468
10467
  }
10469
10468
  const now = Date.now();
10470
- if (now > hardDeadline) {
10471
- this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10472
- break;
10473
- }
10474
- if (!softTimeoutTriggered && now > softDeadline) {
10475
- softTimeoutTriggered = true;
10469
+ if (now > nextSelfEval) {
10470
+ selfEvalCount++;
10476
10471
  const elapsed = now - start;
10477
- const remaining = hardDeadline - now;
10478
10472
  const repetitionScore = this.detectRepetition(toolCallLog);
10479
10473
  this.emit({
10480
10474
  type: "compaction",
10481
- content: `Timeout approaching (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 injecting self-assessment`,
10475
+ content: `Health check #${selfEvalCount} (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 assessing progress`,
10482
10476
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10483
10477
  });
10484
10478
  messages.push({
10485
10479
  role: "user",
10486
- content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
10480
+ content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
10487
10481
  });
10482
+ nextSelfEval = now + selfEvalInterval;
10488
10483
  }
10489
10484
  while (this.pendingUserMessages.length > 0) {
10490
10485
  const userMsg = this.pendingUserMessages.shift();
@@ -10693,7 +10688,7 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
10693
10688
  });
10694
10689
  }
10695
10690
  }
10696
- while (!completed && !this.aborted && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles && Date.now() < hardDeadline) {
10691
+ while (!completed && !this.aborted && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles) {
10697
10692
  bruteForceCycle++;
10698
10693
  const totalTurns = messages.filter((m) => m.role === "assistant").length;
10699
10694
  this.emit({
@@ -10727,24 +10722,20 @@ You have ${this.options.maxTurns} more turns. Continue making progress. Call tas
10727
10722
  break;
10728
10723
  }
10729
10724
  const bfNow = Date.now();
10730
- if (bfNow > hardDeadline) {
10731
- this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10732
- break;
10733
- }
10734
- if (!softTimeoutTriggered && bfNow > softDeadline) {
10735
- softTimeoutTriggered = true;
10725
+ if (bfNow > nextSelfEval) {
10726
+ selfEvalCount++;
10736
10727
  const elapsed = bfNow - start;
10737
- const remaining = hardDeadline - bfNow;
10738
10728
  const repetitionScore = this.detectRepetition(toolCallLog);
10739
10729
  this.emit({
10740
10730
  type: "compaction",
10741
- content: `Timeout approaching (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 injecting self-assessment`,
10731
+ content: `Health check #${selfEvalCount} (${(elapsed / 6e4).toFixed(1)}m elapsed) \u2014 assessing progress`,
10742
10732
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10743
10733
  });
10744
10734
  messages.push({
10745
10735
  role: "user",
10746
- content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
10736
+ content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
10747
10737
  });
10738
+ nextSelfEval = bfNow + selfEvalInterval;
10748
10739
  }
10749
10740
  while (this.pendingUserMessages.length > 0) {
10750
10741
  const userMsg = this.pendingUserMessages.shift();
@@ -11416,8 +11407,7 @@ ${newerSummary}` : newerSummary;
11416
11407
  const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
11417
11408
  method: "POST",
11418
11409
  headers: this.authHeaders(),
11419
- body: JSON.stringify(body),
11420
- signal: AbortSignal.timeout(request.timeoutMs)
11410
+ body: JSON.stringify(body)
11421
11411
  });
11422
11412
  if (!resp.ok) {
11423
11413
  const text = await resp.text().catch(() => "");
@@ -11477,8 +11467,7 @@ ${newerSummary}` : newerSummary;
11477
11467
  const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
11478
11468
  method: "POST",
11479
11469
  headers: this.authHeaders(),
11480
- body: JSON.stringify(body),
11481
- signal: AbortSignal.timeout(request.timeoutMs)
11470
+ body: JSON.stringify(body)
11482
11471
  });
11483
11472
  if (!resp.ok) {
11484
11473
  const text = await resp.text().catch(() => "");
@@ -18994,9 +18983,28 @@ var init_status_bar = __esm({
18994
18983
  this.metrics.totalTokens = update.totalTokens;
18995
18984
  if (update.estimatedContextTokens !== void 0)
18996
18985
  this.metrics.estimatedContextTokens = update.estimatedContextTokens;
18986
+ this._streamingTokens = 0;
18997
18987
  if (this.active)
18998
18988
  this.renderFooterPreserveCursor();
18999
18989
  }
18990
+ /** Running count of tokens estimated during streaming (reset on authoritative updateMetrics) */
18991
+ _streamingTokens = 0;
18992
+ _streamThrottleTimer = null;
18993
+ /** Increment the live streaming token counter (throttled re-render at 100ms) */
18994
+ incrementStreamingTokens(count) {
18995
+ this._streamingTokens += count;
18996
+ if (!this._streamThrottleTimer && this.active) {
18997
+ this._streamThrottleTimer = setTimeout(() => {
18998
+ this._streamThrottleTimer = null;
18999
+ if (this.active)
19000
+ this.renderFooterPreserveCursor();
19001
+ }, 100);
19002
+ }
19003
+ }
19004
+ /** Get the effective completion tokens (authoritative + live streaming estimate) */
19005
+ get effectiveCompletionTokens() {
19006
+ return this.metrics.completionTokens + this._streamingTokens;
19007
+ }
19000
19008
  /** Reset metrics (e.g. on session start) */
19001
19009
  resetMetrics() {
19002
19010
  this.metrics.promptTokens = 0;
@@ -19122,7 +19130,8 @@ var init_status_bar = __esm({
19122
19130
  const pipe = pastel2(60, " \u2502 ");
19123
19131
  const tokIn = m.promptTokens > 0 ? m.promptTokens.toLocaleString() : `~${Math.max(m.estimatedContextTokens, 0).toLocaleString()}`;
19124
19132
  const tokInLabel = pastel2(117, "In: ") + c2.bold(tokIn);
19125
- const tokOut = m.completionTokens > 0 ? m.completionTokens.toLocaleString() : `~${Math.ceil(m.totalTokens > 0 ? m.totalTokens - m.promptTokens : m.estimatedContextTokens * 0.3).toLocaleString()}`;
19133
+ const effectiveOut = this.effectiveCompletionTokens;
19134
+ const tokOut = effectiveOut > 0 ? effectiveOut.toLocaleString() : `~${Math.ceil(m.totalTokens > 0 ? m.totalTokens - m.promptTokens : m.estimatedContextTokens * 0.3).toLocaleString()}`;
19126
19135
  const tokOutLabel = pastel2(151, "Out: ") + c2.bold(tokOut);
19127
19136
  const ctxUsed = m.estimatedContextTokens;
19128
19137
  const ctxTotal = m.contextWindowSize;
@@ -19505,7 +19514,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
19505
19514
  streamEnabled: stream?.enabled ?? false,
19506
19515
  bruteForce: bruteForce ?? true,
19507
19516
  bruteForceMaxCycles: 100,
19508
- // effectively unlimited — hard timeout is the real bound
19517
+ // effectively unlimited — no hard timeout, agent runs until complete or aborted
19509
19518
  contextWindowSize: contextWindowSize ?? 0
19510
19519
  });
19511
19520
  const tools = buildTools(repoRoot, config);
@@ -19594,6 +19603,10 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
19594
19603
  if (stream?.enabled) {
19595
19604
  stream.renderer.write(event.content ?? "", event.streamKind ?? "content");
19596
19605
  }
19606
+ if (statusBar && event.content) {
19607
+ const estimatedNewTokens = Math.max(1, Math.ceil(event.content.length / 4));
19608
+ statusBar.incrementStreamingTokens(estimatedNewTokens);
19609
+ }
19597
19610
  break;
19598
19611
  case "stream_end":
19599
19612
  if (stream?.enabled) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
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",