open-agents-ai 0.26.0 → 0.27.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 +194 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6635,9 +6635,18 @@ var init_vision = __esm({
|
|
|
6635
6635
|
},
|
|
6636
6636
|
required: ["image"]
|
|
6637
6637
|
};
|
|
6638
|
+
/** Active Ollama model name — used as vision fallback if it has vision capability */
|
|
6639
|
+
_activeModel = "";
|
|
6640
|
+
/** Whether the active model has vision capability */
|
|
6641
|
+
_activeModelHasVision = false;
|
|
6638
6642
|
constructor(workingDir) {
|
|
6639
6643
|
this.workingDir = workingDir;
|
|
6640
6644
|
}
|
|
6645
|
+
/** Set the active Ollama model and whether it supports vision */
|
|
6646
|
+
setActiveModel(model, hasVision) {
|
|
6647
|
+
this._activeModel = model;
|
|
6648
|
+
this._activeModelHasVision = hasVision;
|
|
6649
|
+
}
|
|
6641
6650
|
async execute(args) {
|
|
6642
6651
|
const start = performance.now();
|
|
6643
6652
|
const rawPath = args["image"];
|
|
@@ -6738,7 +6747,8 @@ Coordinates are normalized (0-1). Multiply by image width/height for pixel value
|
|
|
6738
6747
|
}
|
|
6739
6748
|
async tryOllamaVision(buffer, filename, action, prompt, length, start) {
|
|
6740
6749
|
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
6741
|
-
const
|
|
6750
|
+
const envModel = process.env["OLLAMA_VISION_MODEL"];
|
|
6751
|
+
const model = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
|
|
6742
6752
|
const imageBase64 = buffer.toString("base64");
|
|
6743
6753
|
let ollamaPrompt;
|
|
6744
6754
|
switch (action) {
|
|
@@ -6965,7 +6975,16 @@ var init_desktop_click = __esm({
|
|
|
6965
6975
|
DesktopClickTool = class {
|
|
6966
6976
|
workingDir;
|
|
6967
6977
|
name = "desktop_click";
|
|
6968
|
-
description = "Click on a UI element identified by natural language description. Takes a screenshot, uses
|
|
6978
|
+
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' })";
|
|
6979
|
+
/** Active Ollama model name — used as vision fallback if it has vision capability */
|
|
6980
|
+
_activeModel = "";
|
|
6981
|
+
/** Whether the active model has vision capability */
|
|
6982
|
+
_activeModelHasVision = false;
|
|
6983
|
+
/** Set the active Ollama model and whether it supports vision */
|
|
6984
|
+
setActiveModel(model, hasVision) {
|
|
6985
|
+
this._activeModel = model;
|
|
6986
|
+
this._activeModelHasVision = hasVision;
|
|
6987
|
+
}
|
|
6969
6988
|
parameters = {
|
|
6970
6989
|
type: "object",
|
|
6971
6990
|
properties: {
|
|
@@ -7050,7 +7069,8 @@ var init_desktop_click = __esm({
|
|
|
7050
7069
|
if (!visionWorked) {
|
|
7051
7070
|
try {
|
|
7052
7071
|
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
7053
|
-
const
|
|
7072
|
+
const envModel = process.env["OLLAMA_VISION_MODEL"];
|
|
7073
|
+
const ollamaModel = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
|
|
7054
7074
|
const imageBase64 = readFileSync11(screenshotPath).toString("base64");
|
|
7055
7075
|
const res = await fetch(`${ollamaHost}/api/generate`, {
|
|
7056
7076
|
method: "POST",
|
|
@@ -7148,7 +7168,16 @@ Screenshot: ${screenshotPath}`,
|
|
|
7148
7168
|
DesktopDescribeTool = class {
|
|
7149
7169
|
workingDir;
|
|
7150
7170
|
name = "desktop_describe";
|
|
7151
|
-
description = "Take a screenshot and describe what's on the desktop using
|
|
7171
|
+
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.";
|
|
7172
|
+
/** Active Ollama model name — used as vision fallback if it has vision capability */
|
|
7173
|
+
_activeModel = "";
|
|
7174
|
+
/** Whether the active model has vision capability */
|
|
7175
|
+
_activeModelHasVision = false;
|
|
7176
|
+
/** Set the active Ollama model and whether it supports vision */
|
|
7177
|
+
setActiveModel(model, hasVision) {
|
|
7178
|
+
this._activeModel = model;
|
|
7179
|
+
this._activeModelHasVision = hasVision;
|
|
7180
|
+
}
|
|
7152
7181
|
parameters = {
|
|
7153
7182
|
type: "object",
|
|
7154
7183
|
properties: {
|
|
@@ -7214,7 +7243,8 @@ ${caption}`);
|
|
|
7214
7243
|
if (!visionWorked) {
|
|
7215
7244
|
try {
|
|
7216
7245
|
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
7217
|
-
const
|
|
7246
|
+
const envModel = process.env["OLLAMA_VISION_MODEL"];
|
|
7247
|
+
const ollamaModel = envModel || (this._activeModelHasVision && this._activeModel ? this._activeModel : "moondream");
|
|
7218
7248
|
const imageBase64 = imageBuffer.toString("base64");
|
|
7219
7249
|
const ollamaPrompt = question || "Describe what you see on this desktop screenshot in detail. Include visible applications, windows, text, and UI elements.";
|
|
7220
7250
|
const res = await fetch(`${ollamaHost}/api/generate`, {
|
|
@@ -12783,6 +12813,52 @@ async function queryContextSize(baseUrl, modelName, apiKey) {
|
|
|
12783
12813
|
return ollamaSize;
|
|
12784
12814
|
return queryOpenAIContextSize(baseUrl, modelName, apiKey);
|
|
12785
12815
|
}
|
|
12816
|
+
async function queryModelCapabilities(baseUrl, modelName) {
|
|
12817
|
+
const caps = { vision: false, toolUse: false, thinking: false };
|
|
12818
|
+
try {
|
|
12819
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
12820
|
+
const res = await fetch(`${normalized}/api/show`, {
|
|
12821
|
+
method: "POST",
|
|
12822
|
+
headers: { "Content-Type": "application/json" },
|
|
12823
|
+
body: JSON.stringify({ name: modelName }),
|
|
12824
|
+
signal: AbortSignal.timeout(1e4)
|
|
12825
|
+
});
|
|
12826
|
+
if (!res.ok)
|
|
12827
|
+
return caps;
|
|
12828
|
+
const data = await res.json();
|
|
12829
|
+
if (Array.isArray(data.capabilities)) {
|
|
12830
|
+
if (data.capabilities.includes("vision"))
|
|
12831
|
+
caps.vision = true;
|
|
12832
|
+
if (data.capabilities.includes("tools"))
|
|
12833
|
+
caps.toolUse = true;
|
|
12834
|
+
if (data.capabilities.includes("thinking"))
|
|
12835
|
+
caps.thinking = true;
|
|
12836
|
+
}
|
|
12837
|
+
if (data.model_info) {
|
|
12838
|
+
for (const key of Object.keys(data.model_info)) {
|
|
12839
|
+
const k = key.toLowerCase();
|
|
12840
|
+
if (k.includes("vision.block_count") || k.includes("clip.") || k.includes("image_token_id") || k.includes("projector") || k.includes("vision.embedding_length")) {
|
|
12841
|
+
const val = data.model_info[key];
|
|
12842
|
+
if (val !== null && val !== void 0 && val !== 0 && val !== "") {
|
|
12843
|
+
caps.vision = true;
|
|
12844
|
+
}
|
|
12845
|
+
}
|
|
12846
|
+
}
|
|
12847
|
+
}
|
|
12848
|
+
const nameLower = modelName.toLowerCase();
|
|
12849
|
+
if (/qwen3|qwen2\.5|llama3\.[13]|mistral|mixtral|command-r|gemma3|devstral|deepseek/.test(nameLower)) {
|
|
12850
|
+
caps.toolUse = true;
|
|
12851
|
+
}
|
|
12852
|
+
if (data.template) {
|
|
12853
|
+
if (data.template.includes("<think>") || data.template.includes("thinking")) {
|
|
12854
|
+
caps.thinking = true;
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
return caps;
|
|
12858
|
+
} catch {
|
|
12859
|
+
return caps;
|
|
12860
|
+
}
|
|
12861
|
+
}
|
|
12786
12862
|
function formatBytes(bytes) {
|
|
12787
12863
|
if (bytes < 1024)
|
|
12788
12864
|
return `${bytes} B`;
|
|
@@ -14685,6 +14761,63 @@ function isFirstRun() {
|
|
|
14685
14761
|
return true;
|
|
14686
14762
|
}
|
|
14687
14763
|
}
|
|
14764
|
+
async function ensureVisionDeps(onInfo) {
|
|
14765
|
+
const log = onInfo ?? (() => {
|
|
14766
|
+
});
|
|
14767
|
+
try {
|
|
14768
|
+
execSync13("which tesseract", { stdio: "pipe", timeout: 3e3 });
|
|
14769
|
+
} 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) {
|
|
14780
|
+
try {
|
|
14781
|
+
execSync13(cmd, { stdio: "pipe", timeout: 6e4 });
|
|
14782
|
+
installed = true;
|
|
14783
|
+
break;
|
|
14784
|
+
} catch {
|
|
14785
|
+
}
|
|
14786
|
+
}
|
|
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 {
|
|
14792
|
+
}
|
|
14793
|
+
}
|
|
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) {
|
|
14806
|
+
try {
|
|
14807
|
+
execSync13(cmd, { stdio: "pipe", timeout: 12e4 });
|
|
14808
|
+
installed = true;
|
|
14809
|
+
break;
|
|
14810
|
+
} catch {
|
|
14811
|
+
}
|
|
14812
|
+
}
|
|
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
|
+
}
|
|
14819
|
+
}
|
|
14820
|
+
}
|
|
14688
14821
|
function expandedModelName(baseModel) {
|
|
14689
14822
|
return `open-agents-${baseModel.replace(":", "-").replace(/\./g, "")}`;
|
|
14690
14823
|
}
|
|
@@ -15484,6 +15617,10 @@ async function switchModel(query, ctx, local = false) {
|
|
|
15484
15617
|
ctx.setContextWindowSize(ctxSize);
|
|
15485
15618
|
}
|
|
15486
15619
|
}
|
|
15620
|
+
if (ctx.setCapabilities) {
|
|
15621
|
+
const caps = await queryModelCapabilities(ctx.config.backendUrl, finalModel);
|
|
15622
|
+
ctx.setCapabilities(caps);
|
|
15623
|
+
}
|
|
15487
15624
|
} catch (err) {
|
|
15488
15625
|
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
15489
15626
|
}
|
|
@@ -18666,6 +18803,18 @@ var init_status_bar = __esm({
|
|
|
18666
18803
|
setContextWindowSize(size) {
|
|
18667
18804
|
this.metrics.contextWindowSize = size;
|
|
18668
18805
|
}
|
|
18806
|
+
/** Model capabilities — shown as emoji indicators on the status bar */
|
|
18807
|
+
_caps = {
|
|
18808
|
+
vision: false,
|
|
18809
|
+
toolUse: false,
|
|
18810
|
+
thinking: false
|
|
18811
|
+
};
|
|
18812
|
+
/** Update model capability indicators */
|
|
18813
|
+
setCapabilities(caps) {
|
|
18814
|
+
this._caps = caps;
|
|
18815
|
+
if (this.active)
|
|
18816
|
+
this.renderFooterPreserveCursor();
|
|
18817
|
+
}
|
|
18669
18818
|
/** Update token metrics from a token_usage event */
|
|
18670
18819
|
updateMetrics(update) {
|
|
18671
18820
|
if (update.promptTokens !== void 0)
|
|
@@ -18822,7 +18971,15 @@ var init_status_bar = __esm({
|
|
|
18822
18971
|
const countdown = this._countdown > 0 ? c2.dim(` ${this._countdown}s`) : "";
|
|
18823
18972
|
recordingLabel = pipe + dot + pastel2(210, " REC") + countdown;
|
|
18824
18973
|
}
|
|
18825
|
-
|
|
18974
|
+
const capParts = [];
|
|
18975
|
+
if (this._caps.vision)
|
|
18976
|
+
capParts.push("\u{1F441}");
|
|
18977
|
+
if (this._caps.toolUse)
|
|
18978
|
+
capParts.push("\u{1F527}");
|
|
18979
|
+
if (this._caps.thinking)
|
|
18980
|
+
capParts.push("\u{1F9E0}");
|
|
18981
|
+
const capsLabel = capParts.length > 0 ? pipe + pastel2(183, capParts.join(" ")) : "";
|
|
18982
|
+
return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${costLabel}${capsLabel}${recordingLabel}`;
|
|
18826
18983
|
}
|
|
18827
18984
|
// -------------------------------------------------------------------------
|
|
18828
18985
|
// Private
|
|
@@ -19157,7 +19314,7 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
|
|
|
19157
19314
|
}
|
|
19158
19315
|
};
|
|
19159
19316
|
}
|
|
19160
|
-
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize) {
|
|
19317
|
+
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps) {
|
|
19161
19318
|
const modelTier = getModelTier(config.model);
|
|
19162
19319
|
const projectCtx = buildProjectContext(repoRoot, taskStores?.contextStores);
|
|
19163
19320
|
let dynamicContext = formatContextForPrompt(projectCtx, modelTier);
|
|
@@ -19190,6 +19347,12 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
|
|
|
19190
19347
|
}
|
|
19191
19348
|
}
|
|
19192
19349
|
}
|
|
19350
|
+
const hasVision = modelCaps?.vision ?? false;
|
|
19351
|
+
for (const tool of tools) {
|
|
19352
|
+
if ("setActiveModel" in tool && typeof tool.setActiveModel === "function") {
|
|
19353
|
+
tool.setActiveModel(config.model, hasVision);
|
|
19354
|
+
}
|
|
19355
|
+
}
|
|
19193
19356
|
runner.registerTools(tools);
|
|
19194
19357
|
const filesTouched = /* @__PURE__ */ new Set();
|
|
19195
19358
|
const toolSequence = [];
|
|
@@ -19489,11 +19652,29 @@ async function startInteractive(config, repoPath) {
|
|
|
19489
19652
|
}
|
|
19490
19653
|
}).catch(() => {
|
|
19491
19654
|
});
|
|
19655
|
+
let resolvedCaps = {
|
|
19656
|
+
vision: false,
|
|
19657
|
+
toolUse: false,
|
|
19658
|
+
thinking: false
|
|
19659
|
+
};
|
|
19660
|
+
queryModelCapabilities(config.backendUrl, config.model).then((caps) => {
|
|
19661
|
+
resolvedCaps = caps;
|
|
19662
|
+
statusBar.setCapabilities(caps);
|
|
19663
|
+
}).catch(() => {
|
|
19664
|
+
});
|
|
19492
19665
|
const provider = detectProvider(config.backendUrl);
|
|
19493
19666
|
const costTracker = new CostTracker(provider.id);
|
|
19494
19667
|
const sessionMetrics = new SessionMetrics();
|
|
19495
19668
|
const workEvaluator = new WorkEvaluator();
|
|
19496
19669
|
ensureTranscribeCliBackground();
|
|
19670
|
+
ensureVisionDeps((msg) => {
|
|
19671
|
+
if (statusBar?.isActive) {
|
|
19672
|
+
statusBar.beginContentWrite();
|
|
19673
|
+
renderInfo(msg);
|
|
19674
|
+
statusBar.endContentWrite();
|
|
19675
|
+
}
|
|
19676
|
+
}).catch(() => {
|
|
19677
|
+
});
|
|
19497
19678
|
const voiceEngine = new VoiceEngine();
|
|
19498
19679
|
const streamRenderer = new StreamRenderer();
|
|
19499
19680
|
if (savedSettings.voice) {
|
|
@@ -19791,6 +19972,10 @@ async function startInteractive(config, repoPath) {
|
|
|
19791
19972
|
resolvedContextWindowSize = size;
|
|
19792
19973
|
statusBar.setContextWindowSize(size);
|
|
19793
19974
|
},
|
|
19975
|
+
setCapabilities: (caps) => {
|
|
19976
|
+
resolvedCaps = caps;
|
|
19977
|
+
statusBar.setCapabilities(caps);
|
|
19978
|
+
},
|
|
19794
19979
|
hasActiveTask: () => activeTask !== null,
|
|
19795
19980
|
abortTask() {
|
|
19796
19981
|
if (!activeTask)
|
|
@@ -19946,7 +20131,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
19946
20131
|
toolPatternStore: toolPatternStore ?? void 0
|
|
19947
20132
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
19948
20133
|
lastCompletedSummary = summary;
|
|
19949
|
-
}, currentTaskType, resolvedContextWindowSize);
|
|
20134
|
+
}, currentTaskType, resolvedContextWindowSize, resolvedCaps);
|
|
19950
20135
|
activeTask = task;
|
|
19951
20136
|
showPrompt();
|
|
19952
20137
|
await task.promise;
|
|
@@ -20048,7 +20233,7 @@ Summarize or analyze this transcription as appropriate.`;
|
|
|
20048
20233
|
toolPatternStore: toolPatternStore ?? void 0
|
|
20049
20234
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
20050
20235
|
lastCompletedSummary = summary;
|
|
20051
|
-
}, currentTaskType, resolvedContextWindowSize);
|
|
20236
|
+
}, currentTaskType, resolvedContextWindowSize, resolvedCaps);
|
|
20052
20237
|
activeTask = task;
|
|
20053
20238
|
showPrompt();
|
|
20054
20239
|
await task.promise;
|
package/package.json
CHANGED