open-agents-ai 0.68.3 → 0.69.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/README.md +13 -0
- package/dist/index.js +234 -38
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -56,6 +56,10 @@ An autonomous multi-turn tool-calling agent that reads your code, makes changes,
|
|
|
56
56
|
- **Temporal agency** — schedule future tasks via OS cron, set cross-session reminders, flag attention items — startup injection surfaces due items automatically
|
|
57
57
|
- **Web crawling** — multi-page web scraping with Crawlee/Playwright for deep documentation extraction
|
|
58
58
|
- **Task templates** — specialized system prompts and tool recommendations for code, document, analysis, plan tasks
|
|
59
|
+
- **Inference capability scoring** — canirun.ai-style hardware assessment at first launch: memory/compute/speed scores, per-model compatibility matrix, recommended model selection
|
|
60
|
+
- **Auto-install everything** — first-run wizard auto-installs Ollama, curl, Python3, python3-venv with platform-aware package managers (apt, dnf, yum, pacman, apk, zypper, brew)
|
|
61
|
+
- **P2P inference network** — `/p2p` mesh networking with secret-safe variable placeholders (`{{OA_VAR_*}}`), trust tiers (LOCAL/TEE/VERIFIED/PUBLIC), WebSocket peer mesh, and inference routing with automatic secret redaction/injection
|
|
62
|
+
- **Secret vault** — `/secrets` manages API keys and credentials with AES-256-GCM encrypted persistence; secrets are automatically redacted before sending to untrusted inference peers and re-injected on response
|
|
59
63
|
- **Auto-expanding context** — detects RAM/VRAM and creates an optimized model variant on first run
|
|
60
64
|
- **Mid-task steering** — type while the agent works to add context without interrupting
|
|
61
65
|
- **Smart compaction** — 6 context compaction strategies (default, aggressive, decisions, errors, summary, structured) with research-backed design
|
|
@@ -839,6 +843,15 @@ The TUI features an animated multilingual phrase carousel, live metrics bar with
|
|
|
839
843
|
| `/tools` | List agent-created custom tools |
|
|
840
844
|
| `/skills [keyword]` | List/search available AIWG skills |
|
|
841
845
|
| `/<skill-name> [args]` | Invoke an AIWG skill directly |
|
|
846
|
+
| **P2P & Secrets** | |
|
|
847
|
+
| `/p2p start` | Start the P2P inference mesh node |
|
|
848
|
+
| `/p2p connect <url>` | Connect to a remote peer |
|
|
849
|
+
| `/p2p status` | Show mesh status, connected peers, routing stats |
|
|
850
|
+
| `/p2p stop` | Stop the P2P mesh |
|
|
851
|
+
| `/secrets set <name> <value>` | Register a secret in the vault |
|
|
852
|
+
| `/secrets list` | List registered secrets (values hidden) |
|
|
853
|
+
| `/secrets import-env` | Auto-import secrets from environment variables |
|
|
854
|
+
| `/expose` | Expose local inference via cloudflared tunnel |
|
|
842
855
|
| **Metrics & Updates** | |
|
|
843
856
|
| `/cost` | Show token cost breakdown for the current session |
|
|
844
857
|
| `/evaluate` | Score the last completed task with LLM-as-judge |
|
package/dist/index.js
CHANGED
|
@@ -24906,6 +24906,158 @@ Try manually:
|
|
|
24906
24906
|
}
|
|
24907
24907
|
}
|
|
24908
24908
|
}
|
|
24909
|
+
function computeInferenceScore(specs) {
|
|
24910
|
+
const effectiveGB = Math.max(specs.gpuVramGB, specs.availableRamGB);
|
|
24911
|
+
const memoryScore = Math.min(100, Math.round(effectiveGB <= 4 ? effectiveGB * 2.5 : effectiveGB <= 16 ? 10 + (effectiveGB - 4) * 2.5 : effectiveGB <= 48 ? 40 + (effectiveGB - 16) * 0.94 : effectiveGB <= 128 ? 70 + (effectiveGB - 48) * 0.375 : 100));
|
|
24912
|
+
let computeScore;
|
|
24913
|
+
if (specs.gpuVramGB >= 24)
|
|
24914
|
+
computeScore = 90;
|
|
24915
|
+
else if (specs.gpuVramGB >= 12)
|
|
24916
|
+
computeScore = 70;
|
|
24917
|
+
else if (specs.gpuVramGB >= 8)
|
|
24918
|
+
computeScore = 55;
|
|
24919
|
+
else if (specs.gpuVramGB >= 4)
|
|
24920
|
+
computeScore = 35;
|
|
24921
|
+
else if (specs.totalRamGB >= 32)
|
|
24922
|
+
computeScore = 25;
|
|
24923
|
+
else
|
|
24924
|
+
computeScore = Math.max(5, Math.round(specs.totalRamGB * 1.5));
|
|
24925
|
+
let speedScore;
|
|
24926
|
+
if (specs.gpuVramGB >= 48)
|
|
24927
|
+
speedScore = 95;
|
|
24928
|
+
else if (specs.gpuVramGB >= 24)
|
|
24929
|
+
speedScore = 80;
|
|
24930
|
+
else if (specs.gpuVramGB >= 12)
|
|
24931
|
+
speedScore = 65;
|
|
24932
|
+
else if (specs.gpuVramGB >= 8)
|
|
24933
|
+
speedScore = 50;
|
|
24934
|
+
else if (specs.gpuVramGB > 0)
|
|
24935
|
+
speedScore = 35;
|
|
24936
|
+
else
|
|
24937
|
+
speedScore = Math.min(30, Math.round(specs.totalRamGB * 0.6));
|
|
24938
|
+
const overall = Math.round(memoryScore * 0.45 + computeScore * 0.35 + speedScore * 0.2);
|
|
24939
|
+
const modelCompat = QWEN_VARIANTS.filter((v) => !v.cloud).map((v) => {
|
|
24940
|
+
const budget = effectiveGB * 0.8;
|
|
24941
|
+
const fits = v.sizeGB <= budget;
|
|
24942
|
+
let note;
|
|
24943
|
+
if (fits) {
|
|
24944
|
+
const headroom = budget - v.sizeGB;
|
|
24945
|
+
note = headroom > v.sizeGB * 0.5 ? "comfortable" : headroom > v.sizeGB * 0.15 ? "good fit" : "tight fit";
|
|
24946
|
+
if (specs.gpuVramGB > 0 && v.sizeGB <= specs.gpuVramGB * 0.8) {
|
|
24947
|
+
note += " (GPU accelerated)";
|
|
24948
|
+
}
|
|
24949
|
+
} else {
|
|
24950
|
+
const overshoot = v.sizeGB - budget;
|
|
24951
|
+
note = `need ${overshoot.toFixed(0)} GB more`;
|
|
24952
|
+
}
|
|
24953
|
+
return { tag: v.tag, fits, note };
|
|
24954
|
+
});
|
|
24955
|
+
let summary;
|
|
24956
|
+
if (overall >= 80)
|
|
24957
|
+
summary = "Excellent \u2014 can run large models with full context windows";
|
|
24958
|
+
else if (overall >= 60)
|
|
24959
|
+
summary = "Strong \u2014 medium to large models with good context";
|
|
24960
|
+
else if (overall >= 40)
|
|
24961
|
+
summary = "Moderate \u2014 small to medium models, reasonable performance";
|
|
24962
|
+
else if (overall >= 20)
|
|
24963
|
+
summary = "Basic \u2014 small models only, limited context";
|
|
24964
|
+
else
|
|
24965
|
+
summary = "Minimal \u2014 very small models or cloud inference recommended";
|
|
24966
|
+
return { overall, memory: memoryScore, compute: computeScore, speed: speedScore, summary, modelCompat };
|
|
24967
|
+
}
|
|
24968
|
+
function renderScoreBar(score, width = 20) {
|
|
24969
|
+
const filled = Math.round(score / 100 * width);
|
|
24970
|
+
const empty = width - filled;
|
|
24971
|
+
const color = score >= 70 ? c2.green : score >= 40 ? c2.yellow : c2.red;
|
|
24972
|
+
return color("\u2588".repeat(filled)) + c2.dim("\u2591".repeat(empty));
|
|
24973
|
+
}
|
|
24974
|
+
function ensurePython3() {
|
|
24975
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Installing Python3...
|
|
24976
|
+
`);
|
|
24977
|
+
const plat = platform();
|
|
24978
|
+
if (plat === "win32") {
|
|
24979
|
+
process.stdout.write(` ${c2.dim("Download Python from https://python.org/downloads/")}
|
|
24980
|
+
|
|
24981
|
+
`);
|
|
24982
|
+
return;
|
|
24983
|
+
}
|
|
24984
|
+
const strategies = [
|
|
24985
|
+
{ check: "apt-get", install: "sudo apt-get update -qq && sudo apt-get install -y -qq python3 python3-venv python3-pip", label: "apt" },
|
|
24986
|
+
{ check: "dnf", install: "sudo dnf install -y -q python3 python3-pip", label: "dnf" },
|
|
24987
|
+
{ check: "yum", install: "sudo yum install -y -q python3 python3-pip", label: "yum" },
|
|
24988
|
+
{ check: "pacman", install: "sudo pacman -S --noconfirm python python-pip", label: "pacman" },
|
|
24989
|
+
{ check: "apk", install: "sudo apk add --quiet python3 py3-pip", label: "apk" },
|
|
24990
|
+
{ check: "zypper", install: "sudo zypper install -y python3 python3-pip", label: "zypper" }
|
|
24991
|
+
];
|
|
24992
|
+
if (plat === "darwin") {
|
|
24993
|
+
if (hasCmd("brew")) {
|
|
24994
|
+
try {
|
|
24995
|
+
execSync20("brew install python3", { stdio: "inherit", timeout: 3e5 });
|
|
24996
|
+
if (hasCmd("python3")) {
|
|
24997
|
+
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via Homebrew.
|
|
24998
|
+
`);
|
|
24999
|
+
return;
|
|
25000
|
+
}
|
|
25001
|
+
} catch {
|
|
25002
|
+
}
|
|
25003
|
+
}
|
|
25004
|
+
}
|
|
25005
|
+
for (const s of strategies) {
|
|
25006
|
+
if (hasCmd(s.check)) {
|
|
25007
|
+
try {
|
|
25008
|
+
execSync20(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
25009
|
+
if (hasCmd("python3") || hasCmd("python")) {
|
|
25010
|
+
process.stdout.write(` ${c2.green("\u2714")} Python3 installed via ${s.label}.
|
|
25011
|
+
`);
|
|
25012
|
+
return;
|
|
25013
|
+
}
|
|
25014
|
+
} catch {
|
|
25015
|
+
}
|
|
25016
|
+
}
|
|
25017
|
+
}
|
|
25018
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Could not install Python3 automatically. Install manually.
|
|
25019
|
+
|
|
25020
|
+
`);
|
|
25021
|
+
}
|
|
25022
|
+
function checkPythonVenv() {
|
|
25023
|
+
try {
|
|
25024
|
+
execSync20("python3 -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
25025
|
+
return true;
|
|
25026
|
+
} catch {
|
|
25027
|
+
try {
|
|
25028
|
+
execSync20("python -m venv --help", { stdio: "pipe", timeout: 5e3 });
|
|
25029
|
+
return true;
|
|
25030
|
+
} catch {
|
|
25031
|
+
return false;
|
|
25032
|
+
}
|
|
25033
|
+
}
|
|
25034
|
+
}
|
|
25035
|
+
function ensurePythonVenv() {
|
|
25036
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Installing python3-venv...
|
|
25037
|
+
`);
|
|
25038
|
+
const strategies = [
|
|
25039
|
+
{ check: "apt-get", install: "sudo apt-get update -qq && sudo apt-get install -y -qq python3-venv", label: "apt" },
|
|
25040
|
+
{ check: "dnf", install: "sudo dnf install -y -q python3-libs", label: "dnf" },
|
|
25041
|
+
{ check: "pacman", install: "sudo pacman -S --noconfirm python", label: "pacman" },
|
|
25042
|
+
{ check: "apk", install: "sudo apk add --quiet python3", label: "apk" }
|
|
25043
|
+
];
|
|
25044
|
+
for (const s of strategies) {
|
|
25045
|
+
if (hasCmd(s.check)) {
|
|
25046
|
+
try {
|
|
25047
|
+
execSync20(s.install, { stdio: "inherit", timeout: 12e4 });
|
|
25048
|
+
if (checkPythonVenv()) {
|
|
25049
|
+
process.stdout.write(` ${c2.green("\u2714")} python3-venv installed via ${s.label}.
|
|
25050
|
+
`);
|
|
25051
|
+
return;
|
|
25052
|
+
}
|
|
25053
|
+
} catch {
|
|
25054
|
+
}
|
|
25055
|
+
}
|
|
25056
|
+
}
|
|
25057
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Could not install python3-venv. Install manually: sudo apt install python3-venv
|
|
25058
|
+
|
|
25059
|
+
`);
|
|
25060
|
+
}
|
|
24909
25061
|
async function runSetupWizard(config) {
|
|
24910
25062
|
const rl = readline.createInterface({
|
|
24911
25063
|
input: process.stdin,
|
|
@@ -25049,9 +25201,49 @@ async function doSetup(config, rl) {
|
|
|
25049
25201
|
`);
|
|
25050
25202
|
} else {
|
|
25051
25203
|
process.stdout.write(` ${c2.dim(" GPU:")} No NVIDIA GPU detected (CPU inference)
|
|
25204
|
+
`);
|
|
25205
|
+
}
|
|
25206
|
+
const score = computeInferenceScore(specs);
|
|
25207
|
+
process.stdout.write(`
|
|
25208
|
+
${c2.bold("Inference Capability Score")}
|
|
25209
|
+
`);
|
|
25210
|
+
process.stdout.write(` ${renderScoreBar(score.overall)} ${c2.bold(String(score.overall))}${c2.dim("/100")}
|
|
25211
|
+
`);
|
|
25212
|
+
process.stdout.write(` ${c2.dim("Memory:")} ${renderScoreBar(score.memory, 15)} ${score.memory} `);
|
|
25213
|
+
process.stdout.write(`${c2.dim("Compute:")} ${renderScoreBar(score.compute, 15)} ${score.compute} `);
|
|
25214
|
+
process.stdout.write(`${c2.dim("Speed:")} ${renderScoreBar(score.speed, 15)} ${score.speed}
|
|
25215
|
+
`);
|
|
25216
|
+
process.stdout.write(` ${c2.dim(score.summary)}
|
|
25217
|
+
`);
|
|
25218
|
+
process.stdout.write(`
|
|
25219
|
+
${c2.dim("Model compatibility:")}
|
|
25220
|
+
`);
|
|
25221
|
+
for (const compat of score.modelCompat) {
|
|
25222
|
+
const icon = compat.fits ? c2.green("\u2714") : c2.red("\u2716");
|
|
25223
|
+
const tag = compat.fits ? compat.tag : c2.dim(compat.tag);
|
|
25224
|
+
process.stdout.write(` ${icon} ${tag.padEnd(compat.fits ? 20 : 20)} ${c2.dim(compat.note)}
|
|
25052
25225
|
`);
|
|
25053
25226
|
}
|
|
25054
25227
|
process.stdout.write("\n");
|
|
25228
|
+
const hasPython = hasCmd("python3") || hasCmd("python");
|
|
25229
|
+
if (!hasPython) {
|
|
25230
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Python3 not found (needed for vision, OCR, browser automation).
|
|
25231
|
+
`);
|
|
25232
|
+
const installPy = await ask(rl, ` ${c2.bold("Install Python3?")} (Y/n) `);
|
|
25233
|
+
if (installPy.toLowerCase() !== "n") {
|
|
25234
|
+
ensurePython3();
|
|
25235
|
+
}
|
|
25236
|
+
} else {
|
|
25237
|
+
const hasVenv = checkPythonVenv();
|
|
25238
|
+
if (!hasVenv) {
|
|
25239
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Python3 venv module not found (needed for Moondream, OCR, browser automation).
|
|
25240
|
+
`);
|
|
25241
|
+
const installVenv = await ask(rl, ` ${c2.bold("Install python3-venv?")} (Y/n) `);
|
|
25242
|
+
if (installVenv.toLowerCase() !== "n") {
|
|
25243
|
+
ensurePythonVenv();
|
|
25244
|
+
}
|
|
25245
|
+
}
|
|
25246
|
+
}
|
|
25055
25247
|
let models = [];
|
|
25056
25248
|
let usingCustomEndpoint = false;
|
|
25057
25249
|
try {
|
|
@@ -25060,36 +25252,18 @@ async function doSetup(config, rl) {
|
|
|
25060
25252
|
process.stdout.write(` ${c2.yellow("\u26A0")} Cannot reach Ollama at ${c2.bold(config.backendUrl)}
|
|
25061
25253
|
|
|
25062
25254
|
`);
|
|
25063
|
-
const
|
|
25064
|
-
if (
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
const child = spawn12("ollama", ["serve"], { stdio: "ignore", detached: true });
|
|
25069
|
-
child.unref();
|
|
25070
|
-
await new Promise((resolve28) => setTimeout(resolve28, 3e3));
|
|
25071
|
-
try {
|
|
25072
|
-
models = await fetchOllamaModels(config.backendUrl);
|
|
25073
|
-
process.stdout.write(` ${c2.green("\u2714")} Ollama is running.
|
|
25074
|
-
|
|
25075
|
-
`);
|
|
25076
|
-
} catch {
|
|
25077
|
-
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama started but not responding yet. It may need a moment.
|
|
25078
|
-
|
|
25079
|
-
`);
|
|
25080
|
-
}
|
|
25081
|
-
} catch {
|
|
25082
|
-
process.stdout.write(` ${c2.yellow("\u26A0")} Could not start Ollama. Try running ${c2.bold("ollama serve")} manually.
|
|
25083
|
-
|
|
25084
|
-
`);
|
|
25255
|
+
const useCustom = await ask(rl, ` ${c2.bold("Use a custom inference endpoint instead?")} (Y/n) `);
|
|
25256
|
+
if (useCustom.toLowerCase() === "y" || useCustom.toLowerCase() === "yes") {
|
|
25257
|
+
const endpointResult = await promptForCustomEndpoint(config, rl);
|
|
25258
|
+
if (endpointResult) {
|
|
25259
|
+
return endpointResult;
|
|
25085
25260
|
}
|
|
25261
|
+
usingCustomEndpoint = true;
|
|
25086
25262
|
} else {
|
|
25087
|
-
|
|
25088
|
-
|
|
25089
|
-
const installed = await autoInstallOllama(rl);
|
|
25090
|
-
if (installed) {
|
|
25263
|
+
const ollamaInstalled = hasCmd("ollama");
|
|
25264
|
+
if (ollamaInstalled) {
|
|
25091
25265
|
process.stdout.write(`
|
|
25092
|
-
${c2.cyan("\u25CF")} Starting
|
|
25266
|
+
${c2.cyan("\u25CF")} Ollama is installed but not running. Starting automatically...
|
|
25093
25267
|
`);
|
|
25094
25268
|
try {
|
|
25095
25269
|
const child = spawn12("ollama", ["serve"], { stdio: "ignore", detached: true });
|
|
@@ -25101,27 +25275,49 @@ async function doSetup(config, rl) {
|
|
|
25101
25275
|
|
|
25102
25276
|
`);
|
|
25103
25277
|
} catch {
|
|
25104
|
-
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama
|
|
25278
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama started but not responding yet. It may need a moment.
|
|
25105
25279
|
|
|
25106
25280
|
`);
|
|
25107
25281
|
}
|
|
25108
25282
|
} catch {
|
|
25109
|
-
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama
|
|
25283
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Could not start Ollama. Try running ${c2.bold("ollama serve")} manually.
|
|
25110
25284
|
|
|
25111
25285
|
`);
|
|
25112
25286
|
}
|
|
25113
|
-
}
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
if (
|
|
25117
|
-
const
|
|
25118
|
-
if (
|
|
25119
|
-
|
|
25287
|
+
} else {
|
|
25288
|
+
const installOllama = await ask(rl, `
|
|
25289
|
+
${c2.bold("Install Ollama for local inference?")} (Y/n) `);
|
|
25290
|
+
if (installOllama.toLowerCase() !== "n") {
|
|
25291
|
+
const installed = await autoInstallOllama(rl);
|
|
25292
|
+
if (installed) {
|
|
25293
|
+
process.stdout.write(`
|
|
25294
|
+
${c2.cyan("\u25CF")} Starting ollama serve...
|
|
25295
|
+
`);
|
|
25296
|
+
try {
|
|
25297
|
+
const child = spawn12("ollama", ["serve"], { stdio: "ignore", detached: true });
|
|
25298
|
+
child.unref();
|
|
25299
|
+
await new Promise((resolve28) => setTimeout(resolve28, 3e3));
|
|
25300
|
+
try {
|
|
25301
|
+
models = await fetchOllamaModels(config.backendUrl);
|
|
25302
|
+
process.stdout.write(` ${c2.green("\u2714")} Ollama is running.
|
|
25303
|
+
|
|
25304
|
+
`);
|
|
25305
|
+
} catch {
|
|
25306
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama installed but not responding yet. Try ${c2.bold("ollama serve")} manually.
|
|
25307
|
+
|
|
25308
|
+
`);
|
|
25309
|
+
}
|
|
25310
|
+
} catch {
|
|
25311
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Ollama installed. Start it with: ${c2.bold("ollama serve")}
|
|
25312
|
+
|
|
25313
|
+
`);
|
|
25314
|
+
}
|
|
25120
25315
|
}
|
|
25121
|
-
usingCustomEndpoint = true;
|
|
25122
25316
|
} else {
|
|
25123
25317
|
process.stdout.write(`
|
|
25124
|
-
${c2.dim("You
|
|
25318
|
+
${c2.dim("You'll need to configure an inference endpoint.")}
|
|
25319
|
+
`);
|
|
25320
|
+
process.stdout.write(` ${c2.dim("Use:")} ${c2.bold("/endpoint <url> --auth <key>")} ${c2.dim("once inside the TUI.")}
|
|
25125
25321
|
|
|
25126
25322
|
`);
|
|
25127
25323
|
return config.model;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.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",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"node": ">=18.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
+
"aiwg": "^2026.3.2",
|
|
62
63
|
"better-sqlite3": "^11.7.0",
|
|
63
64
|
"glob": "^11.0.0",
|
|
64
65
|
"ignore": "^6.0.2",
|