open-agents-ai 0.27.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 +292 -82
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4019,7 +4019,7 @@ function ensureCommand(command) {
4019
4019
  const result2 = {
4020
4020
  available: false,
4021
4021
  installed: false,
4022
- error: `No supported package manager found. Install ${command} manually.`
4022
+ error: `No supported package manager detected \u2014 ${command} unavailable.`
4023
4023
  };
4024
4024
  _cache.set(command, result2);
4025
4025
  return result2;
@@ -4029,7 +4029,7 @@ function ensureCommand(command) {
4029
4029
  const result2 = {
4030
4030
  available: false,
4031
4031
  installed: false,
4032
- error: `No package for ${command} on ${pm}. Install manually.`
4032
+ error: `No package mapping for ${command} on ${pm} \u2014 cannot auto-install.`
4033
4033
  };
4034
4034
  _cache.set(command, result2);
4035
4035
  return result2;
@@ -6474,6 +6474,17 @@ async function probeStation(endpoint) {
6474
6474
  }
6475
6475
  }
6476
6476
  function findStationBinary() {
6477
+ const oaVenvPython = join15(process.env["HOME"] || "/root", ".open-agents", "venv", "bin", "python");
6478
+ if (existsSync11(oaVenvPython)) {
6479
+ try {
6480
+ execSync10(`${JSON.stringify(oaVenvPython)} -c "import moondream_station"`, { stdio: "pipe", timeout: 5e3 });
6481
+ return oaVenvPython;
6482
+ } catch {
6483
+ }
6484
+ }
6485
+ const oaVenvBin = join15(process.env["HOME"] || "/root", ".open-agents", "venv", "bin", "moondream-station");
6486
+ if (existsSync11(oaVenvBin))
6487
+ return oaVenvBin;
6477
6488
  const thisDir = dirname4(fileURLToPath(import.meta.url));
6478
6489
  const localVenvPaths = [
6479
6490
  resolve15(thisDir, "../../../../.moondream-venv/bin/python"),
@@ -10402,32 +10413,31 @@ Rules:
10402
10413
  * Build a self-eval prompt for the agent when approaching timeout.
10403
10414
  * Returns the prompt to inject. The agent will respond with a plan.
10404
10415
  */
10405
- buildTimeoutSelfEvalPrompt(elapsedMs2, toolCallCount, repetitionScore, remainingMs) {
10416
+ buildHealthCheckPrompt(elapsedMs2, toolCallCount, repetitionScore, checkNumber) {
10406
10417
  const elapsedMin = (elapsedMs2 / 6e4).toFixed(1);
10407
- const remainingMin = (remainingMs / 6e4).toFixed(1);
10408
10418
  const stuckWarning = repetitionScore > 0.5 ? `
10409
10419
  \u26A0 REPETITION DETECTED: Your recent tool calls are ${Math.round(repetitionScore * 100)}% repetitive. You may be stuck in a loop.` : "";
10410
- return `[TIMEOUT APPROACHING \u2014 Self-Assessment Required]
10420
+ return `[HEALTH CHECK #${checkNumber} \u2014 Progress Assessment]
10411
10421
 
10412
- 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}
10413
10423
 
10414
- ASSESS YOUR SITUATION and choose ONE action:
10424
+ Briefly assess your situation and choose ONE action:
10415
10425
 
10416
- 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.
10417
10427
 
10418
- 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.
10419
10429
 
10420
- 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.
10421
10431
 
10422
- 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.`;
10423
10433
  }
10424
10434
  /** Run a task through the agentic loop */
10425
10435
  async run(task, context) {
10426
10436
  const start = Date.now();
10427
10437
  const taskTimeoutMs = this.options.taskTimeoutMs;
10428
- const softDeadline = start + Math.floor(taskTimeoutMs * 0.8);
10429
- const hardDeadline = start + Math.floor(taskTimeoutMs * 1.5);
10430
- let softTimeoutTriggered = false;
10438
+ const selfEvalInterval = taskTimeoutMs;
10439
+ let nextSelfEval = start + selfEvalInterval;
10440
+ let selfEvalCount = 0;
10431
10441
  const toolCallLog = [];
10432
10442
  this.aborted = false;
10433
10443
  this.pendingUserMessages.length = 0;
@@ -10456,24 +10466,20 @@ TASK: ${task}` : task }
10456
10466
  break;
10457
10467
  }
10458
10468
  const now = Date.now();
10459
- if (now > hardDeadline) {
10460
- this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10461
- break;
10462
- }
10463
- if (!softTimeoutTriggered && now > softDeadline) {
10464
- softTimeoutTriggered = true;
10469
+ if (now > nextSelfEval) {
10470
+ selfEvalCount++;
10465
10471
  const elapsed = now - start;
10466
- const remaining = hardDeadline - now;
10467
10472
  const repetitionScore = this.detectRepetition(toolCallLog);
10468
10473
  this.emit({
10469
10474
  type: "compaction",
10470
- 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`,
10471
10476
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10472
10477
  });
10473
10478
  messages.push({
10474
10479
  role: "user",
10475
- content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
10480
+ content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
10476
10481
  });
10482
+ nextSelfEval = now + selfEvalInterval;
10477
10483
  }
10478
10484
  while (this.pendingUserMessages.length > 0) {
10479
10485
  const userMsg = this.pendingUserMessages.shift();
@@ -10682,7 +10688,7 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
10682
10688
  });
10683
10689
  }
10684
10690
  }
10685
- 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) {
10686
10692
  bruteForceCycle++;
10687
10693
  const totalTurns = messages.filter((m) => m.role === "assistant").length;
10688
10694
  this.emit({
@@ -10716,24 +10722,20 @@ You have ${this.options.maxTurns} more turns. Continue making progress. Call tas
10716
10722
  break;
10717
10723
  }
10718
10724
  const bfNow = Date.now();
10719
- if (bfNow > hardDeadline) {
10720
- this.emit({ type: "error", content: "Task hard timeout reached", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
10721
- break;
10722
- }
10723
- if (!softTimeoutTriggered && bfNow > softDeadline) {
10724
- softTimeoutTriggered = true;
10725
+ if (bfNow > nextSelfEval) {
10726
+ selfEvalCount++;
10725
10727
  const elapsed = bfNow - start;
10726
- const remaining = hardDeadline - bfNow;
10727
10728
  const repetitionScore = this.detectRepetition(toolCallLog);
10728
10729
  this.emit({
10729
10730
  type: "compaction",
10730
- 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`,
10731
10732
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
10732
10733
  });
10733
10734
  messages.push({
10734
10735
  role: "user",
10735
- content: this.buildTimeoutSelfEvalPrompt(elapsed, toolCallCount, repetitionScore, remaining)
10736
+ content: this.buildHealthCheckPrompt(elapsed, toolCallCount, repetitionScore, selfEvalCount)
10736
10737
  });
10738
+ nextSelfEval = bfNow + selfEvalInterval;
10737
10739
  }
10738
10740
  while (this.pendingUserMessages.length > 0) {
10739
10741
  const userMsg = this.pendingUserMessages.shift();
@@ -11405,8 +11407,7 @@ ${newerSummary}` : newerSummary;
11405
11407
  const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
11406
11408
  method: "POST",
11407
11409
  headers: this.authHeaders(),
11408
- body: JSON.stringify(body),
11409
- signal: AbortSignal.timeout(request.timeoutMs)
11410
+ body: JSON.stringify(body)
11410
11411
  });
11411
11412
  if (!resp.ok) {
11412
11413
  const text = await resp.text().catch(() => "");
@@ -11466,8 +11467,7 @@ ${newerSummary}` : newerSummary;
11466
11467
  const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, {
11467
11468
  method: "POST",
11468
11469
  headers: this.authHeaders(),
11469
- body: JSON.stringify(body),
11470
- signal: AbortSignal.timeout(request.timeoutMs)
11470
+ body: JSON.stringify(body)
11471
11471
  });
11472
11472
  if (!resp.ok) {
11473
11473
  const text = await resp.text().catch(() => "");
@@ -14761,62 +14761,220 @@ function isFirstRun() {
14761
14761
  return true;
14762
14762
  }
14763
14763
  }
14764
- async function ensureVisionDeps(onInfo) {
14765
- const log = onInfo ?? (() => {
14766
- });
14764
+ function hasCmd(cmd) {
14767
14765
  try {
14768
- execSync13("which tesseract", { stdio: "pipe", timeout: 3e3 });
14766
+ execSync13(`which ${cmd}`, { stdio: "pipe", timeout: 3e3 });
14767
+ return true;
14769
14768
  } catch {
14770
- log("Installing tesseract-ocr...");
14771
- try {
14772
- const cmds = [
14773
- "sudo -n apt-get install -y tesseract-ocr 2>/dev/null",
14774
- "sudo -n dnf install -y tesseract 2>/dev/null",
14775
- "sudo -n pacman -S --noconfirm tesseract 2>/dev/null",
14776
- "brew install tesseract 2>/dev/null"
14777
- ];
14778
- let installed = false;
14779
- for (const cmd of cmds) {
14769
+ return false;
14770
+ }
14771
+ }
14772
+ function detectPkgManager() {
14773
+ if (hasCmd("apt-get"))
14774
+ return "apt";
14775
+ if (hasCmd("dnf"))
14776
+ return "dnf";
14777
+ if (hasCmd("pacman"))
14778
+ return "pacman";
14779
+ if (hasCmd("brew"))
14780
+ return "brew";
14781
+ return null;
14782
+ }
14783
+ function getVenvDir() {
14784
+ return join22(homedir8(), ".open-agents", "venv");
14785
+ }
14786
+ function hasVenvModule() {
14787
+ try {
14788
+ execSync13("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
14789
+ return true;
14790
+ } catch {
14791
+ return false;
14792
+ }
14793
+ }
14794
+ function ensureVenv(log) {
14795
+ const venvDir = getVenvDir();
14796
+ const venvPip = join22(venvDir, "bin", "pip");
14797
+ if (existsSync15(venvPip))
14798
+ return venvDir;
14799
+ log("Creating Python venv for vision deps...");
14800
+ if (!hasCmd("python3")) {
14801
+ log("python3 not found \u2014 cannot create venv.");
14802
+ return null;
14803
+ }
14804
+ if (!hasVenvModule()) {
14805
+ log("python3 venv module not available \u2014 venv creation skipped.");
14806
+ return null;
14807
+ }
14808
+ try {
14809
+ mkdirSync7(join22(homedir8(), ".open-agents"), { recursive: true });
14810
+ execSync13(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
14811
+ execSync13(`"${join22(venvDir, "bin", "pip")}" install --upgrade pip`, {
14812
+ stdio: "pipe",
14813
+ timeout: 6e4
14814
+ });
14815
+ log("Python venv created at ~/.open-agents/venv");
14816
+ return venvDir;
14817
+ } catch (err) {
14818
+ log(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
14819
+ return null;
14820
+ }
14821
+ }
14822
+ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
14823
+ try {
14824
+ execSync13(`sudo -n ${cmd}`, {
14825
+ stdio: "pipe",
14826
+ timeout: timeoutMs,
14827
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
14828
+ });
14829
+ return true;
14830
+ } catch {
14831
+ return false;
14832
+ }
14833
+ }
14834
+ function runWithSudo(cmd, password, timeoutMs = 12e4) {
14835
+ try {
14836
+ execSync13(`sudo -S ${cmd}`, {
14837
+ input: password + "\n",
14838
+ stdio: ["pipe", "pipe", "pipe"],
14839
+ timeout: timeoutMs,
14840
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
14841
+ });
14842
+ return true;
14843
+ } catch {
14844
+ return false;
14845
+ }
14846
+ }
14847
+ async function sudoInstall(cmd, getSudoPassword, log, cachedPasswordRef, timeoutMs = 12e4) {
14848
+ if (cachedPasswordRef.value) {
14849
+ if (runWithSudo(cmd, cachedPasswordRef.value, timeoutMs))
14850
+ return true;
14851
+ }
14852
+ if (trySudoPasswordless(cmd, timeoutMs))
14853
+ return true;
14854
+ const pw = await getSudoPassword();
14855
+ if (pw) {
14856
+ cachedPasswordRef.value = pw;
14857
+ if (runWithSudo(cmd, pw, timeoutMs))
14858
+ return true;
14859
+ log("Authentication failed \u2014 please re-enter password.");
14860
+ const pw2 = await getSudoPassword();
14861
+ if (pw2) {
14862
+ cachedPasswordRef.value = pw2;
14863
+ if (runWithSudo(cmd, pw2, timeoutMs))
14864
+ return true;
14865
+ }
14866
+ }
14867
+ return false;
14868
+ }
14869
+ async function ensureVisionDeps(onInfo, getSudoPassword) {
14870
+ const log = onInfo ?? (() => {
14871
+ });
14872
+ const cachedPasswordRef = { value: null };
14873
+ const getPassword = getSudoPassword ?? (() => Promise.resolve(null));
14874
+ if (!hasCmd("tesseract")) {
14875
+ const pm2 = detectPkgManager();
14876
+ if (pm2) {
14877
+ const pkgMap = {
14878
+ apt: { cmd: "apt-get install -y tesseract-ocr", needsSudo: true },
14879
+ dnf: { cmd: "dnf install -y tesseract", needsSudo: true },
14880
+ pacman: { cmd: "pacman -S --noconfirm tesseract", needsSudo: true },
14881
+ brew: { cmd: "brew install tesseract", needsSudo: false }
14882
+ };
14883
+ const pkg = pkgMap[pm2];
14884
+ if (pkg.needsSudo) {
14885
+ log("Installing tesseract-ocr...");
14886
+ const ok = await sudoInstall(pkg.cmd, getPassword, log, cachedPasswordRef);
14887
+ if (ok && hasCmd("tesseract")) {
14888
+ log("tesseract-ocr installed successfully.");
14889
+ } else {
14890
+ log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
14891
+ }
14892
+ } else {
14893
+ log("Installing tesseract-ocr...");
14780
14894
  try {
14781
- execSync13(cmd, { stdio: "pipe", timeout: 6e4 });
14782
- installed = true;
14783
- break;
14895
+ execSync13(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
14896
+ if (hasCmd("tesseract")) {
14897
+ log("tesseract-ocr installed successfully.");
14898
+ } else {
14899
+ log("tesseract-ocr install completed but binary not found \u2014 OCR features will be limited.");
14900
+ }
14784
14901
  } catch {
14902
+ log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
14785
14903
  }
14786
14904
  }
14787
- if (installed)
14788
- log("Tesseract installed.");
14789
- else
14790
- log("Could not auto-install tesseract (install manually: sudo apt install tesseract-ocr)");
14791
- } catch {
14905
+ } else {
14906
+ log("No supported package manager detected \u2014 tesseract OCR unavailable.");
14792
14907
  }
14793
14908
  }
14794
- try {
14795
- execSync13("which moondream-station", { stdio: "pipe", timeout: 3e3 });
14796
- } catch {
14797
- log("Installing moondream-station...");
14798
- try {
14799
- const pipCmds = [
14800
- "pip3 install moondream-station 2>/dev/null",
14801
- "pip install moondream-station 2>/dev/null",
14802
- "python3 -m pip install moondream-station 2>/dev/null"
14803
- ];
14804
- let installed = false;
14805
- for (const cmd of pipCmds) {
14909
+ const pm = detectPkgManager();
14910
+ if (!hasCmd("pip3") && !hasCmd("pip") && pm) {
14911
+ const pipCmds = {
14912
+ apt: "apt-get install -y python3-pip",
14913
+ dnf: "dnf install -y python3-pip"
14914
+ };
14915
+ const pipCmd = pipCmds[pm];
14916
+ if (pipCmd) {
14917
+ log("Installing python3-pip...");
14918
+ const ok = await sudoInstall(pipCmd, getPassword, log, cachedPasswordRef);
14919
+ if (!ok) {
14920
+ log("python3-pip could not be installed \u2014 moondream-station may be unavailable.");
14921
+ }
14922
+ }
14923
+ }
14924
+ if (hasCmd("python3") && !hasVenvModule() && pm) {
14925
+ const venvCmds = {
14926
+ apt: () => {
14806
14927
  try {
14807
- execSync13(cmd, { stdio: "pipe", timeout: 12e4 });
14808
- installed = true;
14809
- break;
14928
+ const pyVer = execSync13(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
14929
+ return `apt-get install -y python3-venv python${pyVer}-venv`;
14810
14930
  } catch {
14931
+ return "apt-get install -y python3-venv";
14932
+ }
14933
+ },
14934
+ dnf: () => "dnf install -y python3-venv"
14935
+ };
14936
+ const cmdFn = venvCmds[pm];
14937
+ if (cmdFn) {
14938
+ const cmd = cmdFn();
14939
+ if (cmd) {
14940
+ log("Installing python3-venv...");
14941
+ const ok = await sudoInstall(cmd, getPassword, log, cachedPasswordRef);
14942
+ if (!ok) {
14943
+ log("python3-venv could not be installed \u2014 moondream-station may be unavailable.");
14811
14944
  }
14812
14945
  }
14813
- if (installed)
14814
- log("moondream-station installed.");
14815
- else
14816
- log("Could not auto-install moondream-station (install manually: pip install moondream-station)");
14817
- } catch {
14818
14946
  }
14819
14947
  }
14948
+ const venvDir = getVenvDir();
14949
+ const venvBin = join22(venvDir, "bin");
14950
+ const venvMoondream = join22(venvBin, "moondream-station");
14951
+ if (hasCmd("moondream-station") || existsSync15(venvMoondream)) {
14952
+ return;
14953
+ }
14954
+ const venv = ensureVenv(log);
14955
+ if (!venv) {
14956
+ log("Python venv unavailable \u2014 moondream-station will not be installed.");
14957
+ return;
14958
+ }
14959
+ const venvPip = join22(venvBin, "pip");
14960
+ log("Installing moondream-station in ~/.open-agents/venv...");
14961
+ try {
14962
+ execSync13(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
14963
+ if (existsSync15(venvMoondream)) {
14964
+ log("moondream-station installed successfully.");
14965
+ } else {
14966
+ try {
14967
+ const check = execSync13(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
14968
+ if (check.includes("moondream")) {
14969
+ log("moondream-station package installed.");
14970
+ }
14971
+ } catch {
14972
+ log("moondream-station install completed.");
14973
+ }
14974
+ }
14975
+ } catch (err) {
14976
+ log(`moondream-station install failed: ${err instanceof Error ? err.message : String(err)}`);
14977
+ }
14820
14978
  }
14821
14979
  function expandedModelName(baseModel) {
14822
14980
  return `open-agents-${baseModel.replace(":", "-").replace(/\./g, "")}`;
@@ -18825,9 +18983,28 @@ var init_status_bar = __esm({
18825
18983
  this.metrics.totalTokens = update.totalTokens;
18826
18984
  if (update.estimatedContextTokens !== void 0)
18827
18985
  this.metrics.estimatedContextTokens = update.estimatedContextTokens;
18986
+ this._streamingTokens = 0;
18828
18987
  if (this.active)
18829
18988
  this.renderFooterPreserveCursor();
18830
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
+ }
18831
19008
  /** Reset metrics (e.g. on session start) */
18832
19009
  resetMetrics() {
18833
19010
  this.metrics.promptTokens = 0;
@@ -18953,7 +19130,8 @@ var init_status_bar = __esm({
18953
19130
  const pipe = pastel2(60, " \u2502 ");
18954
19131
  const tokIn = m.promptTokens > 0 ? m.promptTokens.toLocaleString() : `~${Math.max(m.estimatedContextTokens, 0).toLocaleString()}`;
18955
19132
  const tokInLabel = pastel2(117, "In: ") + c2.bold(tokIn);
18956
- 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()}`;
18957
19135
  const tokOutLabel = pastel2(151, "Out: ") + c2.bold(tokOut);
18958
19136
  const ctxUsed = m.estimatedContextTokens;
18959
19137
  const ctxTotal = m.contextWindowSize;
@@ -19336,7 +19514,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
19336
19514
  streamEnabled: stream?.enabled ?? false,
19337
19515
  bruteForce: bruteForce ?? true,
19338
19516
  bruteForceMaxCycles: 100,
19339
- // effectively unlimited — hard timeout is the real bound
19517
+ // effectively unlimited — no hard timeout, agent runs until complete or aborted
19340
19518
  contextWindowSize: contextWindowSize ?? 0
19341
19519
  });
19342
19520
  const tools = buildTools(repoRoot, config);
@@ -19425,6 +19603,10 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
19425
19603
  if (stream?.enabled) {
19426
19604
  stream.renderer.write(event.content ?? "", event.streamKind ?? "content");
19427
19605
  }
19606
+ if (statusBar && event.content) {
19607
+ const estimatedNewTokens = Math.max(1, Math.ceil(event.content.length / 4));
19608
+ statusBar.incrementStreamingTokens(estimatedNewTokens);
19609
+ }
19428
19610
  break;
19429
19611
  case "stream_end":
19430
19612
  if (stream?.enabled) {
@@ -19667,13 +19849,33 @@ async function startInteractive(config, repoPath) {
19667
19849
  const sessionMetrics = new SessionMetrics();
19668
19850
  const workEvaluator = new WorkEvaluator();
19669
19851
  ensureTranscribeCliBackground();
19852
+ let depSudoResolver = null;
19853
+ let depSudoPromptPending = false;
19670
19854
  ensureVisionDeps((msg) => {
19671
19855
  if (statusBar?.isActive) {
19672
19856
  statusBar.beginContentWrite();
19673
19857
  renderInfo(msg);
19674
19858
  statusBar.endContentWrite();
19675
19859
  }
19676
- }).catch(() => {
19860
+ }, () => new Promise((resolve19) => {
19861
+ depSudoPromptPending = true;
19862
+ depSudoResolver = (pw) => {
19863
+ depSudoPromptPending = false;
19864
+ depSudoResolver = null;
19865
+ if (pw)
19866
+ sessionSudoPassword = pw;
19867
+ resolve19(pw);
19868
+ };
19869
+ if (statusBar?.isActive) {
19870
+ statusBar.beginContentWrite();
19871
+ }
19872
+ process.stdout.write(` ${c2.bold(c2.yellow("\u{1F511} Password needed for dependency install:"))}
19873
+ `);
19874
+ process.stdout.write(` ${c2.bold(c2.yellow("\u{1F511} Password:"))} `);
19875
+ if (statusBar?.isActive) {
19876
+ statusBar.endContentWrite();
19877
+ }
19878
+ })).catch(() => {
19677
19879
  });
19678
19880
  const voiceEngine = new VoiceEngine();
19679
19881
  const streamRenderer = new StreamRenderer();
@@ -20070,6 +20272,14 @@ async function startInteractive(config, repoPath) {
20070
20272
  }, 50);
20071
20273
  });
20072
20274
  async function processLine(input) {
20275
+ if (depSudoPromptPending && depSudoResolver) {
20276
+ const pw = input.trim();
20277
+ process.stdout.write(`\r\x1B[K ${c2.dim("\u{1F511} Password received")}
20278
+ `);
20279
+ depSudoResolver(pw || null);
20280
+ showPrompt();
20281
+ return;
20282
+ }
20073
20283
  if (sudoPromptPending && activeTask) {
20074
20284
  sudoPromptPending = false;
20075
20285
  sessionSudoPassword = input;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.27.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",