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.
- package/dist/index.js +393 -11
- 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
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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