open-agents-ai 0.100.0 → 0.101.1
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 +250 -134
- package/package.json +1 -1
- package/prompts/agentic/system-large.md +16 -4
- package/prompts/agentic/system-medium.md +17 -1
- package/prompts/agentic/system-small.md +3 -1
package/dist/index.js
CHANGED
|
@@ -16927,8 +16927,13 @@ var init_agenticRunner = __esm({
|
|
|
16927
16927
|
_fileRegistry = /* @__PURE__ */ new Map();
|
|
16928
16928
|
// -- Memex Experience Archive (arXiv:2603.04257 inspired) --
|
|
16929
16929
|
_memexArchive = /* @__PURE__ */ new Map();
|
|
16930
|
-
// --
|
|
16930
|
+
// -- ENOENT tracker (prevents path-guessing spirals) --
|
|
16931
|
+
// Tracks both consecutive AND sliding-window ENOENT counts. The sliding
|
|
16932
|
+
// window catches oscillation patterns (success → ENOENT → success → ENOENT)
|
|
16933
|
+
// that the consecutive counter misses.
|
|
16931
16934
|
_consecutiveEnoent = 0;
|
|
16935
|
+
_recentEnoents = [];
|
|
16936
|
+
// sliding window of last 8 tool calls
|
|
16932
16937
|
// -- Session Checkpointing (Priority 5) --
|
|
16933
16938
|
_sessionId = `session-${Date.now()}`;
|
|
16934
16939
|
_workingDirectory = "";
|
|
@@ -17413,7 +17418,7 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
17413
17418
|
if (this.aborted)
|
|
17414
17419
|
return null;
|
|
17415
17420
|
toolCallCount++;
|
|
17416
|
-
const argsKey = Object.
|
|
17421
|
+
const argsKey = Object.entries(tc.arguments ?? {}).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 80) : JSON.stringify(v)}`).join(",");
|
|
17417
17422
|
toolCallLog.push({ name: tc.name, argsKey });
|
|
17418
17423
|
this.emit({
|
|
17419
17424
|
type: "tool_call",
|
|
@@ -17503,11 +17508,19 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17503
17508
|
});
|
|
17504
17509
|
}
|
|
17505
17510
|
const enoentTools = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
|
|
17506
|
-
|
|
17511
|
+
const isEnoent = !result.success && enoentTools.has(tc.name) && /ENOENT|no such file|does not exist|not found/i.test(result.error ?? result.output);
|
|
17512
|
+
if (enoentTools.has(tc.name)) {
|
|
17513
|
+
this._recentEnoents.push(isEnoent);
|
|
17514
|
+
if (this._recentEnoents.length > 8)
|
|
17515
|
+
this._recentEnoents.shift();
|
|
17516
|
+
}
|
|
17517
|
+
if (isEnoent) {
|
|
17507
17518
|
this._consecutiveEnoent++;
|
|
17508
|
-
|
|
17519
|
+
const windowEnoents = this._recentEnoents.filter(Boolean).length;
|
|
17520
|
+
if (this._consecutiveEnoent >= 2 || windowEnoents >= 4) {
|
|
17509
17521
|
const failedPath = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
|
|
17510
|
-
this.
|
|
17522
|
+
const triggerReason = this._consecutiveEnoent >= 2 ? `${this._consecutiveEnoent} consecutive file-not-found errors` : `${windowEnoents}/8 recent file operations failed`;
|
|
17523
|
+
this.pendingUserMessages.push(`[SYSTEM] ${triggerReason} (last: "${failedPath}"). You are repeating failed paths. CHANGE YOUR APPROACH:
|
|
17511
17524
|
1. Call list_directory(".") to see what exists at the project root
|
|
17512
17525
|
2. Entries in a directory listing are RELATIVE to that directory. If you listed "parent/" and saw "child", the full path is "parent/child" \u2014 NOT ".child" or just "child"
|
|
17513
17526
|
3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
|
|
@@ -17557,6 +17570,38 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17557
17570
|
}
|
|
17558
17571
|
if (completed)
|
|
17559
17572
|
break;
|
|
17573
|
+
const currentRepScore = this.detectRepetition(toolCallLog);
|
|
17574
|
+
if (currentRepScore > 0.6 && toolCallLog.length >= 6) {
|
|
17575
|
+
const { repetitionWindow } = this.contextLimits();
|
|
17576
|
+
const recentWindow = toolCallLog.slice(-repetitionWindow);
|
|
17577
|
+
const freqMap = /* @__PURE__ */ new Map();
|
|
17578
|
+
for (const tc of recentWindow) {
|
|
17579
|
+
const key = `${tc.name}(${tc.argsKey.slice(0, 60)})`;
|
|
17580
|
+
freqMap.set(key, (freqMap.get(key) ?? 0) + 1);
|
|
17581
|
+
}
|
|
17582
|
+
const topRepeated = [...freqMap.entries()].sort((a, b) => b[1] - a[1]).slice(0, 2).map(([k, v]) => `${k} (${v}x)`).join(", ");
|
|
17583
|
+
messages.push({
|
|
17584
|
+
role: "user",
|
|
17585
|
+
content: `[LOOP DETECTED] Your last ${repetitionWindow} tool calls are ${Math.round(currentRepScore * 100)}% repetitive.
|
|
17586
|
+
Repeated calls: ${topRepeated}
|
|
17587
|
+
|
|
17588
|
+
You are stuck. The same call will give the same result. CHANGE YOUR APPROACH:
|
|
17589
|
+
- If exploring: you already have this data. Use it to make a decision.
|
|
17590
|
+
- If looking for a file: use grep_search or find_files instead of listing directories.
|
|
17591
|
+
- If a path doesn't exist: use list_directory(".") to see what does exist.
|
|
17592
|
+
- If confused about the task: re-read the original task prompt above.
|
|
17593
|
+
|
|
17594
|
+
TASK REMINDER: ${this._taskState.goal}
|
|
17595
|
+
` + (this._taskState.completedSteps.length > 0 ? `Progress so far: ${this._taskState.completedSteps.slice(-3).join("; ")}
|
|
17596
|
+
` : "") + `
|
|
17597
|
+
Take a DIFFERENT action now.`
|
|
17598
|
+
});
|
|
17599
|
+
this.emit({
|
|
17600
|
+
type: "status",
|
|
17601
|
+
content: `Loop intervention: ${Math.round(currentRepScore * 100)}% repetitive (${topRepeated})`,
|
|
17602
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
17603
|
+
});
|
|
17604
|
+
}
|
|
17560
17605
|
} else {
|
|
17561
17606
|
const content = msg.content || "";
|
|
17562
17607
|
messages.push({ role: "assistant", content });
|
|
@@ -18106,13 +18151,16 @@ ${tail}`;
|
|
|
18106
18151
|
if (memexIndexStr)
|
|
18107
18152
|
enrichments.push(memexIndexStr);
|
|
18108
18153
|
const fullSummary = enrichments.join("\n\n");
|
|
18154
|
+
const goalReminder = this._taskState.goal ? `
|
|
18155
|
+
|
|
18156
|
+
**YOUR ACTIVE TASK:** ${this._taskState.goal}` : "";
|
|
18109
18157
|
const compactionMsg = {
|
|
18110
18158
|
role: "system",
|
|
18111
18159
|
content: `[Context compacted${strategyLabel} \u2014 summary of earlier work]
|
|
18112
18160
|
|
|
18113
18161
|
${fullSummary}
|
|
18114
18162
|
|
|
18115
|
-
[Continue from the recent context below. Do not repeat work already completed above.]`
|
|
18163
|
+
[Continue from the recent context below. Do not repeat work already completed above.]${goalReminder}`
|
|
18116
18164
|
};
|
|
18117
18165
|
this.persistCheckpoint(fullSummary);
|
|
18118
18166
|
let result = [...head, compactionMsg, ...recent];
|
|
@@ -29203,30 +29251,51 @@ function runWithSudo(cmd, password, timeoutMs = 12e4) {
|
|
|
29203
29251
|
timeout: timeoutMs,
|
|
29204
29252
|
env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
|
|
29205
29253
|
});
|
|
29206
|
-
return
|
|
29207
|
-
} catch {
|
|
29208
|
-
|
|
29254
|
+
return "ok";
|
|
29255
|
+
} catch (err) {
|
|
29256
|
+
const stderr = err?.stderr?.toString() ?? "";
|
|
29257
|
+
if (/incorrect password|authentication failure|Sorry, try again|not in the sudoers/i.test(stderr)) {
|
|
29258
|
+
return "auth_fail";
|
|
29259
|
+
}
|
|
29260
|
+
return "cmd_fail";
|
|
29209
29261
|
}
|
|
29210
29262
|
}
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29263
|
+
function validateSudoPassword(password) {
|
|
29264
|
+
return runWithSudo("true", password, 1e4) === "ok";
|
|
29265
|
+
}
|
|
29266
|
+
async function acquireSudoPassword(getSudoPassword, log, cachedPasswordRef) {
|
|
29267
|
+
if (cachedPasswordRef.value && validateSudoPassword(cachedPasswordRef.value)) {
|
|
29268
|
+
return cachedPasswordRef.value;
|
|
29269
|
+
}
|
|
29270
|
+
if (trySudoPasswordless("true", 1e4)) {
|
|
29271
|
+
return "";
|
|
29215
29272
|
}
|
|
29273
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
29274
|
+
if (attempt > 0)
|
|
29275
|
+
log("Authentication failed \u2014 please re-enter password.");
|
|
29276
|
+
const pw = await getSudoPassword();
|
|
29277
|
+
if (!pw)
|
|
29278
|
+
return null;
|
|
29279
|
+
if (validateSudoPassword(pw)) {
|
|
29280
|
+
cachedPasswordRef.value = pw;
|
|
29281
|
+
return pw;
|
|
29282
|
+
}
|
|
29283
|
+
}
|
|
29284
|
+
return null;
|
|
29285
|
+
}
|
|
29286
|
+
async function sudoInstall(cmd, getSudoPassword, log, cachedPasswordRef, timeoutMs = 12e4) {
|
|
29216
29287
|
if (trySudoPasswordless(cmd, timeoutMs))
|
|
29217
29288
|
return true;
|
|
29218
|
-
const pw = await getSudoPassword
|
|
29219
|
-
if (pw)
|
|
29220
|
-
|
|
29221
|
-
|
|
29222
|
-
|
|
29223
|
-
|
|
29224
|
-
|
|
29225
|
-
|
|
29226
|
-
|
|
29227
|
-
|
|
29228
|
-
return true;
|
|
29229
|
-
}
|
|
29289
|
+
const pw = await acquireSudoPassword(getSudoPassword, log, cachedPasswordRef);
|
|
29290
|
+
if (pw === null)
|
|
29291
|
+
return false;
|
|
29292
|
+
if (pw === "")
|
|
29293
|
+
return false;
|
|
29294
|
+
const result = runWithSudo(cmd, pw, timeoutMs);
|
|
29295
|
+
if (result === "ok")
|
|
29296
|
+
return true;
|
|
29297
|
+
if (result === "cmd_fail") {
|
|
29298
|
+
log(`Install command failed (not an auth issue) \u2014 package may not be available for this OS.`);
|
|
29230
29299
|
}
|
|
29231
29300
|
return false;
|
|
29232
29301
|
}
|
|
@@ -29235,108 +29304,68 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
29235
29304
|
});
|
|
29236
29305
|
const cachedPasswordRef = { value: null };
|
|
29237
29306
|
const getPassword = getSudoPassword ?? (() => Promise.resolve(null));
|
|
29238
|
-
|
|
29239
|
-
|
|
29240
|
-
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
dnf: { cmd: "dnf install -y tesseract", needsSudo: true },
|
|
29244
|
-
pacman: { cmd: "pacman -S --noconfirm tesseract", needsSudo: true },
|
|
29245
|
-
brew: { cmd: "brew install tesseract", needsSudo: false },
|
|
29246
|
-
choco: { cmd: "choco install -y tesseract", needsSudo: false }
|
|
29247
|
-
};
|
|
29248
|
-
const pkg = pkgMap[pm2];
|
|
29249
|
-
if (pkg.needsSudo) {
|
|
29250
|
-
log("Installing tesseract-ocr...");
|
|
29251
|
-
const ok = await sudoInstall(pkg.cmd, getPassword, log, cachedPasswordRef);
|
|
29252
|
-
if (ok && hasCmd("tesseract")) {
|
|
29253
|
-
log("tesseract-ocr installed successfully.");
|
|
29254
|
-
} else {
|
|
29255
|
-
log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
|
|
29256
|
-
}
|
|
29257
|
-
} else {
|
|
29258
|
-
log("Installing tesseract-ocr...");
|
|
29259
|
-
try {
|
|
29260
|
-
execSync25(pkg.cmd, { stdio: "pipe", timeout: 12e4 });
|
|
29261
|
-
if (hasCmd("tesseract")) {
|
|
29262
|
-
log("tesseract-ocr installed successfully.");
|
|
29263
|
-
} else {
|
|
29264
|
-
log("tesseract-ocr install completed but binary not found \u2014 OCR features will be limited.");
|
|
29265
|
-
}
|
|
29266
|
-
} catch {
|
|
29267
|
-
log("tesseract-ocr could not be installed \u2014 OCR features will be limited.");
|
|
29268
|
-
}
|
|
29269
|
-
}
|
|
29270
|
-
} else {
|
|
29271
|
-
log("No supported package manager detected \u2014 tesseract OCR unavailable.");
|
|
29272
|
-
}
|
|
29273
|
-
}
|
|
29274
|
-
const pdfDeps = [
|
|
29275
|
-
{
|
|
29276
|
-
binary: "pdftotext",
|
|
29277
|
-
label: "poppler-utils (pdftotext)",
|
|
29278
|
-
pkgMap: {
|
|
29279
|
-
apt: { cmd: "apt-get install -y poppler-utils", needsSudo: true },
|
|
29280
|
-
dnf: { cmd: "dnf install -y poppler-utils", needsSudo: true },
|
|
29281
|
-
pacman: { cmd: "pacman -S --noconfirm poppler", needsSudo: true },
|
|
29282
|
-
brew: { cmd: "brew install poppler", needsSudo: false },
|
|
29283
|
-
choco: { cmd: "choco install -y poppler", needsSudo: false }
|
|
29284
|
-
}
|
|
29285
|
-
},
|
|
29286
|
-
{
|
|
29287
|
-
binary: "gs",
|
|
29288
|
-
label: "ghostscript",
|
|
29289
|
-
pkgMap: {
|
|
29290
|
-
apt: { cmd: "apt-get install -y ghostscript", needsSudo: true },
|
|
29291
|
-
dnf: { cmd: "dnf install -y ghostscript", needsSudo: true },
|
|
29292
|
-
pacman: { cmd: "pacman -S --noconfirm ghostscript", needsSudo: true },
|
|
29293
|
-
brew: { cmd: "brew install ghostscript", needsSudo: false },
|
|
29294
|
-
choco: { cmd: "choco install -y ghostscript", needsSudo: false }
|
|
29295
|
-
}
|
|
29296
|
-
},
|
|
29297
|
-
{
|
|
29298
|
-
binary: "ocrmypdf",
|
|
29299
|
-
label: "ocrmypdf",
|
|
29300
|
-
pkgMap: {
|
|
29301
|
-
apt: { cmd: "apt-get install -y ocrmypdf", needsSudo: true },
|
|
29302
|
-
dnf: { cmd: "dnf install -y ocrmypdf", needsSudo: true },
|
|
29303
|
-
pacman: { cmd: "pacman -S --noconfirm ocrmypdf", needsSudo: true },
|
|
29304
|
-
brew: { cmd: "brew install ocrmypdf", needsSudo: false },
|
|
29305
|
-
choco: { cmd: "choco install -y ocrmypdf", needsSudo: false }
|
|
29306
|
-
}
|
|
29307
|
-
}
|
|
29307
|
+
const allDeps = [
|
|
29308
|
+
{ binary: "tesseract", label: "tesseract-ocr", pkgs: { apt: "tesseract-ocr", dnf: "tesseract", pacman: "tesseract", brew: "tesseract", choco: "tesseract" } },
|
|
29309
|
+
{ binary: "pdftotext", label: "poppler-utils", pkgs: { apt: "poppler-utils", dnf: "poppler-utils", pacman: "poppler", brew: "poppler", choco: "poppler" } },
|
|
29310
|
+
{ binary: "gs", label: "ghostscript", pkgs: { apt: "ghostscript", dnf: "ghostscript", pacman: "ghostscript", brew: "ghostscript", choco: "ghostscript" } },
|
|
29311
|
+
{ binary: "ocrmypdf", label: "ocrmypdf", pkgs: { apt: "ocrmypdf", dnf: "ocrmypdf", pacman: "ocrmypdf", brew: "ocrmypdf", choco: "ocrmypdf" } }
|
|
29308
29312
|
];
|
|
29309
29313
|
{
|
|
29310
29314
|
const pm2 = detectPkgManager();
|
|
29311
|
-
|
|
29312
|
-
|
|
29313
|
-
|
|
29314
|
-
|
|
29315
|
-
log(`No supported package manager
|
|
29316
|
-
|
|
29317
|
-
|
|
29318
|
-
|
|
29319
|
-
|
|
29320
|
-
|
|
29321
|
-
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
|
|
29325
|
-
|
|
29315
|
+
const missing = allDeps.filter((d) => !hasCmd(d.binary));
|
|
29316
|
+
if (missing.length === 0) {
|
|
29317
|
+
} else if (!pm2) {
|
|
29318
|
+
for (const d of missing)
|
|
29319
|
+
log(`No supported package manager \u2014 ${d.label} unavailable.`);
|
|
29320
|
+
} else {
|
|
29321
|
+
const pkgNames = missing.map((d) => d.pkgs[pm2]).filter(Boolean);
|
|
29322
|
+
if (pkgNames.length === 0) {
|
|
29323
|
+
for (const d of missing)
|
|
29324
|
+
log(`${d.label} not available for ${pm2}.`);
|
|
29325
|
+
} else {
|
|
29326
|
+
const labels = missing.map((d) => d.label).join(", ");
|
|
29327
|
+
log(`Installing ${labels}...`);
|
|
29328
|
+
const needsSudo = pm2 !== "brew" && pm2 !== "choco";
|
|
29329
|
+
let batchCmd;
|
|
29330
|
+
switch (pm2) {
|
|
29331
|
+
case "apt":
|
|
29332
|
+
batchCmd = `apt-get update -qq && apt-get install -y -qq ${pkgNames.join(" ")}`;
|
|
29333
|
+
break;
|
|
29334
|
+
case "dnf":
|
|
29335
|
+
batchCmd = `dnf install -y -q ${pkgNames.join(" ")}`;
|
|
29336
|
+
break;
|
|
29337
|
+
case "pacman":
|
|
29338
|
+
batchCmd = `pacman -S --noconfirm ${pkgNames.join(" ")}`;
|
|
29339
|
+
break;
|
|
29340
|
+
case "brew":
|
|
29341
|
+
batchCmd = `brew install ${pkgNames.join(" ")}`;
|
|
29342
|
+
break;
|
|
29343
|
+
case "choco":
|
|
29344
|
+
batchCmd = `choco install -y ${pkgNames.join(" ")}`;
|
|
29345
|
+
break;
|
|
29346
|
+
default:
|
|
29347
|
+
batchCmd = `echo "unsupported pm: ${pm2}" && exit 1`;
|
|
29348
|
+
break;
|
|
29349
|
+
}
|
|
29350
|
+
let ok = false;
|
|
29351
|
+
if (needsSudo) {
|
|
29352
|
+
ok = await sudoInstall(batchCmd, getPassword, log, cachedPasswordRef, 18e4);
|
|
29326
29353
|
} else {
|
|
29327
|
-
|
|
29354
|
+
try {
|
|
29355
|
+
execSync25(batchCmd, { stdio: "pipe", timeout: 18e4 });
|
|
29356
|
+
ok = true;
|
|
29357
|
+
} catch {
|
|
29358
|
+
ok = false;
|
|
29359
|
+
}
|
|
29328
29360
|
}
|
|
29329
|
-
|
|
29330
|
-
|
|
29331
|
-
|
|
29332
|
-
|
|
29333
|
-
|
|
29334
|
-
log(`${dep.label} installed successfully.`);
|
|
29361
|
+
for (const d of missing) {
|
|
29362
|
+
if (hasCmd(d.binary)) {
|
|
29363
|
+
log(`${d.label} installed.`);
|
|
29364
|
+
} else if (!ok) {
|
|
29365
|
+
log(`${d.label} could not be installed \u2014 features will be limited.`);
|
|
29335
29366
|
} else {
|
|
29336
|
-
log(`${
|
|
29367
|
+
log(`${d.label} install completed but binary not found.`);
|
|
29337
29368
|
}
|
|
29338
|
-
} catch {
|
|
29339
|
-
log(`${dep.label} could not be installed \u2014 PDF OCR features will be limited.`);
|
|
29340
29369
|
}
|
|
29341
29370
|
}
|
|
29342
29371
|
}
|
|
@@ -29641,17 +29670,41 @@ async function handleSlashCommand(input, ctx) {
|
|
|
29641
29670
|
}
|
|
29642
29671
|
return "handled";
|
|
29643
29672
|
case "config":
|
|
29644
|
-
case "cfg":
|
|
29673
|
+
case "cfg": {
|
|
29674
|
+
const resolved = loadProjectSettings(ctx.repoRoot);
|
|
29675
|
+
const globalS = loadGlobalSettings();
|
|
29676
|
+
const merged = { ...globalS, ...resolved };
|
|
29645
29677
|
renderConfig({
|
|
29678
|
+
// -- Core inference --
|
|
29646
29679
|
model: ctx.config.model,
|
|
29647
29680
|
backendType: ctx.config.backendType,
|
|
29648
29681
|
backendUrl: ctx.config.backendUrl,
|
|
29649
|
-
|
|
29682
|
+
apiKey: ctx.config.apiKey ? "[set]" : "[not set]",
|
|
29650
29683
|
maxRetries: String(ctx.config.maxRetries),
|
|
29684
|
+
timeoutMs: String(ctx.config.timeoutMs),
|
|
29685
|
+
dbPath: ctx.config.dbPath,
|
|
29686
|
+
// -- Behaviour --
|
|
29687
|
+
dryRun: String(ctx.config.dryRun),
|
|
29651
29688
|
verbose: String(ctx.config.verbose),
|
|
29652
|
-
|
|
29689
|
+
stream: String(merged.stream ?? false),
|
|
29690
|
+
bruteforce: String(merged.bruteforce ?? false),
|
|
29691
|
+
deepContext: String(merged.deepContext ?? false),
|
|
29692
|
+
// -- Presentation --
|
|
29693
|
+
emojis: String(ctx.getEmojis?.() ?? merged.emojis ?? true),
|
|
29694
|
+
colors: String(ctx.getColors?.() ?? merged.colors ?? true),
|
|
29695
|
+
style: String(ctx.getStyle?.() ?? merged.style ?? "balanced"),
|
|
29696
|
+
// -- Voice --
|
|
29697
|
+
voice: String(merged.voice ?? false),
|
|
29698
|
+
voiceModel: String(merged.voiceModel ?? "glados"),
|
|
29699
|
+
// -- Autonomy --
|
|
29700
|
+
commandsMode: String(ctx.getCommandsMode?.() ?? merged.commandsMode ?? "manual"),
|
|
29701
|
+
updateMode: String(merged.updateMode ?? "auto"),
|
|
29702
|
+
// -- Integrations --
|
|
29703
|
+
telegramKey: merged.telegramKey ? "[set]" : "[not set]",
|
|
29704
|
+
telegramAdmin: String(merged.telegramAdmin ?? "[not set]")
|
|
29653
29705
|
});
|
|
29654
29706
|
return "handled";
|
|
29707
|
+
}
|
|
29655
29708
|
case "cost":
|
|
29656
29709
|
case "costs":
|
|
29657
29710
|
if (ctx.costTracker) {
|
|
@@ -44076,6 +44129,12 @@ function coerceForSettings(key, value) {
|
|
|
44076
44129
|
if (BOOL_KEYS.has(key)) {
|
|
44077
44130
|
return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
|
|
44078
44131
|
}
|
|
44132
|
+
if (ENUM_KEYS[key]) {
|
|
44133
|
+
const allowed = ENUM_KEYS[key];
|
|
44134
|
+
if (!allowed.includes(value)) {
|
|
44135
|
+
throw new Error(`Invalid value "${value}" for ${key}. Allowed: ${allowed.join(", ")}`);
|
|
44136
|
+
}
|
|
44137
|
+
}
|
|
44079
44138
|
return value;
|
|
44080
44139
|
}
|
|
44081
44140
|
async function configCommand(opts, config) {
|
|
@@ -44090,16 +44149,34 @@ async function configCommand(opts, config) {
|
|
|
44090
44149
|
function handleShow(opts, config) {
|
|
44091
44150
|
const repoRoot = resolve30(opts.repoPath ?? cwd3());
|
|
44092
44151
|
printHeader("Configuration");
|
|
44093
|
-
|
|
44152
|
+
const resolved = resolveSettings(repoRoot);
|
|
44153
|
+
printSection("Core Inference");
|
|
44094
44154
|
printKeyValue("backendUrl", config.backendUrl, 2);
|
|
44095
44155
|
printKeyValue("backendType", config.backendType, 2);
|
|
44096
44156
|
printKeyValue("model", config.model, 2);
|
|
44097
44157
|
printKeyValue("apiKey", config.apiKey ? "[set]" : "[not set]", 2);
|
|
44098
44158
|
printKeyValue("maxRetries", String(config.maxRetries), 2);
|
|
44099
44159
|
printKeyValue("timeoutMs", String(config.timeoutMs), 2);
|
|
44160
|
+
printKeyValue("dbPath", config.dbPath, 2);
|
|
44161
|
+
printSection("Behaviour");
|
|
44100
44162
|
printKeyValue("dryRun", String(config.dryRun), 2);
|
|
44101
44163
|
printKeyValue("verbose", String(config.verbose), 2);
|
|
44102
|
-
printKeyValue("
|
|
44164
|
+
printKeyValue("stream", String(resolved.stream ?? false), 2);
|
|
44165
|
+
printKeyValue("bruteforce", String(resolved.bruteforce ?? false), 2);
|
|
44166
|
+
printKeyValue("deepContext", String(resolved.deepContext ?? false), 2);
|
|
44167
|
+
printSection("Presentation");
|
|
44168
|
+
printKeyValue("emojis", String(resolved.emojis ?? true), 2);
|
|
44169
|
+
printKeyValue("colors", String(resolved.colors ?? true), 2);
|
|
44170
|
+
printKeyValue("style", String(resolved.style ?? "balanced"), 2);
|
|
44171
|
+
printSection("Voice & Audio");
|
|
44172
|
+
printKeyValue("voice", String(resolved.voice ?? false), 2);
|
|
44173
|
+
printKeyValue("voiceModel", String(resolved.voiceModel ?? "glados"), 2);
|
|
44174
|
+
printSection("Agent Autonomy");
|
|
44175
|
+
printKeyValue("commandsMode", String(resolved.commandsMode ?? "manual"), 2);
|
|
44176
|
+
printKeyValue("updateMode", String(resolved.updateMode ?? "auto"), 2);
|
|
44177
|
+
printSection("Integrations");
|
|
44178
|
+
printKeyValue("telegramKey", resolved.telegramKey ? redactIfSensitive("telegramKey", resolved.telegramKey) : "[not set]", 2);
|
|
44179
|
+
printKeyValue("telegramAdmin", String(resolved.telegramAdmin ?? "[not set]"), 2);
|
|
44103
44180
|
const projectSettings = loadProjectSettings(repoRoot);
|
|
44104
44181
|
const projectKeys = Object.entries(projectSettings).filter(([, v]) => v !== void 0);
|
|
44105
44182
|
if (projectKeys.length > 0) {
|
|
@@ -44166,10 +44243,28 @@ function handleSet(opts, _config) {
|
|
|
44166
44243
|
process.exit(1);
|
|
44167
44244
|
}
|
|
44168
44245
|
} else {
|
|
44246
|
+
const AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
44247
|
+
"backendUrl",
|
|
44248
|
+
"model",
|
|
44249
|
+
"backendType",
|
|
44250
|
+
"apiKey",
|
|
44251
|
+
"maxRetries",
|
|
44252
|
+
"timeoutMs",
|
|
44253
|
+
"dryRun",
|
|
44254
|
+
"verbose",
|
|
44255
|
+
"dbPath"
|
|
44256
|
+
]);
|
|
44169
44257
|
try {
|
|
44170
|
-
|
|
44171
|
-
|
|
44172
|
-
|
|
44258
|
+
const coerced = coerceForSettings(key, value);
|
|
44259
|
+
if (AGENT_CONFIG_KEYS.has(key)) {
|
|
44260
|
+
setConfigValue(key, value);
|
|
44261
|
+
printSuccess(`Config updated: ${key} = ${redactIfSensitive(key, value)}`);
|
|
44262
|
+
printInfo(`Saved to ~/.open-agents/config.json`);
|
|
44263
|
+
} else {
|
|
44264
|
+
saveGlobalSettings({ [key]: coerced });
|
|
44265
|
+
printSuccess(`Setting updated: ${key} = ${redactIfSensitive(key, value)}`);
|
|
44266
|
+
printInfo(`Saved to ~/.open-agents/settings.json`);
|
|
44267
|
+
}
|
|
44173
44268
|
printInfo("Tip: Use --local to set project-specific overrides.");
|
|
44174
44269
|
} catch (err) {
|
|
44175
44270
|
printError(`Failed to save config: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -44187,7 +44282,7 @@ function handleKeys() {
|
|
|
44187
44282
|
printInfo(" oa config set model qwen3.5:122b # global default");
|
|
44188
44283
|
printInfo(" oa config set model qwen3.5:122b --local # this project only");
|
|
44189
44284
|
}
|
|
44190
|
-
var CONFIG_KEYS, SENSITIVE_KEYS, INT_KEYS, BOOL_KEYS;
|
|
44285
|
+
var CONFIG_KEYS, SENSITIVE_KEYS, INT_KEYS, BOOL_KEYS, ENUM_KEYS;
|
|
44191
44286
|
var init_config3 = __esm({
|
|
44192
44287
|
"packages/cli/dist/commands/config.js"() {
|
|
44193
44288
|
"use strict";
|
|
@@ -44195,23 +44290,44 @@ var init_config3 = __esm({
|
|
|
44195
44290
|
init_oa_directory();
|
|
44196
44291
|
init_output();
|
|
44197
44292
|
CONFIG_KEYS = {
|
|
44293
|
+
// -- Core inference --
|
|
44198
44294
|
backendUrl: "Backend base URL (Ollama or OpenAI-compatible)",
|
|
44199
44295
|
backendType: "Backend type: ollama, vllm, fake",
|
|
44200
44296
|
model: "Model name to use",
|
|
44201
44297
|
apiKey: "Bearer token for authenticated deployments",
|
|
44202
44298
|
maxRetries: "Maximum HTTP retries (integer)",
|
|
44203
44299
|
timeoutMs: "Per-request timeout in milliseconds (integer)",
|
|
44300
|
+
dbPath: "Path to SQLite memory database",
|
|
44301
|
+
// -- Behaviour flags --
|
|
44204
44302
|
dryRun: "Dry-run mode - patches not written (true/false)",
|
|
44205
44303
|
verbose: "Verbose output (true/false)",
|
|
44206
|
-
|
|
44304
|
+
stream: "Enable real-time token streaming with pastel syntax highlighting (true/false)",
|
|
44305
|
+
bruteforce: "Brute-force mode: auto re-engage agent when turn limit hit (true/false)",
|
|
44306
|
+
deepContext: "Deep context mode - relaxes compaction for complex problem-solving (true/false)",
|
|
44307
|
+
// -- TUI presentation --
|
|
44308
|
+
emojis: "Show emoji icons next to tool calls and status messages (true/false)",
|
|
44309
|
+
colors: "Enable pastel ANSI colors in the TUI (true/false)",
|
|
44310
|
+
style: "Personality preset: concise, balanced, verbose, pedagogical",
|
|
44311
|
+
// -- Voice & audio --
|
|
44207
44312
|
voice: "Enable TTS voice feedback (true/false)",
|
|
44208
44313
|
voiceModel: "TTS voice model: glados, overwatch",
|
|
44209
|
-
|
|
44210
|
-
|
|
44314
|
+
// -- Agent autonomy --
|
|
44315
|
+
commandsMode: "Agent slash-command access: auto (agent can invoke) or manual (user-only)",
|
|
44316
|
+
updateMode: "Update behaviour: auto (after task completion) or manual (/update only)",
|
|
44317
|
+
// -- Integrations --
|
|
44318
|
+
telegramKey: "Telegram bot API token for /telegram bridge",
|
|
44319
|
+
telegramAdmin: "Telegram admin user ID \u2014 only this user can interact with the bot"
|
|
44211
44320
|
};
|
|
44212
|
-
SENSITIVE_KEYS = /* @__PURE__ */ new Set(["apiKey", "api_key", "secret", "password", "token"]);
|
|
44321
|
+
SENSITIVE_KEYS = /* @__PURE__ */ new Set(["apiKey", "api_key", "secret", "password", "token", "telegramKey"]);
|
|
44213
44322
|
INT_KEYS = /* @__PURE__ */ new Set(["maxRetries", "timeoutMs"]);
|
|
44214
|
-
BOOL_KEYS = /* @__PURE__ */ new Set(["dryRun", "verbose", "voice", "stream", "bruteforce"]);
|
|
44323
|
+
BOOL_KEYS = /* @__PURE__ */ new Set(["dryRun", "verbose", "voice", "stream", "bruteforce", "deepContext", "emojis", "colors"]);
|
|
44324
|
+
ENUM_KEYS = {
|
|
44325
|
+
commandsMode: ["auto", "manual"],
|
|
44326
|
+
updateMode: ["auto", "manual"],
|
|
44327
|
+
style: ["concise", "balanced", "verbose", "pedagogical"],
|
|
44328
|
+
backendType: ["ollama", "vllm", "fake"],
|
|
44329
|
+
voiceModel: ["glados", "overwatch"]
|
|
44330
|
+
};
|
|
44215
44331
|
}
|
|
44216
44332
|
});
|
|
44217
44333
|
|
package/package.json
CHANGED
|
@@ -135,8 +135,21 @@ When you discover image files (png, jpg, gif, svg, webp, bmp) during codebase ex
|
|
|
135
135
|
|
|
136
136
|
## Self-Awareness & Introspection
|
|
137
137
|
|
|
138
|
-
You are Open Agent (open-agents-ai), an autonomous AI coding agent.
|
|
139
|
-
|
|
138
|
+
You are **Open Agent** (open-agents-ai), an autonomous AI coding agent running on local hardware via Ollama or vLLM with open-weight models. No cloud APIs — everything runs on the user's machine.
|
|
139
|
+
|
|
140
|
+
**Core capabilities** (use explore_tools() to unlock more):
|
|
141
|
+
- Code: read, write, edit, search, patch files across any language
|
|
142
|
+
- Shell: run any command — tests, builds, git, npm, docker, etc.
|
|
143
|
+
- Web: search documentation and fetch web pages
|
|
144
|
+
- Memory: persistent cross-session knowledge (memory_read/memory_write)
|
|
145
|
+
- Skills: 250+ behavioral skills (skill_list), build new ones (skill_build)
|
|
146
|
+
- P2P: connect to other agents via nexus (libp2p + NATS mesh)
|
|
147
|
+
- Background tasks: run long commands in background, check status later
|
|
148
|
+
- Desktop/Vision: screenshot, click UI, OCR (via explore_tools)
|
|
149
|
+
- Scheduling: cron jobs, reminders, agenda (via explore_tools)
|
|
150
|
+
- Custom tools: create reusable tools from repeated workflows
|
|
151
|
+
|
|
152
|
+
**Introspection tools** (use to answer questions about yourself):
|
|
140
153
|
- **Tool discovery**: Use explore_tools() to see all available tools and unlock new ones
|
|
141
154
|
- **Skill discovery**: Use skill_list() to discover behavioral skills with trigger patterns
|
|
142
155
|
- **Memory**: Use memory_read/memory_write/memory_search to access persistent cross-session knowledge
|
|
@@ -144,9 +157,8 @@ You are Open Agent (open-agents-ai), an autonomous AI coding agent. You can disc
|
|
|
144
157
|
- **Metrics**: Use slash_command('stats') to see session performance (when /commands auto)
|
|
145
158
|
- **Capabilities**: Use slash_command('score') to see hardware inference capabilities (when /commands auto)
|
|
146
159
|
- **Project map**: Use codebase_map to understand the project structure
|
|
147
|
-
- **Self-description**: You run on local hardware via Ollama/vLLM with open-weight models, no cloud APIs
|
|
148
160
|
|
|
149
|
-
When asked
|
|
161
|
+
When asked "how do you work?" or "what can you do?", answer from the capability list above and use introspection tools for specifics. Do NOT hallucinate capabilities — use tools to discover concrete information.
|
|
150
162
|
|
|
151
163
|
## Project Awareness
|
|
152
164
|
|
|
@@ -49,4 +49,20 @@ NEVER say "I can't do that". ALWAYS attempt the task using your tools. If a tool
|
|
|
49
49
|
- Directory listing entries are RELATIVE to the listed directory. If you list "parent/" and see "child", the full path is "parent/child" — NOT ".child" or just "child"
|
|
50
50
|
- If an entry is a directory (d), use list_directory on it — NOT file_read
|
|
51
51
|
- Prefer list_directory over shell ls — it shows full paths ready for your next tool call
|
|
52
|
-
|
|
52
|
+
## Self-Awareness
|
|
53
|
+
|
|
54
|
+
You are **Open Agent** (open-agents-ai), an autonomous AI coding agent running on local hardware via Ollama or vLLM with open-weight models. No cloud APIs — everything runs on the user's machine.
|
|
55
|
+
|
|
56
|
+
**Core capabilities** (use explore_tools() to unlock more):
|
|
57
|
+
- Code: read, write, edit, search, patch files across any language
|
|
58
|
+
- Shell: run any command — tests, builds, git, npm, docker, etc.
|
|
59
|
+
- Web: search documentation and fetch web pages
|
|
60
|
+
- Memory: persistent cross-session knowledge (memory_read/memory_write)
|
|
61
|
+
- Skills: 250+ behavioral skills (skill_list), build new ones (skill_build)
|
|
62
|
+
- P2P: connect to other agents via nexus (libp2p + NATS mesh)
|
|
63
|
+
- Background tasks: run long commands in background, check status later
|
|
64
|
+
- Desktop/Vision: screenshot, click UI, OCR (via explore_tools)
|
|
65
|
+
- Scheduling: cron jobs, reminders, agenda (via explore_tools)
|
|
66
|
+
- Custom tools: create reusable tools from repeated workflows
|
|
67
|
+
|
|
68
|
+
When asked "how do you work?" or "what can you do?", answer from this list and use explore_tools() or skill_list() to provide specifics. Do NOT hallucinate capabilities — use tools to discover concrete information.
|
|
@@ -19,4 +19,6 @@ Rules:
|
|
|
19
19
|
- If ENOENT, list_directory on project root. Don't guess paths.
|
|
20
20
|
- Directory entries are RELATIVE. If you list "parent/" and see "child", the path is "parent/child" — NOT ".child".
|
|
21
21
|
- Use list_directory for directories, NOT file_read. Prefer list_directory over shell ls.
|
|
22
|
-
- You are Open Agent
|
|
22
|
+
- You are **Open Agent** (open-agents-ai) — an AI coding agent running locally via Ollama/vLLM. No cloud APIs.
|
|
23
|
+
- Core: code editing, shell commands, web search, memory, 250+ skills (skill_list), P2P networking (nexus), background tasks.
|
|
24
|
+
- When asked "what can you do?", use explore_tools() and skill_list() to discover and report your actual capabilities. Do NOT hallucinate.
|