open-agents-ai 0.26.0 → 0.28.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 +393 -11
  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"),
@@ -6635,9 +6646,18 @@ var init_vision = __esm({
6635
6646
  },
6636
6647
  required: ["image"]
6637
6648
  };
6649
+ /** Active Ollama model name — used as vision fallback if it has vision capability */
6650
+ _activeModel = "";
6651
+ /** Whether the active model has vision capability */
6652
+ _activeModelHasVision = false;
6638
6653
  constructor(workingDir) {
6639
6654
  this.workingDir = workingDir;
6640
6655
  }
6656
+ /** Set the active Ollama model and whether it supports vision */
6657
+ setActiveModel(model, hasVision) {
6658
+ this._activeModel = model;
6659
+ this._activeModelHasVision = hasVision;
6660
+ }
6641
6661
  async execute(args) {
6642
6662
  const start = performance.now();
6643
6663
  const rawPath = args["image"];
@@ -6738,7 +6758,8 @@ Coordinates are normalized (0-1). Multiply by image width/height for pixel value
6738
6758
  }
6739
6759
  async tryOllamaVision(buffer, filename, action, prompt, length, start) {
6740
6760
  const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
6741
- const model = process.env["OLLAMA_VISION_MODEL"] || "moondream";
6761
+ const envModel = process.env["OLLAMA_VISION_MODEL"];
6762
+ const model = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
6742
6763
  const imageBase64 = buffer.toString("base64");
6743
6764
  let ollamaPrompt;
6744
6765
  switch (action) {
@@ -6965,7 +6986,16 @@ var init_desktop_click = __esm({
6965
6986
  DesktopClickTool = class {
6966
6987
  workingDir;
6967
6988
  name = "desktop_click";
6968
- description = "Click on a UI element identified by natural language description. Takes a screenshot, uses Moondream vision to find the described element, then clicks at that location using xdotool (Linux) or cliclick (macOS). Example: desktop_click({ target: 'the Save button' })";
6989
+ description = "Click on a UI element identified by natural language description. Takes a screenshot, uses vision to find the described element, then clicks at that location using xdotool (Linux) or cliclick (macOS). Example: desktop_click({ target: 'the Save button' })";
6990
+ /** Active Ollama model name — used as vision fallback if it has vision capability */
6991
+ _activeModel = "";
6992
+ /** Whether the active model has vision capability */
6993
+ _activeModelHasVision = false;
6994
+ /** Set the active Ollama model and whether it supports vision */
6995
+ setActiveModel(model, hasVision) {
6996
+ this._activeModel = model;
6997
+ this._activeModelHasVision = hasVision;
6998
+ }
6969
6999
  parameters = {
6970
7000
  type: "object",
6971
7001
  properties: {
@@ -7050,7 +7080,8 @@ var init_desktop_click = __esm({
7050
7080
  if (!visionWorked) {
7051
7081
  try {
7052
7082
  const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
7053
- const ollamaModel = process.env["OLLAMA_VISION_MODEL"] || "moondream";
7083
+ const envModel = process.env["OLLAMA_VISION_MODEL"];
7084
+ const ollamaModel = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
7054
7085
  const imageBase64 = readFileSync11(screenshotPath).toString("base64");
7055
7086
  const res = await fetch(`${ollamaHost}/api/generate`, {
7056
7087
  method: "POST",
@@ -7148,7 +7179,16 @@ Screenshot: ${screenshotPath}`,
7148
7179
  DesktopDescribeTool = class {
7149
7180
  workingDir;
7150
7181
  name = "desktop_describe";
7151
- description = "Take a screenshot and describe what's on the desktop using Moondream vision. Optionally ask a specific question about the screen contents. Use this to become aware of the desktop environment.";
7182
+ description = "Take a screenshot and describe what's on the desktop using vision. Optionally ask a specific question about the screen contents. Use this to become aware of the desktop environment.";
7183
+ /** Active Ollama model name — used as vision fallback if it has vision capability */
7184
+ _activeModel = "";
7185
+ /** Whether the active model has vision capability */
7186
+ _activeModelHasVision = false;
7187
+ /** Set the active Ollama model and whether it supports vision */
7188
+ setActiveModel(model, hasVision) {
7189
+ this._activeModel = model;
7190
+ this._activeModelHasVision = hasVision;
7191
+ }
7152
7192
  parameters = {
7153
7193
  type: "object",
7154
7194
  properties: {
@@ -7214,7 +7254,8 @@ ${caption}`);
7214
7254
  if (!visionWorked) {
7215
7255
  try {
7216
7256
  const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
7217
- const ollamaModel = process.env["OLLAMA_VISION_MODEL"] || "moondream";
7257
+ const envModel = process.env["OLLAMA_VISION_MODEL"];
7258
+ const ollamaModel = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
7218
7259
  const imageBase64 = imageBuffer.toString("base64");
7219
7260
  const ollamaPrompt = question || "Describe what you see on this desktop screenshot in detail. Include visible applications, windows, text, and UI elements.";
7220
7261
  const res = await fetch(`${ollamaHost}/api/generate`, {
@@ -12783,6 +12824,52 @@ async function queryContextSize(baseUrl, modelName, apiKey) {
12783
12824
  return ollamaSize;
12784
12825
  return queryOpenAIContextSize(baseUrl, modelName, apiKey);
12785
12826
  }
12827
+ async function queryModelCapabilities(baseUrl, modelName) {
12828
+ const caps = { vision: false, toolUse: false, thinking: false };
12829
+ try {
12830
+ const normalized = normalizeBaseUrl(baseUrl);
12831
+ const res = await fetch(`${normalized}/api/show`, {
12832
+ method: "POST",
12833
+ headers: { "Content-Type": "application/json" },
12834
+ body: JSON.stringify({ name: modelName }),
12835
+ signal: AbortSignal.timeout(1e4)
12836
+ });
12837
+ if (!res.ok)
12838
+ return caps;
12839
+ const data = await res.json();
12840
+ if (Array.isArray(data.capabilities)) {
12841
+ if (data.capabilities.includes("vision"))
12842
+ caps.vision = true;
12843
+ if (data.capabilities.includes("tools"))
12844
+ caps.toolUse = true;
12845
+ if (data.capabilities.includes("thinking"))
12846
+ caps.thinking = true;
12847
+ }
12848
+ if (data.model_info) {
12849
+ for (const key of Object.keys(data.model_info)) {
12850
+ const k = key.toLowerCase();
12851
+ if (k.includes("vision.block_count") || k.includes("clip.") || k.includes("image_token_id") || k.includes("projector") || k.includes("vision.embedding_length")) {
12852
+ const val = data.model_info[key];
12853
+ if (val !== null && val !== void 0 && val !== 0 && val !== "") {
12854
+ caps.vision = true;
12855
+ }
12856
+ }
12857
+ }
12858
+ }
12859
+ const nameLower = modelName.toLowerCase();
12860
+ if (/qwen3|qwen2\.5|llama3\.[13]|mistral|mixtral|command-r|gemma3|devstral|deepseek/.test(nameLower)) {
12861
+ caps.toolUse = true;
12862
+ }
12863
+ if (data.template) {
12864
+ if (data.template.includes("<think>") || data.template.includes("thinking")) {
12865
+ caps.thinking = true;
12866
+ }
12867
+ }
12868
+ return caps;
12869
+ } catch {
12870
+ return caps;
12871
+ }
12872
+ }
12786
12873
  function formatBytes(bytes) {
12787
12874
  if (bytes < 1024)
12788
12875
  return `${bytes} B`;
@@ -14685,6 +14772,221 @@ function isFirstRun() {
14685
14772
  return true;
14686
14773
  }
14687
14774
  }
14775
+ function hasCmd(cmd) {
14776
+ try {
14777
+ execSync13(`which ${cmd}`, { stdio: "pipe", timeout: 3e3 });
14778
+ return true;
14779
+ } catch {
14780
+ return false;
14781
+ }
14782
+ }
14783
+ function detectPkgManager() {
14784
+ if (hasCmd("apt-get"))
14785
+ return "apt";
14786
+ if (hasCmd("dnf"))
14787
+ return "dnf";
14788
+ if (hasCmd("pacman"))
14789
+ return "pacman";
14790
+ if (hasCmd("brew"))
14791
+ return "brew";
14792
+ return null;
14793
+ }
14794
+ function getVenvDir() {
14795
+ return join22(homedir8(), ".open-agents", "venv");
14796
+ }
14797
+ function hasVenvModule() {
14798
+ try {
14799
+ execSync13("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
14800
+ return true;
14801
+ } catch {
14802
+ return false;
14803
+ }
14804
+ }
14805
+ function ensureVenv(log) {
14806
+ const venvDir = getVenvDir();
14807
+ const venvPip = join22(venvDir, "bin", "pip");
14808
+ if (existsSync15(venvPip))
14809
+ return venvDir;
14810
+ log("Creating Python venv for vision deps...");
14811
+ if (!hasCmd("python3")) {
14812
+ log("python3 not found \u2014 cannot create venv.");
14813
+ return null;
14814
+ }
14815
+ if (!hasVenvModule()) {
14816
+ log("python3 venv module not available \u2014 venv creation skipped.");
14817
+ return null;
14818
+ }
14819
+ try {
14820
+ mkdirSync7(join22(homedir8(), ".open-agents"), { recursive: true });
14821
+ execSync13(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
14822
+ execSync13(`"${join22(venvDir, "bin", "pip")}" install --upgrade pip`, {
14823
+ stdio: "pipe",
14824
+ timeout: 6e4
14825
+ });
14826
+ log("Python venv created at ~/.open-agents/venv");
14827
+ return venvDir;
14828
+ } catch (err) {
14829
+ log(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
14830
+ return null;
14831
+ }
14832
+ }
14833
+ function trySudoPasswordless(cmd, timeoutMs = 12e4) {
14834
+ try {
14835
+ execSync13(`sudo -n ${cmd}`, {
14836
+ stdio: "pipe",
14837
+ timeout: timeoutMs,
14838
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
14839
+ });
14840
+ return true;
14841
+ } catch {
14842
+ return false;
14843
+ }
14844
+ }
14845
+ function runWithSudo(cmd, password, timeoutMs = 12e4) {
14846
+ try {
14847
+ execSync13(`sudo -S ${cmd}`, {
14848
+ input: password + "\n",
14849
+ stdio: ["pipe", "pipe", "pipe"],
14850
+ timeout: timeoutMs,
14851
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
14852
+ });
14853
+ return true;
14854
+ } catch {
14855
+ return false;
14856
+ }
14857
+ }
14858
+ async function sudoInstall(cmd, getSudoPassword, log, cachedPasswordRef, timeoutMs = 12e4) {
14859
+ if (cachedPasswordRef.value) {
14860
+ if (runWithSudo(cmd, cachedPasswordRef.value, timeoutMs))
14861
+ return true;
14862
+ }
14863
+ if (trySudoPasswordless(cmd, timeoutMs))
14864
+ return true;
14865
+ const pw = await getSudoPassword();
14866
+ if (pw) {
14867
+ cachedPasswordRef.value = pw;
14868
+ if (runWithSudo(cmd, pw, timeoutMs))
14869
+ return true;
14870
+ log("Authentication failed \u2014 please re-enter password.");
14871
+ const pw2 = await getSudoPassword();
14872
+ if (pw2) {
14873
+ cachedPasswordRef.value = pw2;
14874
+ if (runWithSudo(cmd, pw2, timeoutMs))
14875
+ return true;
14876
+ }
14877
+ }
14878
+ return false;
14879
+ }
14880
+ async function ensureVisionDeps(onInfo, getSudoPassword) {
14881
+ const log = onInfo ?? (() => {
14882
+ });
14883
+ const cachedPasswordRef = { value: null };
14884
+ const getPassword = getSudoPassword ?? (() => Promise.resolve(null));
14885
+ if (!hasCmd("tesseract")) {
14886
+ const pm2 = detectPkgManager();
14887
+ if (pm2) {
14888
+ const pkgMap = {
14889
+ apt: { cmd: "apt-get install -y tesseract-ocr", needsSudo: true },
14890
+ dnf: { cmd: "dnf install -y tesseract", needsSudo: true },
14891
+ pacman: { cmd: "pacman -S --noconfirm tesseract", needsSudo: true },
14892
+ brew: { cmd: "brew install tesseract", needsSudo: false }
14893
+ };
14894
+ const pkg = pkgMap[pm2];
14895
+ if (pkg.needsSudo) {
14896
+ log("Installing tesseract-ocr...");
14897
+ const ok = await sudoInstall(pkg.cmd, getPassword, log, cachedPasswordRef);
14898
+ if (ok && hasCmd("tesseract")) {
14899
+ log("tesseract-ocr installed successfully.");
14900
+ } else {
14901
+ log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
14902
+ }
14903
+ } else {
14904
+ log("Installing tesseract-ocr...");
14905
+ try {
14906
+ execSync13(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
14907
+ if (hasCmd("tesseract")) {
14908
+ log("tesseract-ocr installed successfully.");
14909
+ } else {
14910
+ log("tesseract-ocr install completed but binary not found \u2014 OCR features will be limited.");
14911
+ }
14912
+ } catch {
14913
+ log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
14914
+ }
14915
+ }
14916
+ } else {
14917
+ log("No supported package manager detected \u2014 tesseract OCR unavailable.");
14918
+ }
14919
+ }
14920
+ const pm = detectPkgManager();
14921
+ if (!hasCmd("pip3") && !hasCmd("pip") && pm) {
14922
+ const pipCmds = {
14923
+ apt: "apt-get install -y python3-pip",
14924
+ dnf: "dnf install -y python3-pip"
14925
+ };
14926
+ const pipCmd = pipCmds[pm];
14927
+ if (pipCmd) {
14928
+ log("Installing python3-pip...");
14929
+ const ok = await sudoInstall(pipCmd, getPassword, log, cachedPasswordRef);
14930
+ if (!ok) {
14931
+ log("python3-pip could not be installed \u2014 moondream-station may be unavailable.");
14932
+ }
14933
+ }
14934
+ }
14935
+ if (hasCmd("python3") && !hasVenvModule() && pm) {
14936
+ const venvCmds = {
14937
+ apt: () => {
14938
+ try {
14939
+ const pyVer = execSync13(`python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 }).trim();
14940
+ return `apt-get install -y python3-venv python${pyVer}-venv`;
14941
+ } catch {
14942
+ return "apt-get install -y python3-venv";
14943
+ }
14944
+ },
14945
+ dnf: () => "dnf install -y python3-venv"
14946
+ };
14947
+ const cmdFn = venvCmds[pm];
14948
+ if (cmdFn) {
14949
+ const cmd = cmdFn();
14950
+ if (cmd) {
14951
+ log("Installing python3-venv...");
14952
+ const ok = await sudoInstall(cmd, getPassword, log, cachedPasswordRef);
14953
+ if (!ok) {
14954
+ log("python3-venv could not be installed \u2014 moondream-station may be unavailable.");
14955
+ }
14956
+ }
14957
+ }
14958
+ }
14959
+ const venvDir = getVenvDir();
14960
+ const venvBin = join22(venvDir, "bin");
14961
+ const venvMoondream = join22(venvBin, "moondream-station");
14962
+ if (hasCmd("moondream-station") || existsSync15(venvMoondream)) {
14963
+ return;
14964
+ }
14965
+ const venv = ensureVenv(log);
14966
+ if (!venv) {
14967
+ log("Python venv unavailable \u2014 moondream-station will not be installed.");
14968
+ return;
14969
+ }
14970
+ const venvPip = join22(venvBin, "pip");
14971
+ log("Installing moondream-station in ~/.open-agents/venv...");
14972
+ try {
14973
+ execSync13(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
14974
+ if (existsSync15(venvMoondream)) {
14975
+ log("moondream-station installed successfully.");
14976
+ } else {
14977
+ try {
14978
+ const check = execSync13(`"${venvPip}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
14979
+ if (check.includes("moondream")) {
14980
+ log("moondream-station package installed.");
14981
+ }
14982
+ } catch {
14983
+ log("moondream-station install completed.");
14984
+ }
14985
+ }
14986
+ } catch (err) {
14987
+ log(`moondream-station install failed: ${err instanceof Error ? err.message : String(err)}`);
14988
+ }
14989
+ }
14688
14990
  function expandedModelName(baseModel) {
14689
14991
  return `open-agents-${baseModel.replace(":", "-").replace(/\./g, "")}`;
14690
14992
  }
@@ -15484,6 +15786,10 @@ async function switchModel(query, ctx, local = false) {
15484
15786
  ctx.setContextWindowSize(ctxSize);
15485
15787
  }
15486
15788
  }
15789
+ if (ctx.setCapabilities) {
15790
+ const caps = await queryModelCapabilities(ctx.config.backendUrl, finalModel);
15791
+ ctx.setCapabilities(caps);
15792
+ }
15487
15793
  } catch (err) {
15488
15794
  renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
15489
15795
  }
@@ -18666,6 +18972,18 @@ var init_status_bar = __esm({
18666
18972
  setContextWindowSize(size) {
18667
18973
  this.metrics.contextWindowSize = size;
18668
18974
  }
18975
+ /** Model capabilities — shown as emoji indicators on the status bar */
18976
+ _caps = {
18977
+ vision: false,
18978
+ toolUse: false,
18979
+ thinking: false
18980
+ };
18981
+ /** Update model capability indicators */
18982
+ setCapabilities(caps) {
18983
+ this._caps = caps;
18984
+ if (this.active)
18985
+ this.renderFooterPreserveCursor();
18986
+ }
18669
18987
  /** Update token metrics from a token_usage event */
18670
18988
  updateMetrics(update) {
18671
18989
  if (update.promptTokens !== void 0)
@@ -18822,7 +19140,15 @@ var init_status_bar = __esm({
18822
19140
  const countdown = this._countdown > 0 ? c2.dim(` ${this._countdown}s`) : "";
18823
19141
  recordingLabel = pipe + dot + pastel2(210, " REC") + countdown;
18824
19142
  }
18825
- return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${costLabel}${recordingLabel}`;
19143
+ const capParts = [];
19144
+ if (this._caps.vision)
19145
+ capParts.push("\u{1F441}");
19146
+ if (this._caps.toolUse)
19147
+ capParts.push("\u{1F527}");
19148
+ if (this._caps.thinking)
19149
+ capParts.push("\u{1F9E0}");
19150
+ const capsLabel = capParts.length > 0 ? pipe + pastel2(183, capParts.join(" ")) : "";
19151
+ return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${costLabel}${capsLabel}${recordingLabel}`;
18826
19152
  }
18827
19153
  // -------------------------------------------------------------------------
18828
19154
  // Private
@@ -19157,7 +19483,7 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
19157
19483
  }
19158
19484
  };
19159
19485
  }
19160
- function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize) {
19486
+ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps) {
19161
19487
  const modelTier = getModelTier(config.model);
19162
19488
  const projectCtx = buildProjectContext(repoRoot, taskStores?.contextStores);
19163
19489
  let dynamicContext = formatContextForPrompt(projectCtx, modelTier);
@@ -19190,6 +19516,12 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
19190
19516
  }
19191
19517
  }
19192
19518
  }
19519
+ const hasVision = modelCaps?.vision ?? false;
19520
+ for (const tool of tools) {
19521
+ if ("setActiveModel" in tool && typeof tool.setActiveModel === "function") {
19522
+ tool.setActiveModel(config.model, hasVision);
19523
+ }
19524
+ }
19193
19525
  runner.registerTools(tools);
19194
19526
  const filesTouched = /* @__PURE__ */ new Set();
19195
19527
  const toolSequence = [];
@@ -19489,11 +19821,49 @@ async function startInteractive(config, repoPath) {
19489
19821
  }
19490
19822
  }).catch(() => {
19491
19823
  });
19824
+ let resolvedCaps = {
19825
+ vision: false,
19826
+ toolUse: false,
19827
+ thinking: false
19828
+ };
19829
+ queryModelCapabilities(config.backendUrl, config.model).then((caps) => {
19830
+ resolvedCaps = caps;
19831
+ statusBar.setCapabilities(caps);
19832
+ }).catch(() => {
19833
+ });
19492
19834
  const provider = detectProvider(config.backendUrl);
19493
19835
  const costTracker = new CostTracker(provider.id);
19494
19836
  const sessionMetrics = new SessionMetrics();
19495
19837
  const workEvaluator = new WorkEvaluator();
19496
19838
  ensureTranscribeCliBackground();
19839
+ let depSudoResolver = null;
19840
+ let depSudoPromptPending = false;
19841
+ ensureVisionDeps((msg) => {
19842
+ if (statusBar?.isActive) {
19843
+ statusBar.beginContentWrite();
19844
+ renderInfo(msg);
19845
+ statusBar.endContentWrite();
19846
+ }
19847
+ }, () => new Promise((resolve19) => {
19848
+ depSudoPromptPending = true;
19849
+ depSudoResolver = (pw) => {
19850
+ depSudoPromptPending = false;
19851
+ depSudoResolver = null;
19852
+ if (pw)
19853
+ sessionSudoPassword = pw;
19854
+ resolve19(pw);
19855
+ };
19856
+ if (statusBar?.isActive) {
19857
+ statusBar.beginContentWrite();
19858
+ }
19859
+ process.stdout.write(` ${c2.bold(c2.yellow("\u{1F511} Password needed for dependency install:"))}
19860
+ `);
19861
+ process.stdout.write(` ${c2.bold(c2.yellow("\u{1F511} Password:"))} `);
19862
+ if (statusBar?.isActive) {
19863
+ statusBar.endContentWrite();
19864
+ }
19865
+ })).catch(() => {
19866
+ });
19497
19867
  const voiceEngine = new VoiceEngine();
19498
19868
  const streamRenderer = new StreamRenderer();
19499
19869
  if (savedSettings.voice) {
@@ -19791,6 +20161,10 @@ async function startInteractive(config, repoPath) {
19791
20161
  resolvedContextWindowSize = size;
19792
20162
  statusBar.setContextWindowSize(size);
19793
20163
  },
20164
+ setCapabilities: (caps) => {
20165
+ resolvedCaps = caps;
20166
+ statusBar.setCapabilities(caps);
20167
+ },
19794
20168
  hasActiveTask: () => activeTask !== null,
19795
20169
  abortTask() {
19796
20170
  if (!activeTask)
@@ -19885,6 +20259,14 @@ async function startInteractive(config, repoPath) {
19885
20259
  }, 50);
19886
20260
  });
19887
20261
  async function processLine(input) {
20262
+ if (depSudoPromptPending && depSudoResolver) {
20263
+ const pw = input.trim();
20264
+ process.stdout.write(`\r\x1B[K ${c2.dim("\u{1F511} Password received")}
20265
+ `);
20266
+ depSudoResolver(pw || null);
20267
+ showPrompt();
20268
+ return;
20269
+ }
19888
20270
  if (sudoPromptPending && activeTask) {
19889
20271
  sudoPromptPending = false;
19890
20272
  sessionSudoPassword = input;
@@ -19946,7 +20328,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
19946
20328
  toolPatternStore: toolPatternStore ?? void 0
19947
20329
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
19948
20330
  lastCompletedSummary = summary;
19949
- }, currentTaskType, resolvedContextWindowSize);
20331
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps);
19950
20332
  activeTask = task;
19951
20333
  showPrompt();
19952
20334
  await task.promise;
@@ -20048,7 +20430,7 @@ Summarize or analyze this transcription as appropriate.`;
20048
20430
  toolPatternStore: toolPatternStore ?? void 0
20049
20431
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
20050
20432
  lastCompletedSummary = summary;
20051
- }, currentTaskType, resolvedContextWindowSize);
20433
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps);
20052
20434
  activeTask = task;
20053
20435
  showPrompt();
20054
20436
  await task.promise;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.26.0",
3
+ "version": "0.28.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",