0agent 1.0.50 → 1.0.54
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/bin/0agent.js +3 -3
- package/bin/chat.js +31 -10
- package/bin/postinstall.js +76 -0
- package/dist/daemon.mjs +126 -35
- package/package.json +3 -3
package/bin/0agent.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* 0agent improve # self-improvement analysis
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
22
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, openSync } from 'node:fs';
|
|
23
23
|
import { resolve, dirname } from 'node:path';
|
|
24
24
|
import { homedir, platform } from 'node:os';
|
|
25
25
|
import { spawn, execSync } from 'node:child_process';
|
|
@@ -431,10 +431,10 @@ async function startDaemon() {
|
|
|
431
431
|
|
|
432
432
|
mkdirSync(resolve(AGENT_DIR, 'logs'), { recursive: true });
|
|
433
433
|
|
|
434
|
-
const
|
|
434
|
+
const logFd = openSync(LOG_PATH, 'w');
|
|
435
435
|
const child = spawn(process.execPath, [startScript], {
|
|
436
436
|
detached: true,
|
|
437
|
-
stdio: ['ignore',
|
|
437
|
+
stdio: ['ignore', logFd, logFd],
|
|
438
438
|
env: { ...process.env, ZEROAGENT_CONFIG: CONFIG_PATH },
|
|
439
439
|
});
|
|
440
440
|
child.unref();
|
package/bin/chat.js
CHANGED
|
@@ -1206,24 +1206,36 @@ connectWS();
|
|
|
1206
1206
|
|
|
1207
1207
|
// ── Startup: ensure fresh daemon + verify LLM ────────────────────────────────
|
|
1208
1208
|
async function _spawnDaemon() {
|
|
1209
|
-
const pkgRoot
|
|
1209
|
+
const pkgRoot = resolve(new URL(import.meta.url).pathname, '..', '..');
|
|
1210
1210
|
const bundled = resolve(pkgRoot, 'dist', 'daemon.mjs');
|
|
1211
|
-
|
|
1211
|
+
const devPath = resolve(pkgRoot, 'packages', 'daemon', 'dist', 'start.js');
|
|
1212
|
+
const daemonScript = existsSync(bundled) ? bundled : existsSync(devPath) ? devPath : null;
|
|
1213
|
+
|
|
1214
|
+
if (!daemonScript) return 'no-bundle';
|
|
1215
|
+
if (!existsSync(CONFIG_PATH)) return 'no-config';
|
|
1216
|
+
|
|
1212
1217
|
const { spawn } = await import('node:child_process');
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1218
|
+
const { openSync: fsOpen, mkdirSync: fsMkdir } = await import('node:fs');
|
|
1219
|
+
const logDir = resolve(AGENT_DIR, 'logs');
|
|
1220
|
+
fsMkdir(logDir, { recursive: true });
|
|
1221
|
+
const logFd = fsOpen(resolve(logDir, 'daemon.log'), 'w');
|
|
1222
|
+
|
|
1223
|
+
const child = spawn(process.execPath, [daemonScript], {
|
|
1224
|
+
detached: true,
|
|
1225
|
+
stdio: ['ignore', logFd, logFd],
|
|
1215
1226
|
env: { ...process.env, ZEROAGENT_CONFIG: CONFIG_PATH },
|
|
1216
1227
|
});
|
|
1217
1228
|
child.unref();
|
|
1229
|
+
|
|
1218
1230
|
// Wait up to 10s for daemon to be ready
|
|
1219
1231
|
for (let i = 0; i < 20; i++) {
|
|
1220
1232
|
await new Promise(r => setTimeout(r, 500));
|
|
1221
1233
|
try {
|
|
1222
1234
|
await fetch(`${BASE_URL}/api/health`, { signal: AbortSignal.timeout(500) });
|
|
1223
|
-
return
|
|
1235
|
+
return 'ok';
|
|
1224
1236
|
} catch {}
|
|
1225
1237
|
}
|
|
1226
|
-
return
|
|
1238
|
+
return 'timeout';
|
|
1227
1239
|
}
|
|
1228
1240
|
|
|
1229
1241
|
async function _safeJsonFetch(url, opts) {
|
|
@@ -1256,23 +1268,32 @@ async function _safeJsonFetch(url, opts) {
|
|
|
1256
1268
|
// Daemon not running at all
|
|
1257
1269
|
}
|
|
1258
1270
|
|
|
1271
|
+
let spawnResult = 'ok';
|
|
1259
1272
|
if (needsRestart) {
|
|
1260
1273
|
startSpin.start('Restarting daemon (new version)');
|
|
1261
|
-
// Kill old daemon
|
|
1262
1274
|
try {
|
|
1263
1275
|
const { execSync } = await import('node:child_process');
|
|
1264
1276
|
execSync('pkill -f "daemon.mjs" 2>/dev/null; true', { stdio: 'ignore' });
|
|
1265
1277
|
} catch {}
|
|
1266
1278
|
await new Promise(r => setTimeout(r, 800));
|
|
1267
|
-
|
|
1279
|
+
spawnResult = await _spawnDaemon();
|
|
1280
|
+
daemonOk = spawnResult === 'ok';
|
|
1268
1281
|
} else if (!daemonOk) {
|
|
1269
1282
|
startSpin.start('Starting daemon');
|
|
1270
|
-
|
|
1283
|
+
spawnResult = await _spawnDaemon();
|
|
1284
|
+
daemonOk = spawnResult === 'ok';
|
|
1271
1285
|
}
|
|
1272
1286
|
|
|
1273
1287
|
startSpin.stop();
|
|
1274
1288
|
if (!daemonOk) {
|
|
1275
|
-
|
|
1289
|
+
if (spawnResult === 'no-config') {
|
|
1290
|
+
console.log(` ${fmt(C.yellow, '!')} Not configured yet. Run: ${fmt(C.bold, '0agent init')}`);
|
|
1291
|
+
} else if (spawnResult === 'no-bundle') {
|
|
1292
|
+
console.log(` ${fmt(C.red, '✗')} Daemon bundle missing. Reinstall: ${fmt(C.bold, 'npm i -g 0agent@latest')}`);
|
|
1293
|
+
} else {
|
|
1294
|
+
const logPath = resolve(AGENT_DIR, 'logs', 'daemon.log');
|
|
1295
|
+
console.log(` ${fmt(C.red, '✗')} Daemon failed to start. Check logs: ${fmt(C.dim, logPath)}`);
|
|
1296
|
+
}
|
|
1276
1297
|
rl.prompt();
|
|
1277
1298
|
return;
|
|
1278
1299
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall — runs automatically after `npm install -g 0agent`
|
|
4
|
+
*
|
|
5
|
+
* Ensures all runtime dependencies are installed in the package directory.
|
|
6
|
+
* This handles cases where native modules (better-sqlite3) failed to build,
|
|
7
|
+
* or where the package was installed in a non-standard way.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { resolve, dirname } from 'node:path';
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
16
|
+
|
|
17
|
+
// Runtime deps that must be present (matches bundle externals in scripts/bundle.mjs)
|
|
18
|
+
const REQUIRED = [
|
|
19
|
+
'better-sqlite3',
|
|
20
|
+
'hono',
|
|
21
|
+
'@hono/node-server',
|
|
22
|
+
'ws',
|
|
23
|
+
'yaml',
|
|
24
|
+
'zod',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function depInstalled(name) {
|
|
28
|
+
// Handle scoped packages like @hono/node-server
|
|
29
|
+
const modPath = resolve(pkgRoot, 'node_modules', name);
|
|
30
|
+
return existsSync(modPath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const missing = REQUIRED.filter(d => !depInstalled(d));
|
|
34
|
+
|
|
35
|
+
if (missing.length === 0) {
|
|
36
|
+
// All present — check if better-sqlite3 native binary actually loads
|
|
37
|
+
try {
|
|
38
|
+
const { createRequire } = await import('node:module');
|
|
39
|
+
const req = createRequire(import.meta.url);
|
|
40
|
+
req('better-sqlite3');
|
|
41
|
+
} catch {
|
|
42
|
+
// Binary broken — try rebuild
|
|
43
|
+
try {
|
|
44
|
+
process.stdout.write(' Rebuilding better-sqlite3 for this platform…\n');
|
|
45
|
+
execSync('npm rebuild better-sqlite3', {
|
|
46
|
+
cwd: pkgRoot,
|
|
47
|
+
stdio: 'inherit',
|
|
48
|
+
timeout: 60_000,
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
process.stdout.write(
|
|
52
|
+
' ⚠ Could not rebuild better-sqlite3. Memory persistence will be disabled.\n' +
|
|
53
|
+
' If you need it, install build tools:\n' +
|
|
54
|
+
' macOS: xcode-select --install\n' +
|
|
55
|
+
' Linux: sudo apt-get install build-essential python3\n'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
process.stdout.write(` Installing dependencies: ${missing.join(', ')}\n`);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
execSync(
|
|
66
|
+
`npm install --omit=dev --prefix "${pkgRoot}" ${missing.join(' ')}`,
|
|
67
|
+
{ stdio: 'inherit', timeout: 120_000 }
|
|
68
|
+
);
|
|
69
|
+
process.stdout.write(' ✓ Dependencies installed\n');
|
|
70
|
+
} catch (err) {
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
` ✗ Failed to install some dependencies: ${err.message}\n` +
|
|
73
|
+
` Try manually: npm install --prefix "${pkgRoot}" ${missing.join(' ')}\n`
|
|
74
|
+
);
|
|
75
|
+
// Don't exit non-zero — let the agent start anyway; daemon will log the actual error
|
|
76
|
+
}
|
package/dist/daemon.mjs
CHANGED
|
@@ -2571,13 +2571,13 @@ var init_GUICapability = __esm({
|
|
|
2571
2571
|
description = "Automate desktop GUI \u2014 click, type, screenshot, hotkeys, find text on screen.";
|
|
2572
2572
|
toolDefinition = {
|
|
2573
2573
|
name: "gui_automation",
|
|
2574
|
-
description: 'Automate desktop GUI interactions. Take screenshots to see the current screen state, click on buttons/links/fields, type text, press keyboard shortcuts, scroll, open apps.
|
|
2574
|
+
description: 'Automate desktop GUI interactions. Take screenshots to see the current screen state, click on buttons/links/fields, type text, press keyboard shortcuts, scroll, open apps. IMPORTANT: Limit screenshots to at most 3 per task \u2014 avoid re-screenshotting if you already know the layout. Prefer targeted actions (click, find_and_click, hotkey) over repeated screenshots. Use get_cursor_pos to check cursor position without a full screenshot. To open a website, ALWAYS use action="open_url" \u2014 never open_app + new tab, which creates duplicate windows.',
|
|
2575
2575
|
input_schema: {
|
|
2576
2576
|
type: "object",
|
|
2577
2577
|
properties: {
|
|
2578
2578
|
action: {
|
|
2579
2579
|
type: "string",
|
|
2580
|
-
description: '"screenshot" | "click" | "double_click" | "right_click" | "move" | "type" | "hotkey" | "scroll" | "drag" | "find_and_click" | "get_screen_size" | "open_app"'
|
|
2580
|
+
description: '"screenshot" | "click" | "double_click" | "right_click" | "move" | "type" | "hotkey" | "scroll" | "drag" | "find_and_click" | "get_screen_size" | "get_cursor_pos" | "open_url" | "open_app"'
|
|
2581
2581
|
},
|
|
2582
2582
|
x: { type: "number", description: "X coordinate (pixels from left)" },
|
|
2583
2583
|
y: { type: "number", description: "Y coordinate (pixels from top)" },
|
|
@@ -2588,6 +2588,7 @@ var init_GUICapability = __esm({
|
|
|
2588
2588
|
direction: { type: "string", description: '"up" | "down" | "left" | "right" for scroll' },
|
|
2589
2589
|
amount: { type: "number", description: "Scroll clicks (default 3)" },
|
|
2590
2590
|
app: { type: "string", description: 'App name to open e.g. "Safari", "Terminal", "Chrome"' },
|
|
2591
|
+
url: { type: "string", description: 'URL to open e.g. "https://example.com" (use with open_url)' },
|
|
2591
2592
|
interval: { type: "number", description: "Seconds to wait between actions (default 0.05)" },
|
|
2592
2593
|
duration: { type: "number", description: "Seconds for mouse movement animation (default 0.2)" }
|
|
2593
2594
|
},
|
|
@@ -2599,7 +2600,7 @@ var init_GUICapability = __esm({
|
|
|
2599
2600
|
const start = Date.now();
|
|
2600
2601
|
const script = this._buildScript(action, input);
|
|
2601
2602
|
if (!script) {
|
|
2602
|
-
return { success: false, output: `Unknown GUI action: "${action}". Valid: screenshot, click, double_click, right_click, move, type, hotkey, scroll, drag, find_and_click, get_screen_size, open_app`, duration_ms: 0 };
|
|
2603
|
+
return { success: false, output: `Unknown GUI action: "${action}". Valid: screenshot, click, double_click, right_click, move, type, hotkey, scroll, drag, find_and_click, get_screen_size, get_cursor_pos, open_url, open_app`, duration_ms: 0 };
|
|
2603
2604
|
}
|
|
2604
2605
|
const tmpFile = resolve3(tmpdir(), `0agent_gui_${Date.now()}.py`);
|
|
2605
2606
|
writeFileSync2(tmpFile, script, "utf8");
|
|
@@ -2652,6 +2653,7 @@ var init_GUICapability = __esm({
|
|
|
2652
2653
|
const dir = input.direction != null ? String(input.direction) : "down";
|
|
2653
2654
|
const amount = input.amount != null ? Number(input.amount) : 3;
|
|
2654
2655
|
const app = input.app != null ? String(input.app) : "";
|
|
2656
|
+
const url = input.url != null ? String(input.url) : "";
|
|
2655
2657
|
const interval = input.interval != null ? Number(input.interval) : 0.05;
|
|
2656
2658
|
const duration = input.duration != null ? Number(input.duration) : 0.2;
|
|
2657
2659
|
const header = `
|
|
@@ -2666,6 +2668,11 @@ pyautogui.PAUSE = ${interval}
|
|
|
2666
2668
|
return header + `
|
|
2667
2669
|
w, h = pyautogui.size()
|
|
2668
2670
|
print(f"Screen size: {w} x {h}")
|
|
2671
|
+
`;
|
|
2672
|
+
case "get_cursor_pos":
|
|
2673
|
+
return header + `
|
|
2674
|
+
x, y = pyautogui.position()
|
|
2675
|
+
print(f"Cursor position: ({x}, {y})")
|
|
2669
2676
|
`;
|
|
2670
2677
|
case "screenshot": {
|
|
2671
2678
|
return header + `
|
|
@@ -2705,10 +2712,13 @@ try:
|
|
|
2705
2712
|
print("\\n".join(hits[:40]))
|
|
2706
2713
|
except ImportError:
|
|
2707
2714
|
print("(pytesseract not installed \u2014 install it for OCR: pip3 install pytesseract)")
|
|
2708
|
-
print(f"Screenshot saved: {shot_path}")
|
|
2709
2715
|
except Exception as e:
|
|
2710
2716
|
print(f"OCR failed: {e}")
|
|
2711
|
-
|
|
2717
|
+
finally:
|
|
2718
|
+
try:
|
|
2719
|
+
os.remove(shot_path)
|
|
2720
|
+
except Exception:
|
|
2721
|
+
pass
|
|
2712
2722
|
`;
|
|
2713
2723
|
}
|
|
2714
2724
|
case "click":
|
|
@@ -2790,13 +2800,78 @@ for i, word in enumerate(data['text']):
|
|
|
2790
2800
|
cy = data['top'][i] + data['height'][i] // 2
|
|
2791
2801
|
found.append((cx, cy, word))
|
|
2792
2802
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2803
|
+
try:
|
|
2804
|
+
if found:
|
|
2805
|
+
cx, cy, word = found[0]
|
|
2806
|
+
pyautogui.click(cx, cy, duration=${duration})
|
|
2807
|
+
print(f"Found '{word}' at ({cx},{cy}) \u2014 clicked")
|
|
2808
|
+
else:
|
|
2809
|
+
print(f"Text '${safeText}' not found on screen. Take a screenshot to see current state.")
|
|
2810
|
+
sys.exit(1)
|
|
2811
|
+
finally:
|
|
2812
|
+
try:
|
|
2813
|
+
os.remove(shot_path)
|
|
2814
|
+
except Exception:
|
|
2815
|
+
pass
|
|
2816
|
+
`;
|
|
2817
|
+
}
|
|
2818
|
+
case "open_url": {
|
|
2819
|
+
if (!url) return null;
|
|
2820
|
+
const safeUrl = url.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2821
|
+
const osName = platform2();
|
|
2822
|
+
if (osName === "darwin") {
|
|
2823
|
+
return header + `
|
|
2824
|
+
import subprocess
|
|
2825
|
+
|
|
2826
|
+
url = '${safeUrl}'
|
|
2827
|
+
|
|
2828
|
+
# Check if Chrome is running
|
|
2829
|
+
chrome_running = subprocess.run(['pgrep', '-x', 'Google Chrome'], capture_output=True).returncode == 0
|
|
2830
|
+
firefox_running = subprocess.run(['pgrep', '-x', 'firefox'], capture_output=True).returncode == 0
|
|
2831
|
+
safari_running = subprocess.run(['pgrep', '-x', 'Safari'], capture_output=True).returncode == 0
|
|
2832
|
+
|
|
2833
|
+
if chrome_running:
|
|
2834
|
+
# Open in existing Chrome window \u2014 no new window created
|
|
2835
|
+
script = f'tell application "Google Chrome" to open location "{url}"'
|
|
2836
|
+
subprocess.run(['osascript', '-e', script])
|
|
2837
|
+
subprocess.run(['osascript', '-e', 'tell application "Google Chrome" to activate'])
|
|
2838
|
+
print(f"Navigated Chrome to: {url}")
|
|
2839
|
+
elif firefox_running:
|
|
2840
|
+
script = f'tell application "Firefox" to open location "{url}"'
|
|
2841
|
+
subprocess.run(['osascript', '-e', script])
|
|
2842
|
+
subprocess.run(['osascript', '-e', 'tell application "Firefox" to activate'])
|
|
2843
|
+
print(f"Navigated Firefox to: {url}")
|
|
2844
|
+
elif safari_running:
|
|
2845
|
+
script = f'tell application "Safari" to open location "{url}"'
|
|
2846
|
+
subprocess.run(['osascript', '-e', script])
|
|
2847
|
+
subprocess.run(['osascript', '-e', 'tell application "Safari" to activate'])
|
|
2848
|
+
print(f"Navigated Safari to: {url}")
|
|
2797
2849
|
else:
|
|
2798
|
-
|
|
2799
|
-
|
|
2850
|
+
# No browser open \u2014 launch default browser with the URL
|
|
2851
|
+
subprocess.run(['open', url])
|
|
2852
|
+
print(f"Launched browser with: {url}")
|
|
2853
|
+
time.sleep(1.0)
|
|
2854
|
+
`;
|
|
2855
|
+
}
|
|
2856
|
+
return header + `
|
|
2857
|
+
import subprocess
|
|
2858
|
+
|
|
2859
|
+
url = '${safeUrl}'
|
|
2860
|
+
|
|
2861
|
+
# Try to reuse existing browser via wmctrl/xdotool, fall back to xdg-open
|
|
2862
|
+
chrome_pid = subprocess.run(['pgrep', '-x', 'chrome'], capture_output=True)
|
|
2863
|
+
firefox_pid = subprocess.run(['pgrep', '-x', 'firefox'], capture_output=True)
|
|
2864
|
+
|
|
2865
|
+
if chrome_pid.returncode == 0:
|
|
2866
|
+
subprocess.Popen(['google-chrome', '--new-tab', url])
|
|
2867
|
+
print(f"Opened in Chrome tab: {url}")
|
|
2868
|
+
elif firefox_pid.returncode == 0:
|
|
2869
|
+
subprocess.Popen(['firefox', '--new-tab', url])
|
|
2870
|
+
print(f"Opened in Firefox tab: {url}")
|
|
2871
|
+
else:
|
|
2872
|
+
subprocess.Popen(['xdg-open', url])
|
|
2873
|
+
print(f"Opened with default browser: {url}")
|
|
2874
|
+
time.sleep(1.0)
|
|
2800
2875
|
`;
|
|
2801
2876
|
}
|
|
2802
2877
|
case "open_app": {
|
|
@@ -3009,8 +3084,8 @@ var init_AgentExecutor = __esm({
|
|
|
3009
3084
|
init_capabilities();
|
|
3010
3085
|
SELF_MOD_PATTERN = /\b(yourself|the agent|this agent|this cli|0agent|your code|your source|agent cli|improve.*agent|update.*agent|add.*to.*agent|fix.*agent|self.?improv)\b/i;
|
|
3011
3086
|
AgentExecutor = class {
|
|
3012
|
-
constructor(
|
|
3013
|
-
this.llm =
|
|
3087
|
+
constructor(llm, config, onStep, onToken) {
|
|
3088
|
+
this.llm = llm;
|
|
3014
3089
|
this.config = config;
|
|
3015
3090
|
this.onStep = onStep;
|
|
3016
3091
|
this.onToken = onToken;
|
|
@@ -3025,7 +3100,7 @@ var init_AgentExecutor = __esm({
|
|
|
3025
3100
|
maxCommandMs;
|
|
3026
3101
|
registry;
|
|
3027
3102
|
agentRoot;
|
|
3028
|
-
async execute(task, systemContext) {
|
|
3103
|
+
async execute(task, systemContext, signal) {
|
|
3029
3104
|
const filesWritten = [];
|
|
3030
3105
|
const commandsRun = [];
|
|
3031
3106
|
let totalTokens = 0;
|
|
@@ -3041,6 +3116,10 @@ var init_AgentExecutor = __esm({
|
|
|
3041
3116
|
}
|
|
3042
3117
|
let finalOutput = "";
|
|
3043
3118
|
for (let i = 0; i < this.maxIterations; i++) {
|
|
3119
|
+
if (signal?.aborted) {
|
|
3120
|
+
finalOutput = "Cancelled.";
|
|
3121
|
+
break;
|
|
3122
|
+
}
|
|
3044
3123
|
this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
|
|
3045
3124
|
let response;
|
|
3046
3125
|
let llmFailed = false;
|
|
@@ -3056,7 +3135,8 @@ var init_AgentExecutor = __esm({
|
|
|
3056
3135
|
(token) => {
|
|
3057
3136
|
this.onToken(token);
|
|
3058
3137
|
finalOutput += token;
|
|
3059
|
-
}
|
|
3138
|
+
},
|
|
3139
|
+
signal
|
|
3060
3140
|
);
|
|
3061
3141
|
break;
|
|
3062
3142
|
} catch (err) {
|
|
@@ -3541,8 +3621,8 @@ var init_RuntimeSelfHeal = __esm({
|
|
|
3541
3621
|
// network issue
|
|
3542
3622
|
];
|
|
3543
3623
|
RuntimeSelfHeal = class {
|
|
3544
|
-
constructor(
|
|
3545
|
-
this.llm =
|
|
3624
|
+
constructor(llm, eventBus) {
|
|
3625
|
+
this.llm = llm;
|
|
3546
3626
|
this.eventBus = eventBus;
|
|
3547
3627
|
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
3548
3628
|
while (dir !== "/" && !existsSync6(resolve6(dir, "package.json"))) {
|
|
@@ -3736,8 +3816,8 @@ var init_SelfHealLoop = __esm({
|
|
|
3736
3816
|
init_ExecutionVerifier();
|
|
3737
3817
|
init_RuntimeSelfHeal();
|
|
3738
3818
|
SelfHealLoop = class {
|
|
3739
|
-
constructor(
|
|
3740
|
-
this.llm =
|
|
3819
|
+
constructor(llm, config, onStep, onToken, maxAttempts = 3, runtimeHealer) {
|
|
3820
|
+
this.llm = llm;
|
|
3741
3821
|
this.config = config;
|
|
3742
3822
|
this.onStep = onStep;
|
|
3743
3823
|
this.onToken = onToken;
|
|
@@ -3746,17 +3826,18 @@ var init_SelfHealLoop = __esm({
|
|
|
3746
3826
|
this.verifier = new ExecutionVerifier(config.cwd);
|
|
3747
3827
|
}
|
|
3748
3828
|
verifier;
|
|
3749
|
-
async executeWithHealing(task, systemContext) {
|
|
3829
|
+
async executeWithHealing(task, systemContext, signal) {
|
|
3750
3830
|
const attempts = [];
|
|
3751
3831
|
let currentContext = systemContext;
|
|
3752
3832
|
let finalResult = null;
|
|
3753
3833
|
let lastVerification = { success: true, method: "none", details: "", retryable: false, elapsed_ms: 0 };
|
|
3754
3834
|
for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
|
|
3835
|
+
if (signal?.aborted) break;
|
|
3755
3836
|
if (attempt > 1) {
|
|
3756
3837
|
this.onStep(`\u21BA Self-healing (attempt ${attempt}/${this.maxAttempts}): ${lastVerification.details}`);
|
|
3757
3838
|
}
|
|
3758
3839
|
const executor = new AgentExecutor(this.llm, this.config, this.onStep, this.onToken);
|
|
3759
|
-
const result = await executor.execute(task, currentContext);
|
|
3840
|
+
const result = await executor.execute(task, currentContext, signal);
|
|
3760
3841
|
finalResult = result;
|
|
3761
3842
|
lastVerification = await this.verifier.verify(result);
|
|
3762
3843
|
attempts.push({ attempt_number: attempt, error_context: currentContext ?? "", result, verification: lastVerification });
|
|
@@ -4207,24 +4288,24 @@ var LLMExecutor = class {
|
|
|
4207
4288
|
return { content: res.content, tokens_used: res.tokens_used, model: res.model };
|
|
4208
4289
|
}
|
|
4209
4290
|
// ─── Tool-calling completion with optional streaming ─────────────────────
|
|
4210
|
-
async completeWithTools(messages, tools, system, onToken) {
|
|
4291
|
+
async completeWithTools(messages, tools, system, onToken, signal) {
|
|
4211
4292
|
switch (this.config.provider) {
|
|
4212
4293
|
case "anthropic":
|
|
4213
|
-
return this.anthropic(messages, tools, system, onToken);
|
|
4294
|
+
return this.anthropic(messages, tools, system, onToken, signal);
|
|
4214
4295
|
case "openai":
|
|
4215
|
-
return this.openai(messages, tools, system, onToken);
|
|
4296
|
+
return this.openai(messages, tools, system, onToken, void 0, signal);
|
|
4216
4297
|
case "xai":
|
|
4217
|
-
return this.openai(messages, tools, system, onToken, "https://api.x.ai/v1");
|
|
4298
|
+
return this.openai(messages, tools, system, onToken, "https://api.x.ai/v1", signal);
|
|
4218
4299
|
case "gemini":
|
|
4219
|
-
return this.openai(messages, tools, system, onToken, "https://generativelanguage.googleapis.com/v1beta/openai");
|
|
4300
|
+
return this.openai(messages, tools, system, onToken, "https://generativelanguage.googleapis.com/v1beta/openai", signal);
|
|
4220
4301
|
case "ollama":
|
|
4221
4302
|
return this.ollama(messages, system, onToken);
|
|
4222
4303
|
default:
|
|
4223
|
-
return this.openai(messages, tools, system, onToken);
|
|
4304
|
+
return this.openai(messages, tools, system, onToken, void 0, signal);
|
|
4224
4305
|
}
|
|
4225
4306
|
}
|
|
4226
4307
|
// ─── Anthropic ───────────────────────────────────────────────────────────
|
|
4227
|
-
async anthropic(messages, tools, system, onToken) {
|
|
4308
|
+
async anthropic(messages, tools, system, onToken, signal) {
|
|
4228
4309
|
const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
|
|
4229
4310
|
const filtered = messages.filter((m) => m.role !== "system");
|
|
4230
4311
|
const anthropicMsgs = filtered.map((m) => {
|
|
@@ -4272,8 +4353,7 @@ var LLMExecutor = class {
|
|
|
4272
4353
|
"anthropic-version": "2023-06-01"
|
|
4273
4354
|
},
|
|
4274
4355
|
body: JSON.stringify(body),
|
|
4275
|
-
signal: AbortSignal.timeout(12e4)
|
|
4276
|
-
// 60s timeout
|
|
4356
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(12e4)]) : AbortSignal.timeout(12e4)
|
|
4277
4357
|
});
|
|
4278
4358
|
if (!res.ok) {
|
|
4279
4359
|
const err = await res.text();
|
|
@@ -4356,7 +4436,7 @@ var LLMExecutor = class {
|
|
|
4356
4436
|
};
|
|
4357
4437
|
}
|
|
4358
4438
|
// ─── OpenAI (also xAI, Gemini) ───────────────────────────────────────────
|
|
4359
|
-
async openai(messages, tools, system, onToken, baseUrl = "https://api.openai.com/v1") {
|
|
4439
|
+
async openai(messages, tools, system, onToken, baseUrl = "https://api.openai.com/v1", signal) {
|
|
4360
4440
|
const allMessages = [];
|
|
4361
4441
|
const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
|
|
4362
4442
|
if (sysContent) allMessages.push({ role: "system", content: sysContent });
|
|
@@ -4397,7 +4477,7 @@ var LLMExecutor = class {
|
|
|
4397
4477
|
"Authorization": `Bearer ${this.config.api_key}`
|
|
4398
4478
|
},
|
|
4399
4479
|
body: JSON.stringify(body),
|
|
4400
|
-
signal: AbortSignal.timeout(12e4)
|
|
4480
|
+
signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(12e4)]) : AbortSignal.timeout(12e4)
|
|
4401
4481
|
});
|
|
4402
4482
|
if (!res.ok) {
|
|
4403
4483
|
const err = await res.text();
|
|
@@ -4816,6 +4896,7 @@ import { homedir as homedir2 } from "node:os";
|
|
|
4816
4896
|
import YAML2 from "yaml";
|
|
4817
4897
|
var SessionManager = class {
|
|
4818
4898
|
sessions = /* @__PURE__ */ new Map();
|
|
4899
|
+
abortControllers = /* @__PURE__ */ new Map();
|
|
4819
4900
|
inferenceEngine;
|
|
4820
4901
|
eventBus;
|
|
4821
4902
|
graph;
|
|
@@ -4944,6 +5025,11 @@ var SessionManager = class {
|
|
|
4944
5025
|
session.status = "cancelled";
|
|
4945
5026
|
session.completed_at = Date.now();
|
|
4946
5027
|
session.error = "cancelled";
|
|
5028
|
+
const controller = this.abortControllers.get(id);
|
|
5029
|
+
if (controller) {
|
|
5030
|
+
controller.abort();
|
|
5031
|
+
this.abortControllers.delete(id);
|
|
5032
|
+
}
|
|
4947
5033
|
this.emit({
|
|
4948
5034
|
type: "session.failed",
|
|
4949
5035
|
session_id: id,
|
|
@@ -5005,6 +5091,9 @@ var SessionManager = class {
|
|
|
5005
5091
|
* All callers must have created the session first.
|
|
5006
5092
|
*/
|
|
5007
5093
|
async _executeSession(sessionId, enrichedReq) {
|
|
5094
|
+
const abortController = new AbortController();
|
|
5095
|
+
this.abortControllers.set(sessionId, abortController);
|
|
5096
|
+
const signal = abortController.signal;
|
|
5008
5097
|
try {
|
|
5009
5098
|
await this.startSession(sessionId);
|
|
5010
5099
|
this.addStep(sessionId, `Extracting entities from: "${enrichedReq.task.slice(0, 60)}${enrichedReq.task.length > 60 ? "\u2026" : ""}"`);
|
|
@@ -5082,9 +5171,9 @@ Current task:`;
|
|
|
5082
5171
|
(step) => this.addStep(sessionId, step),
|
|
5083
5172
|
(token) => this.emit({ type: "session.token", session_id: sessionId, token })
|
|
5084
5173
|
);
|
|
5085
|
-
agentResult = await healLoop.executeWithHealing(enrichedReq.task, systemContext);
|
|
5174
|
+
agentResult = await healLoop.executeWithHealing(enrichedReq.task, systemContext, signal);
|
|
5086
5175
|
} catch {
|
|
5087
|
-
agentResult = await executor.execute(enrichedReq.task, systemContext);
|
|
5176
|
+
agentResult = await executor.execute(enrichedReq.task, systemContext, signal);
|
|
5088
5177
|
}
|
|
5089
5178
|
if (this.conversationStore && userEntityId) {
|
|
5090
5179
|
const now = Date.now();
|
|
@@ -5183,6 +5272,8 @@ Current task:`;
|
|
|
5183
5272
|
} catch (err) {
|
|
5184
5273
|
const message = err instanceof Error ? err.message : String(err);
|
|
5185
5274
|
this.failSession(sessionId, message);
|
|
5275
|
+
} finally {
|
|
5276
|
+
this.abortControllers.delete(sessionId);
|
|
5186
5277
|
}
|
|
5187
5278
|
return this.sessions.get(sessionId);
|
|
5188
5279
|
}
|
|
@@ -5288,7 +5379,7 @@ Conversation:
|
|
|
5288
5379
|
User: ${task.slice(0, 600)}
|
|
5289
5380
|
Agent: ${output.slice(0, 500)}`;
|
|
5290
5381
|
try {
|
|
5291
|
-
const resp = await
|
|
5382
|
+
const resp = await extractLLM.complete(
|
|
5292
5383
|
[{ role: "user", content: prompt }],
|
|
5293
5384
|
"You are a memory extraction system. Be concise. Extract only factual, durable information. Return valid JSON only."
|
|
5294
5385
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.54",
|
|
4
4
|
"description": "A persistent, learning AI agent that runs on your machine. An agent that learns.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"bin/",
|
|
14
14
|
"dist/",
|
|
15
15
|
"skills/",
|
|
16
|
-
"seeds/"
|
|
17
|
-
"bin/chat.js"
|
|
16
|
+
"seeds/"
|
|
18
17
|
],
|
|
19
18
|
"scripts": {
|
|
20
19
|
"build": "turbo run build",
|
|
21
20
|
"bundle": "node scripts/bundle.mjs",
|
|
22
21
|
"test": "turbo run test",
|
|
23
22
|
"lint": "turbo run lint",
|
|
23
|
+
"postinstall": "node bin/postinstall.js",
|
|
24
24
|
"prepublishOnly": "pnpm build --filter='!@0agent/dashboard' --filter='!@0agent/core-native' && node scripts/bundle.mjs"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|