0agent 1.0.39 → 1.0.41
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 +21 -8
- package/bin/chat.js +88 -13
- package/dist/daemon.mjs +207 -70
- package/package.json +1 -1
package/bin/0agent.js
CHANGED
|
@@ -278,7 +278,16 @@ async function runInit() {
|
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
// ── Step 3:
|
|
281
|
+
// ── Step 3: Workspace folder ─────────────────────────────────────────────
|
|
282
|
+
const defaultWorkspace = resolve(homedir(), '0agent-workspace');
|
|
283
|
+
console.log(`\n \x1b[1mWorkspace folder\x1b[0m`);
|
|
284
|
+
console.log(` \x1b[2mAll files the agent creates will go here. You own it.\x1b[0m`);
|
|
285
|
+
const wsRaw = await ask(` Path [${defaultWorkspace}]: `);
|
|
286
|
+
const workspacePath = wsRaw.trim() || defaultWorkspace;
|
|
287
|
+
mkdirSync(workspacePath, { recursive: true });
|
|
288
|
+
console.log(` \x1b[32m✓\x1b[0m Workspace: \x1b[36m${workspacePath}\x1b[0m`);
|
|
289
|
+
|
|
290
|
+
// ── Step 4: Embedding ────────────────────────────────────────────────────
|
|
282
291
|
const embIdx = await arrowSelect('Embeddings (for semantic memory search)?', [
|
|
283
292
|
'Local via Ollama (nomic-embed-text) — free, private',
|
|
284
293
|
'OpenAI text-embedding-3-small — cloud',
|
|
@@ -286,12 +295,12 @@ async function runInit() {
|
|
|
286
295
|
], 0);
|
|
287
296
|
const embeddingProvider = ['nomic-ollama', 'openai', 'none'][embIdx];
|
|
288
297
|
|
|
289
|
-
// ── Step
|
|
298
|
+
// ── Step 5: Sandbox ──────────────────────────────────────────────────────
|
|
290
299
|
const sandboxes = detectSandboxes();
|
|
291
300
|
const sandboxChoice = sandboxes[0] ?? 'process';
|
|
292
301
|
console.log(`\n Sandbox: \x1b[32m${sandboxChoice}\x1b[0m detected`);
|
|
293
302
|
|
|
294
|
-
// ── Step
|
|
303
|
+
// ── Step 6: Seed graph ───────────────────────────────────────────────────
|
|
295
304
|
const seedIdx = await arrowSelect('Starting knowledge?', [
|
|
296
305
|
'software-engineering — sprint workflow + 15 skills ← recommended',
|
|
297
306
|
'Start from scratch',
|
|
@@ -300,11 +309,12 @@ async function runInit() {
|
|
|
300
309
|
|
|
301
310
|
// ── Summary ───────────────────────────────────────────────────────────────
|
|
302
311
|
console.log('\n \x1b[1mReady to launch\x1b[0m\n');
|
|
303
|
-
console.log(` LLM:
|
|
304
|
-
console.log(` API Key:
|
|
305
|
-
console.log(` Memory:
|
|
306
|
-
console.log(`
|
|
307
|
-
console.log(`
|
|
312
|
+
console.log(` LLM: \x1b[36m${providerKey}/${model}\x1b[0m`);
|
|
313
|
+
console.log(` API Key: ${apiKey ? '\x1b[32m✓ set\x1b[0m (' + apiKey.slice(0, 8) + '••••)' : '\x1b[33mnot set\x1b[0m'}`);
|
|
314
|
+
console.log(` Memory: ${ghToken ? `\x1b[32mgithub.com/${ghOwner}/0agent-memory\x1b[0m` : '\x1b[2mlocal only\x1b[0m'}`);
|
|
315
|
+
console.log(` Workspace: \x1b[36m${workspacePath}\x1b[0m`);
|
|
316
|
+
console.log(` Sandbox: \x1b[36m${sandboxChoice}\x1b[0m`);
|
|
317
|
+
console.log(` Seed: \x1b[36m${seedName ?? 'scratch'}\x1b[0m\n`);
|
|
308
318
|
|
|
309
319
|
// Write config
|
|
310
320
|
const dbPath = resolve(AGENT_DIR, 'graph.db');
|
|
@@ -329,6 +339,9 @@ embedding:
|
|
|
329
339
|
sandbox:
|
|
330
340
|
backend: ${sandboxChoice}
|
|
331
341
|
|
|
342
|
+
workspace:
|
|
343
|
+
path: "${workspacePath}"
|
|
344
|
+
|
|
332
345
|
mcp_servers: []
|
|
333
346
|
|
|
334
347
|
server:
|
package/bin/chat.js
CHANGED
|
@@ -879,6 +879,64 @@ async function _safeJsonFetch(url, opts) {
|
|
|
879
879
|
console.log(` ${fmt(C.yellow, '⚠')} LLM check failed: ${e.message}\n`);
|
|
880
880
|
}
|
|
881
881
|
|
|
882
|
+
// ── Step 3: Workspace folder check ───────────────────────────────────────
|
|
883
|
+
// If no workspace is configured, ask the user to set one now.
|
|
884
|
+
// Then export memory (graph nodes) to that folder.
|
|
885
|
+
await (async () => {
|
|
886
|
+
const wsPath = cfg?.workspace?.path;
|
|
887
|
+
if (wsPath) {
|
|
888
|
+
// Already configured — just ensure the folder exists
|
|
889
|
+
try { mkdirSync(wsPath, { recursive: true }); } catch {}
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// No workspace configured — ask inline
|
|
894
|
+
const { homedir: hd } = await import('node:os');
|
|
895
|
+
const defaultWs = resolve(hd(), '0agent-workspace');
|
|
896
|
+
|
|
897
|
+
process.stdout.write(`\n ${fmt(C.yellow, '⚠')} No workspace folder configured.\n`);
|
|
898
|
+
process.stdout.write(` ${fmt(C.dim, 'The agent needs a folder to create and store files.')}\n\n`);
|
|
899
|
+
|
|
900
|
+
// Temporarily close readline so we can read a raw line
|
|
901
|
+
const wsInput = await new Promise((res) => {
|
|
902
|
+
process.stdout.write(` ${fmt(C.bold, 'Workspace path')} ${fmt(C.dim, `[${defaultWs}]`)}: `);
|
|
903
|
+
rl.once('line', (line) => res(line.trim() || defaultWs));
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
const chosenPath = resolve(wsInput.replace(/^~/, hd()));
|
|
907
|
+
try {
|
|
908
|
+
mkdirSync(chosenPath, { recursive: true });
|
|
909
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Created: ${fmt(C.cyan, chosenPath)}\n`);
|
|
910
|
+
} catch (e) {
|
|
911
|
+
process.stdout.write(` ${fmt(C.red, '✗')} Could not create folder: ${e.message}\n`);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Save workspace path to config
|
|
916
|
+
if (!cfg) cfg = {};
|
|
917
|
+
cfg.workspace = { path: chosenPath };
|
|
918
|
+
saveConfig(cfg);
|
|
919
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Workspace saved to config\n`);
|
|
920
|
+
|
|
921
|
+
// Export memory (graph nodes) to workspace as a JSON snapshot
|
|
922
|
+
try {
|
|
923
|
+
const nodesRes = await fetch(`${BASE_URL}/api/graph/nodes?limit=9999`, {
|
|
924
|
+
signal: AbortSignal.timeout(5000),
|
|
925
|
+
});
|
|
926
|
+
if (nodesRes.ok) {
|
|
927
|
+
const nodes = await nodesRes.json();
|
|
928
|
+
const count = Array.isArray(nodes) ? nodes.length : 0;
|
|
929
|
+
if (count > 0) {
|
|
930
|
+
const exportPath = resolve(chosenPath, '.0agent-memory.json');
|
|
931
|
+
writeFileSync(exportPath, JSON.stringify({ exported_at: new Date().toISOString(), nodes }, null, 2), 'utf8');
|
|
932
|
+
process.stdout.write(` ${fmt(C.green, '✓')} Memory exported: ${fmt(C.dim, `${count} nodes → .0agent-memory.json`)}\n`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
} catch {}
|
|
936
|
+
|
|
937
|
+
process.stdout.write('\n');
|
|
938
|
+
})();
|
|
939
|
+
|
|
882
940
|
// ── Auto-update: check npm, update silently, restart ─────────────────────
|
|
883
941
|
// Runs in background after prompt — never blocks startup.
|
|
884
942
|
// If update found: counts down 3s (press any key to skip), then auto-installs.
|
|
@@ -935,8 +993,8 @@ async function _safeJsonFetch(url, opts) {
|
|
|
935
993
|
})();
|
|
936
994
|
|
|
937
995
|
// Restart using the GLOBAL install, not the npx cache that's currently running.
|
|
938
|
-
// After `npm install -g 0agent@latest`,
|
|
939
|
-
//
|
|
996
|
+
// After `npm install -g 0agent@latest`, use `npm root -g` to find the module root.
|
|
997
|
+
// `npm bin -g` is deprecated and unreliable — use the module root instead.
|
|
940
998
|
async function restartWithLatest() {
|
|
941
999
|
try {
|
|
942
1000
|
const { execSync: ex } = await import('node:child_process');
|
|
@@ -944,22 +1002,39 @@ async function restartWithLatest() {
|
|
|
944
1002
|
const { existsSync: ef } = await import('node:fs');
|
|
945
1003
|
const { spawn: sp } = await import('node:child_process');
|
|
946
1004
|
|
|
947
|
-
//
|
|
948
|
-
|
|
949
|
-
|
|
1005
|
+
// npm root -g → e.g. /Users/sahil/.nvm/versions/node/v20/lib/node_modules
|
|
1006
|
+
// The 0agent entry is at {root}/0agent/bin/0agent.js
|
|
1007
|
+
let newBin = null;
|
|
1008
|
+
try {
|
|
1009
|
+
const npmRoot = ex('npm root -g 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
1010
|
+
if (npmRoot) {
|
|
1011
|
+
const candidate = res(npmRoot, '0agent', 'bin', '0agent.js');
|
|
1012
|
+
if (ef(candidate)) newBin = candidate;
|
|
1013
|
+
}
|
|
1014
|
+
} catch {}
|
|
1015
|
+
|
|
1016
|
+
if (!newBin) {
|
|
1017
|
+
// Fallback: npm prefix -g gives the prefix, bin is {prefix}/bin/0agent
|
|
1018
|
+
try {
|
|
1019
|
+
const prefix = ex('npm prefix -g 2>/dev/null', { encoding: 'utf8' }).trim().split('\n')[0];
|
|
1020
|
+
// The script wrapper lives in {prefix}/bin; the actual JS is in lib/node_modules
|
|
1021
|
+
const candidate = prefix ? res(prefix, 'lib', 'node_modules', '0agent', 'bin', '0agent.js') : null;
|
|
1022
|
+
if (candidate && ef(candidate)) newBin = candidate;
|
|
1023
|
+
} catch {}
|
|
1024
|
+
}
|
|
950
1025
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
// Fallback: let the shell find 0agent in PATH
|
|
957
|
-
child = sp('0agent', [], { stdio: 'inherit', shell: true });
|
|
1026
|
+
if (!newBin) {
|
|
1027
|
+
// Cannot locate the new binary — exit so user restarts manually
|
|
1028
|
+
process.stdout.write(` ${fmt(C.dim, 'Restart manually: 0agent')}\n`);
|
|
1029
|
+
process.exit(0);
|
|
1030
|
+
return;
|
|
958
1031
|
}
|
|
1032
|
+
|
|
1033
|
+
const child = sp(process.execPath, [newBin], { stdio: 'inherit' });
|
|
959
1034
|
child.on('close', (code) => process.exit(code ?? 0));
|
|
960
1035
|
process.stdin.pause();
|
|
961
1036
|
} catch {
|
|
962
|
-
process.exit(0);
|
|
1037
|
+
process.exit(0);
|
|
963
1038
|
}
|
|
964
1039
|
}
|
|
965
1040
|
|
package/dist/daemon.mjs
CHANGED
|
@@ -2111,22 +2111,39 @@ var init_WebSearchCapability = __esm({
|
|
|
2111
2111
|
});
|
|
2112
2112
|
|
|
2113
2113
|
// packages/daemon/src/capabilities/BrowserCapability.ts
|
|
2114
|
-
import { spawnSync as spawnSync2, execSync as execSync2 } from "node:child_process";
|
|
2114
|
+
import { spawnSync as spawnSync2, execSync as execSync2, spawn } from "node:child_process";
|
|
2115
|
+
import { platform } from "node:os";
|
|
2116
|
+
function findSystemChrome() {
|
|
2117
|
+
const candidates = platform() === "darwin" ? [
|
|
2118
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
2119
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
2120
|
+
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
2121
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
|
2122
|
+
] : platform() === "linux" ? ["google-chrome", "chromium-browser", "chromium", "microsoft-edge", "brave-browser"] : ["C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"];
|
|
2123
|
+
for (const c of candidates) {
|
|
2124
|
+
try {
|
|
2125
|
+
execSync2(`test -f "${c}" 2>/dev/null || which "${c}" 2>/dev/null`, { stdio: "pipe" });
|
|
2126
|
+
return c;
|
|
2127
|
+
} catch {
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
return null;
|
|
2131
|
+
}
|
|
2115
2132
|
var BrowserCapability;
|
|
2116
2133
|
var init_BrowserCapability = __esm({
|
|
2117
2134
|
"packages/daemon/src/capabilities/BrowserCapability.ts"() {
|
|
2118
2135
|
"use strict";
|
|
2119
2136
|
BrowserCapability = class {
|
|
2120
2137
|
name = "browser_open";
|
|
2121
|
-
description = "Open a URL in
|
|
2138
|
+
description = "Open a URL in the system browser or extract page content. Use when scrape_url fails on JS-heavy pages.";
|
|
2122
2139
|
toolDefinition = {
|
|
2123
2140
|
name: "browser_open",
|
|
2124
|
-
description:
|
|
2141
|
+
description: `Open or read a URL using the system browser. Use action="open" to launch the URL visibly in the user's default browser. Use action="read" (default) to extract page content headlessly. Use when scrape_url fails on JS-heavy pages.`,
|
|
2125
2142
|
input_schema: {
|
|
2126
2143
|
type: "object",
|
|
2127
2144
|
properties: {
|
|
2128
2145
|
url: { type: "string", description: "URL to open" },
|
|
2129
|
-
action: { type: "string", description: '
|
|
2146
|
+
action: { type: "string", description: '"open" \u2014 launch in system browser (visible); "read" \u2014 extract text content (default); "screenshot" \u2014 take screenshot' },
|
|
2130
2147
|
wait_for: { type: "string", description: "CSS selector to wait for before extracting content" },
|
|
2131
2148
|
extract: { type: "string", description: "CSS selector to extract specific element text" }
|
|
2132
2149
|
},
|
|
@@ -2142,23 +2159,27 @@ var init_BrowserCapability = __esm({
|
|
|
2142
2159
|
if (!url.startsWith("http")) {
|
|
2143
2160
|
return { success: false, output: "URL must start with http:// or https://", duration_ms: 0 };
|
|
2144
2161
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2162
|
+
if (action === "open") {
|
|
2163
|
+
return this.openInSystemBrowser(url, start);
|
|
2164
|
+
}
|
|
2165
|
+
const sysChromeExe = findSystemChrome();
|
|
2166
|
+
if (sysChromeExe) {
|
|
2167
|
+
try {
|
|
2168
|
+
const output = await this.chromeFetch(sysChromeExe, url);
|
|
2169
|
+
return { success: true, output, duration_ms: Date.now() - start };
|
|
2170
|
+
} catch {
|
|
2171
|
+
}
|
|
2149
2172
|
}
|
|
2150
2173
|
try {
|
|
2151
|
-
const output = await this.
|
|
2152
|
-
return { success: true, output, fallback_used: "
|
|
2174
|
+
const output = await this.playwrightFetch(url, action, waitFor, extract);
|
|
2175
|
+
return { success: true, output, fallback_used: "playwright", duration_ms: Date.now() - start };
|
|
2153
2176
|
} catch {
|
|
2154
2177
|
}
|
|
2155
2178
|
try {
|
|
2156
2179
|
const res = await fetch(url, {
|
|
2157
2180
|
headers: {
|
|
2158
2181
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0 Safari/537.36",
|
|
2159
|
-
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
2160
|
-
"Accept-Language": "en-US,en;q=0.9",
|
|
2161
|
-
"Accept-Encoding": "gzip, deflate, br"
|
|
2182
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
|
2162
2183
|
},
|
|
2163
2184
|
signal: AbortSignal.timeout(15e3)
|
|
2164
2185
|
});
|
|
@@ -2168,19 +2189,33 @@ var init_BrowserCapability = __esm({
|
|
|
2168
2189
|
} catch (err) {
|
|
2169
2190
|
return {
|
|
2170
2191
|
success: false,
|
|
2171
|
-
output: `Browser unavailable.
|
|
2192
|
+
output: `Browser unavailable. No system Chrome/Chromium found and Playwright not installed.`,
|
|
2172
2193
|
error: err instanceof Error ? err.message : String(err),
|
|
2173
2194
|
duration_ms: Date.now() - start
|
|
2174
2195
|
};
|
|
2175
2196
|
}
|
|
2176
2197
|
}
|
|
2198
|
+
openInSystemBrowser(url, start) {
|
|
2199
|
+
try {
|
|
2200
|
+
const cmd = platform() === "darwin" ? "open" : platform() === "win32" ? "start" : "xdg-open";
|
|
2201
|
+
spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
|
|
2202
|
+
return { success: true, output: `Opened in default browser: ${url}`, duration_ms: Date.now() - start };
|
|
2203
|
+
} catch (err) {
|
|
2204
|
+
return { success: false, output: `Failed to open browser: ${err instanceof Error ? err.message : String(err)}`, duration_ms: Date.now() - start };
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2177
2207
|
async playwrightFetch(url, action, waitFor, extract) {
|
|
2178
2208
|
const waitLine = waitFor ? `await p.waitForSelector('${waitFor}', { timeout: 8000 }).catch(() => {});` : "";
|
|
2179
2209
|
const extractLine = extract ? `const text = await p.$eval('${extract}', el => el.innerText).catch(() => page_text);` : `const text = page_text;`;
|
|
2180
2210
|
const script = `
|
|
2181
2211
|
const { chromium } = require('playwright');
|
|
2182
2212
|
(async () => {
|
|
2183
|
-
|
|
2213
|
+
let b;
|
|
2214
|
+
try {
|
|
2215
|
+
b = await chromium.launch({ channel: 'chrome', headless: true, args: ['--no-sandbox'] });
|
|
2216
|
+
} catch {
|
|
2217
|
+
b = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
|
|
2218
|
+
}
|
|
2184
2219
|
const p = await b.newPage();
|
|
2185
2220
|
await p.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36');
|
|
2186
2221
|
await p.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 20000 });
|
|
@@ -2195,27 +2230,17 @@ var init_BrowserCapability = __esm({
|
|
|
2195
2230
|
if (result.status !== 0) throw new Error(result.stderr || "Playwright failed");
|
|
2196
2231
|
return result.stdout.trim();
|
|
2197
2232
|
}
|
|
2198
|
-
async chromeFetch(url) {
|
|
2199
|
-
const
|
|
2200
|
-
"
|
|
2201
|
-
"
|
|
2202
|
-
"
|
|
2203
|
-
"
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
timeout: 15e3,
|
|
2210
|
-
encoding: "utf8"
|
|
2211
|
-
});
|
|
2212
|
-
if (result.status === 0 && result.stdout) {
|
|
2213
|
-
return result.stdout.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 5e3);
|
|
2214
|
-
}
|
|
2215
|
-
} catch {
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
throw new Error("No system Chrome found");
|
|
2233
|
+
async chromeFetch(executablePath, url) {
|
|
2234
|
+
const result = spawnSync2(executablePath, [
|
|
2235
|
+
"--headless=new",
|
|
2236
|
+
"--no-sandbox",
|
|
2237
|
+
"--disable-gpu",
|
|
2238
|
+
"--disable-dev-shm-usage",
|
|
2239
|
+
"--dump-dom",
|
|
2240
|
+
url
|
|
2241
|
+
], { timeout: 15e3, encoding: "utf8" });
|
|
2242
|
+
if (result.status !== 0 || !result.stdout) throw new Error("Chrome headless failed");
|
|
2243
|
+
return result.stdout.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 5e3);
|
|
2219
2244
|
}
|
|
2220
2245
|
};
|
|
2221
2246
|
}
|
|
@@ -2318,7 +2343,7 @@ else:
|
|
|
2318
2343
|
});
|
|
2319
2344
|
|
|
2320
2345
|
// packages/daemon/src/capabilities/ShellCapability.ts
|
|
2321
|
-
import { spawn } from "node:child_process";
|
|
2346
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2322
2347
|
var ShellCapability;
|
|
2323
2348
|
var init_ShellCapability = __esm({
|
|
2324
2349
|
"packages/daemon/src/capabilities/ShellCapability.ts"() {
|
|
@@ -2362,7 +2387,7 @@ var init_ShellCapability = __esm({
|
|
|
2362
2387
|
...!success && { error: `exit ${code ?? signal}` }
|
|
2363
2388
|
});
|
|
2364
2389
|
};
|
|
2365
|
-
const proc =
|
|
2390
|
+
const proc = spawn2("bash", ["-c", command], {
|
|
2366
2391
|
cwd,
|
|
2367
2392
|
env: { ...process.env, TERM: "dumb" }
|
|
2368
2393
|
// DO NOT set `timeout` here — we manage it manually via timer
|
|
@@ -2406,14 +2431,14 @@ var init_FileCapability = __esm({
|
|
|
2406
2431
|
"use strict";
|
|
2407
2432
|
FileCapability = class {
|
|
2408
2433
|
name = "file_op";
|
|
2409
|
-
description = "Read, write, or
|
|
2434
|
+
description = "Read, write, list files, or create directories. Scoped to working directory.";
|
|
2410
2435
|
toolDefinition = {
|
|
2411
2436
|
name: "file_op",
|
|
2412
|
-
description: "Read, write,
|
|
2437
|
+
description: "Read, write, list files, or create directories in the working directory.",
|
|
2413
2438
|
input_schema: {
|
|
2414
2439
|
type: "object",
|
|
2415
2440
|
properties: {
|
|
2416
|
-
op: { type: "string", description: '"read", "write", or "
|
|
2441
|
+
op: { type: "string", description: '"read", "write", "list", or "mkdir"' },
|
|
2417
2442
|
path: { type: "string", description: "File or directory path (relative to cwd)" },
|
|
2418
2443
|
content: { type: "string", description: "Content for write operation" }
|
|
2419
2444
|
},
|
|
@@ -2448,7 +2473,11 @@ var init_FileCapability = __esm({
|
|
|
2448
2473
|
const entries = readdirSync(safe, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => `${e.isDirectory() ? "d" : "f"} ${e.name}`).join("\n");
|
|
2449
2474
|
return { success: true, output: entries || "(empty)", duration_ms: Date.now() - start };
|
|
2450
2475
|
}
|
|
2451
|
-
|
|
2476
|
+
if (op === "mkdir") {
|
|
2477
|
+
mkdirSync(safe, { recursive: true });
|
|
2478
|
+
return { success: true, output: `Directory created: ${rel}`, duration_ms: Date.now() - start };
|
|
2479
|
+
}
|
|
2480
|
+
return { success: false, output: `Unknown op: ${op}. Use "read", "write", "list", or "mkdir"`, duration_ms: Date.now() - start };
|
|
2452
2481
|
} catch (err) {
|
|
2453
2482
|
return { success: false, output: `Error: ${err instanceof Error ? err.message : String(err)}`, duration_ms: Date.now() - start };
|
|
2454
2483
|
}
|
|
@@ -2457,6 +2486,74 @@ var init_FileCapability = __esm({
|
|
|
2457
2486
|
}
|
|
2458
2487
|
});
|
|
2459
2488
|
|
|
2489
|
+
// packages/daemon/src/capabilities/MemoryCapability.ts
|
|
2490
|
+
var MemoryCapability;
|
|
2491
|
+
var init_MemoryCapability = __esm({
|
|
2492
|
+
"packages/daemon/src/capabilities/MemoryCapability.ts"() {
|
|
2493
|
+
"use strict";
|
|
2494
|
+
init_src();
|
|
2495
|
+
MemoryCapability = class {
|
|
2496
|
+
constructor(graph) {
|
|
2497
|
+
this.graph = graph;
|
|
2498
|
+
}
|
|
2499
|
+
name = "memory_write";
|
|
2500
|
+
description = "Persist a discovered fact to long-term memory so it survives across sessions.";
|
|
2501
|
+
toolDefinition = {
|
|
2502
|
+
name: "memory_write",
|
|
2503
|
+
description: 'Write an important fact to long-term memory. Call this whenever you discover something worth remembering: URLs (ngrok, live servers), file paths, port numbers, API endpoints, configuration values, decisions made, or task outcomes. Examples: memory_write({label:"ngrok_url", content:"https://abc.ngrok.io", type:"url"}) or memory_write({label:"project_port", content:"3000", type:"config"})',
|
|
2504
|
+
input_schema: {
|
|
2505
|
+
type: "object",
|
|
2506
|
+
properties: {
|
|
2507
|
+
label: { type: "string", description: 'Short name for this fact (e.g. "ngrok_url", "project_port")' },
|
|
2508
|
+
content: { type: "string", description: "The value to remember" },
|
|
2509
|
+
type: { type: "string", description: 'Category: "url", "path", "config", "note", "outcome"' }
|
|
2510
|
+
},
|
|
2511
|
+
required: ["label", "content"]
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
async execute(input, _cwd) {
|
|
2515
|
+
const label = String(input.label ?? "").trim();
|
|
2516
|
+
const content = String(input.content ?? "").trim();
|
|
2517
|
+
const type = String(input.type ?? "note").trim();
|
|
2518
|
+
const start = Date.now();
|
|
2519
|
+
if (!label || !content) {
|
|
2520
|
+
return { success: false, output: "label and content are required", duration_ms: 0 };
|
|
2521
|
+
}
|
|
2522
|
+
try {
|
|
2523
|
+
const nodeId = `memory:${label.toLowerCase().replace(/[^a-z0-9_]/g, "_")}`;
|
|
2524
|
+
const existing = this.graph.getNode(nodeId);
|
|
2525
|
+
if (existing) {
|
|
2526
|
+
this.graph.updateNode(nodeId, {
|
|
2527
|
+
label,
|
|
2528
|
+
metadata: { ...existing.metadata, content, type, updated_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2529
|
+
});
|
|
2530
|
+
} else {
|
|
2531
|
+
const node = createNode({
|
|
2532
|
+
id: nodeId,
|
|
2533
|
+
graph_id: "root",
|
|
2534
|
+
label,
|
|
2535
|
+
type: "context" /* CONTEXT */,
|
|
2536
|
+
metadata: { content, type, saved_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
2537
|
+
});
|
|
2538
|
+
this.graph.addNode(node);
|
|
2539
|
+
}
|
|
2540
|
+
return {
|
|
2541
|
+
success: true,
|
|
2542
|
+
output: `Remembered: "${label}" = ${content.slice(0, 120)}${content.length > 120 ? "\u2026" : ""}`,
|
|
2543
|
+
duration_ms: Date.now() - start
|
|
2544
|
+
};
|
|
2545
|
+
} catch (err) {
|
|
2546
|
+
return {
|
|
2547
|
+
success: false,
|
|
2548
|
+
output: `Memory write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2549
|
+
duration_ms: Date.now() - start
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2460
2557
|
// packages/daemon/src/capabilities/CodespaceBrowserCapability.ts
|
|
2461
2558
|
var CodespaceBrowserCapability_exports = {};
|
|
2462
2559
|
__export(CodespaceBrowserCapability_exports, {
|
|
@@ -2541,6 +2638,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
2541
2638
|
init_ScraperCapability();
|
|
2542
2639
|
init_ShellCapability();
|
|
2543
2640
|
init_FileCapability();
|
|
2641
|
+
init_MemoryCapability();
|
|
2544
2642
|
CapabilityRegistry = class {
|
|
2545
2643
|
capabilities = /* @__PURE__ */ new Map();
|
|
2546
2644
|
/**
|
|
@@ -2553,7 +2651,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
2553
2651
|
* task_type: browser_task). The main agent does NOT have direct access
|
|
2554
2652
|
* to browser_open without going through a subagent spawn.
|
|
2555
2653
|
*/
|
|
2556
|
-
constructor(codespaceManager) {
|
|
2654
|
+
constructor(codespaceManager, graph) {
|
|
2557
2655
|
this.register(new WebSearchCapability());
|
|
2558
2656
|
if (codespaceManager) {
|
|
2559
2657
|
try {
|
|
@@ -2568,6 +2666,9 @@ var init_CapabilityRegistry = __esm({
|
|
|
2568
2666
|
this.register(new ScraperCapability());
|
|
2569
2667
|
this.register(new ShellCapability());
|
|
2570
2668
|
this.register(new FileCapability());
|
|
2669
|
+
if (graph) {
|
|
2670
|
+
this.register(new MemoryCapability(graph));
|
|
2671
|
+
}
|
|
2571
2672
|
}
|
|
2572
2673
|
register(cap) {
|
|
2573
2674
|
this.capabilities.set(cap.name, cap);
|
|
@@ -2614,7 +2715,7 @@ var init_capabilities = __esm({
|
|
|
2614
2715
|
});
|
|
2615
2716
|
|
|
2616
2717
|
// packages/daemon/src/AgentExecutor.ts
|
|
2617
|
-
import { spawn as
|
|
2718
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
2618
2719
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "node:fs";
|
|
2619
2720
|
import { resolve as resolve3, dirname as dirname2, relative } from "node:path";
|
|
2620
2721
|
var SELF_MOD_PATTERN, AgentExecutor;
|
|
@@ -2633,7 +2734,7 @@ var init_AgentExecutor = __esm({
|
|
|
2633
2734
|
this.maxIterations = config.max_iterations ?? 20;
|
|
2634
2735
|
this.maxCommandMs = config.max_command_ms ?? 3e4;
|
|
2635
2736
|
this.agentRoot = config.agent_root;
|
|
2636
|
-
this.registry = new CapabilityRegistry();
|
|
2737
|
+
this.registry = new CapabilityRegistry(void 0, config.graph);
|
|
2637
2738
|
}
|
|
2638
2739
|
cwd;
|
|
2639
2740
|
maxIterations;
|
|
@@ -2658,23 +2759,39 @@ var init_AgentExecutor = __esm({
|
|
|
2658
2759
|
for (let i = 0; i < this.maxIterations; i++) {
|
|
2659
2760
|
this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
|
|
2660
2761
|
let response;
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2762
|
+
let llmFailed = false;
|
|
2763
|
+
{
|
|
2764
|
+
let llmRetry = 0;
|
|
2765
|
+
while (true) {
|
|
2766
|
+
try {
|
|
2767
|
+
response = await this.llm.completeWithTools(
|
|
2768
|
+
messages,
|
|
2769
|
+
this.registry.getToolDefinitions(),
|
|
2770
|
+
systemPrompt,
|
|
2771
|
+
// Only stream tokens on the final (non-tool) turn
|
|
2772
|
+
(token) => {
|
|
2773
|
+
this.onToken(token);
|
|
2774
|
+
finalOutput += token;
|
|
2775
|
+
}
|
|
2776
|
+
);
|
|
2777
|
+
break;
|
|
2778
|
+
} catch (err) {
|
|
2779
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2780
|
+
const isTimeout = /timeout|AbortError|aborted/i.test(msg);
|
|
2781
|
+
if (isTimeout && llmRetry < 2) {
|
|
2782
|
+
llmRetry++;
|
|
2783
|
+
this.onStep(`LLM timeout \u2014 retrying (${llmRetry}/2)\u2026`);
|
|
2784
|
+
await new Promise((r) => setTimeout(r, 2e3 * llmRetry));
|
|
2785
|
+
continue;
|
|
2786
|
+
}
|
|
2787
|
+
this.onStep(`LLM error: ${msg}`);
|
|
2788
|
+
finalOutput = `Error: ${msg}`;
|
|
2789
|
+
llmFailed = true;
|
|
2790
|
+
break;
|
|
2670
2791
|
}
|
|
2671
|
-
|
|
2672
|
-
} catch (err) {
|
|
2673
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2674
|
-
this.onStep(`LLM error: ${msg}`);
|
|
2675
|
-
finalOutput = `Error: ${msg}`;
|
|
2676
|
-
break;
|
|
2792
|
+
}
|
|
2677
2793
|
}
|
|
2794
|
+
if (llmFailed) break;
|
|
2678
2795
|
totalTokens += response.tokens_used;
|
|
2679
2796
|
modelName = response.model;
|
|
2680
2797
|
if (response.stop_reason === "end_turn" || !response.tool_calls?.length) {
|
|
@@ -2755,7 +2872,7 @@ var init_AgentExecutor = __esm({
|
|
|
2755
2872
|
shellExec(command, timeoutMs) {
|
|
2756
2873
|
return new Promise((resolve15) => {
|
|
2757
2874
|
const chunks = [];
|
|
2758
|
-
const proc =
|
|
2875
|
+
const proc = spawn3("bash", ["-c", command], {
|
|
2759
2876
|
cwd: this.cwd,
|
|
2760
2877
|
env: { ...process.env, TERM: "dumb" },
|
|
2761
2878
|
timeout: timeoutMs
|
|
@@ -2889,6 +3006,7 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
|
|
|
2889
3006
|
}
|
|
2890
3007
|
buildSystemPrompt(extra, task) {
|
|
2891
3008
|
const isSelfMod = !!(task && SELF_MOD_PATTERN.test(task));
|
|
3009
|
+
const hasMemory = !!this.config.graph;
|
|
2892
3010
|
const lines = [
|
|
2893
3011
|
`You are 0agent, an AI software engineer. You can execute shell commands and manage files.`,
|
|
2894
3012
|
`Working directory: ${this.cwd}`,
|
|
@@ -2899,12 +3017,24 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
|
|
|
2899
3017
|
` cmd > /tmp/0agent-server.log 2>&1 &`,
|
|
2900
3018
|
` Example: python3 -m http.server 3000 > /tmp/0agent-server.log 2>&1 &`,
|
|
2901
3019
|
` NEVER run background commands without redirecting output.`,
|
|
2902
|
-
`-
|
|
2903
|
-
`-
|
|
3020
|
+
`- To create a folder: use file_op with op="mkdir" and path="folder/name"`,
|
|
3021
|
+
`- To create a file (and its parent folders): use file_op with op="write" \u2014 parent dirs are created automatically`,
|
|
3022
|
+
`- For npm/node projects: check package.json first with file_op op="list"`,
|
|
3023
|
+
`- After writing files, verify with file_op op="read" if needed`,
|
|
2904
3024
|
`- After shell_exec, check output for errors and retry if needed`,
|
|
2905
3025
|
`- For research tasks: use web_search first, then scrape_url for full page content`,
|
|
2906
3026
|
`- Use relative paths from the working directory`,
|
|
2907
|
-
`- Be concise in your final response: state what was done and where to find it
|
|
3027
|
+
`- Be concise in your final response: state what was done and where to find it`,
|
|
3028
|
+
...hasMemory ? [
|
|
3029
|
+
``,
|
|
3030
|
+
`Memory (IMPORTANT):`,
|
|
3031
|
+
`- ALWAYS call memory_write after discovering important facts:`,
|
|
3032
|
+
` \xB7 Live URLs (ngrok, deployed apps, APIs): memory_write({label:"ngrok_url", content:"https://...", type:"url"})`,
|
|
3033
|
+
` \xB7 Server ports: memory_write({label:"dev_server_port", content:"3000", type:"config"})`,
|
|
3034
|
+
` \xB7 File paths of created projects: memory_write({label:"project_path", content:"/path/to/project", type:"path"})`,
|
|
3035
|
+
` \xB7 Task outcomes: memory_write({label:"last_task_result", content:"...", type:"outcome"})`,
|
|
3036
|
+
`- Call memory_write immediately when you find the value, not just at the end`
|
|
3037
|
+
] : []
|
|
2908
3038
|
];
|
|
2909
3039
|
if (isSelfMod && this.agentRoot) {
|
|
2910
3040
|
lines.push(
|
|
@@ -3034,7 +3164,7 @@ var init_ExecutionVerifier = __esm({
|
|
|
3034
3164
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6 } from "node:fs";
|
|
3035
3165
|
import { resolve as resolve5, dirname as dirname3 } from "node:path";
|
|
3036
3166
|
import { fileURLToPath } from "node:url";
|
|
3037
|
-
import { execSync as execSync4, spawn as
|
|
3167
|
+
import { execSync as execSync4, spawn as spawn4 } from "node:child_process";
|
|
3038
3168
|
function isRuntimeBug(error) {
|
|
3039
3169
|
if (TASK_FAILURE_PATTERNS.some((p) => p.test(error))) return false;
|
|
3040
3170
|
return RUNTIME_BUG_PATTERNS.some((p) => p.test(error));
|
|
@@ -3269,7 +3399,7 @@ Rules:
|
|
|
3269
3399
|
restartDaemon() {
|
|
3270
3400
|
const bundlePath = resolve5(this.projectRoot, "dist", "daemon.mjs");
|
|
3271
3401
|
if (existsSync6(bundlePath)) {
|
|
3272
|
-
const child =
|
|
3402
|
+
const child = spawn4(process.execPath, [bundlePath], {
|
|
3273
3403
|
detached: true,
|
|
3274
3404
|
stdio: "ignore",
|
|
3275
3405
|
env: process.env
|
|
@@ -4597,7 +4727,7 @@ var SessionManager = class {
|
|
|
4597
4727
|
if (activeLLM?.isConfigured) {
|
|
4598
4728
|
const executor = new AgentExecutor(
|
|
4599
4729
|
activeLLM,
|
|
4600
|
-
{ cwd: this.cwd, agent_root: this.agentRoot },
|
|
4730
|
+
{ cwd: this.cwd, agent_root: this.agentRoot, graph: this.graph },
|
|
4601
4731
|
// step callback → emit session.step events
|
|
4602
4732
|
(step) => this.addStep(sessionId, step),
|
|
4603
4733
|
// token callback → emit session.token events
|
|
@@ -6616,7 +6746,7 @@ git checkout <commit> graph/ # restore graph files
|
|
|
6616
6746
|
};
|
|
6617
6747
|
|
|
6618
6748
|
// packages/daemon/src/CodespaceManager.ts
|
|
6619
|
-
import { execSync as execSync5, spawn as
|
|
6749
|
+
import { execSync as execSync5, spawn as spawn5 } from "node:child_process";
|
|
6620
6750
|
var BROWSER_PORT_REMOTE = 3e3;
|
|
6621
6751
|
var BROWSER_PORT_LOCAL = 3001;
|
|
6622
6752
|
var DISPLAY_NAME = "0agent-browser";
|
|
@@ -6711,7 +6841,7 @@ var CodespaceManager = class {
|
|
|
6711
6841
|
async openTunnel(name) {
|
|
6712
6842
|
this.closeTunnel();
|
|
6713
6843
|
console.log(`[Codespace] Opening tunnel port ${BROWSER_PORT_REMOTE} \u2192 localhost:${BROWSER_PORT_LOCAL}...`);
|
|
6714
|
-
this.forwardProcess =
|
|
6844
|
+
this.forwardProcess = spawn5(
|
|
6715
6845
|
"gh",
|
|
6716
6846
|
["codespace", "ports", "forward", `${BROWSER_PORT_REMOTE}:${BROWSER_PORT_LOCAL}`, "--codespace", name],
|
|
6717
6847
|
{ stdio: ["ignore", "ignore", "ignore"] }
|
|
@@ -6871,7 +7001,14 @@ var ZeroAgentDaemon = class {
|
|
|
6871
7001
|
}).catch(() => {
|
|
6872
7002
|
});
|
|
6873
7003
|
}
|
|
6874
|
-
const
|
|
7004
|
+
const workspaceCfg = this.config["workspace"];
|
|
7005
|
+
const configuredWorkspace = workspaceCfg?.path;
|
|
7006
|
+
const cwd = process.env["ZEROAGENT_CWD"] ?? configuredWorkspace ?? process.cwd();
|
|
7007
|
+
if (configuredWorkspace) {
|
|
7008
|
+
const { mkdirSync: mks } = await import("node:fs");
|
|
7009
|
+
mks(configuredWorkspace, { recursive: true });
|
|
7010
|
+
console.log(`[0agent] Workspace: ${configuredWorkspace}`);
|
|
7011
|
+
}
|
|
6875
7012
|
const identityManager = new IdentityManager(this.graph);
|
|
6876
7013
|
const identity = await identityManager.init().catch(() => null);
|
|
6877
7014
|
if (identity) {
|