@livx.cc/agentx 0.95.1 → 0.95.2
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/{Agent-1DRfsYaK.d.ts → Agent-DRe91tAy.d.ts} +5 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +578 -421
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +387 -232
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1059,6 +1059,172 @@ var init_tools = __esm({
|
|
|
1059
1059
|
}
|
|
1060
1060
|
});
|
|
1061
1061
|
|
|
1062
|
+
// src/worktree.ts
|
|
1063
|
+
var worktree_exports = {};
|
|
1064
|
+
__export(worktree_exports, {
|
|
1065
|
+
cleanupWorktree: () => cleanupWorktree,
|
|
1066
|
+
enterWorktree: () => enterWorktree,
|
|
1067
|
+
exitWorktree: () => exitWorktree,
|
|
1068
|
+
findGitRoot: () => findGitRoot,
|
|
1069
|
+
getOrCreateWorktree: () => getOrCreateWorktree,
|
|
1070
|
+
getWorktreeSession: () => getWorktreeSession,
|
|
1071
|
+
validateWorktreeSlug: () => validateWorktreeSlug,
|
|
1072
|
+
worktreeBranchName: () => worktreeBranchName
|
|
1073
|
+
});
|
|
1074
|
+
import { spawnSync } from "child_process";
|
|
1075
|
+
import { symlinkSync, mkdirSync, statSync as statSync2, readFileSync, rmSync } from "fs";
|
|
1076
|
+
import { join as join2 } from "path";
|
|
1077
|
+
function validateWorktreeSlug(slug2) {
|
|
1078
|
+
if (!slug2) throw new Error("worktree slug must not be empty");
|
|
1079
|
+
if (slug2.length > MAX_SLUG_LENGTH) throw new Error(`worktree slug must be \u2264${MAX_SLUG_LENGTH} chars (got ${slug2.length})`);
|
|
1080
|
+
for (const seg of slug2.split("/")) {
|
|
1081
|
+
if (seg === "." || seg === "..") throw new Error(`worktree slug must not contain "." or ".." segments`);
|
|
1082
|
+
if (!VALID_SEGMENT.test(seg)) throw new Error(`worktree slug segment "${seg}" contains invalid characters (allowed: a-z A-Z 0-9 . _ -)`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
function flattenSlug(slug2) {
|
|
1086
|
+
return slug2.replaceAll("/", "+");
|
|
1087
|
+
}
|
|
1088
|
+
function worktreeBranchName(slug2) {
|
|
1089
|
+
return `worktree-${flattenSlug(slug2)}`;
|
|
1090
|
+
}
|
|
1091
|
+
function worktreesDir(repoRoot) {
|
|
1092
|
+
return join2(repoRoot, ".claude", "worktrees");
|
|
1093
|
+
}
|
|
1094
|
+
function worktreePathFor(repoRoot, slug2) {
|
|
1095
|
+
return join2(worktreesDir(repoRoot), flattenSlug(slug2));
|
|
1096
|
+
}
|
|
1097
|
+
function git(args, cwd) {
|
|
1098
|
+
const r = spawnSync("git", args, {
|
|
1099
|
+
cwd,
|
|
1100
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1101
|
+
env: { ...process.env, ...GIT_NO_PROMPT_ENV }
|
|
1102
|
+
});
|
|
1103
|
+
return { ok: r.status === 0, stdout: (r.stdout ?? "").toString().trim(), stderr: (r.stderr ?? "").toString().trim() };
|
|
1104
|
+
}
|
|
1105
|
+
function findGitRoot(from) {
|
|
1106
|
+
const r = git(["rev-parse", "--show-toplevel"], from);
|
|
1107
|
+
return r.ok ? r.stdout : null;
|
|
1108
|
+
}
|
|
1109
|
+
function readWorktreeHead(worktreePath) {
|
|
1110
|
+
try {
|
|
1111
|
+
const gitFile = readFileSync(join2(worktreePath, ".git"), "utf-8").trim();
|
|
1112
|
+
const m = gitFile.match(/^gitdir:\s*(.+)$/);
|
|
1113
|
+
if (!m) return null;
|
|
1114
|
+
const headPath = join2(m[1], "HEAD");
|
|
1115
|
+
const head = readFileSync(headPath, "utf-8").trim();
|
|
1116
|
+
const refMatch = head.match(/^ref:\s*(.+)$/);
|
|
1117
|
+
if (!refMatch) return head.length === 40 ? head : null;
|
|
1118
|
+
const refPath = join2(m[1], "..", refMatch[1]);
|
|
1119
|
+
try {
|
|
1120
|
+
return readFileSync(refPath, "utf-8").trim();
|
|
1121
|
+
} catch {
|
|
1122
|
+
}
|
|
1123
|
+
const r = git(["rev-parse", "HEAD"], worktreePath);
|
|
1124
|
+
return r.ok ? r.stdout : null;
|
|
1125
|
+
} catch {
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function getOrCreateWorktree(repoRoot, slug2) {
|
|
1130
|
+
validateWorktreeSlug(slug2);
|
|
1131
|
+
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
1132
|
+
const branch = worktreeBranchName(slug2);
|
|
1133
|
+
const existingHead = readWorktreeHead(worktreePath);
|
|
1134
|
+
if (existingHead) return { worktreePath, branch, headCommit: existingHead, existed: true };
|
|
1135
|
+
mkdirSync(worktreesDir(repoRoot), { recursive: true });
|
|
1136
|
+
const defaultBranch = git(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoRoot);
|
|
1137
|
+
let baseBranch = defaultBranch.ok ? defaultBranch.stdout.replace(/^origin\//, "") : "HEAD";
|
|
1138
|
+
const originRef = `origin/${baseBranch}`;
|
|
1139
|
+
const hasOrigin = git(["rev-parse", "--verify", originRef], repoRoot);
|
|
1140
|
+
if (!hasOrigin.ok) {
|
|
1141
|
+
const fetched = git(["fetch", "origin", baseBranch], repoRoot);
|
|
1142
|
+
if (!fetched.ok) baseBranch = "HEAD";
|
|
1143
|
+
}
|
|
1144
|
+
const base = baseBranch === "HEAD" ? "HEAD" : originRef;
|
|
1145
|
+
const r = git(["worktree", "add", "-B", branch, worktreePath, base], repoRoot);
|
|
1146
|
+
if (!r.ok) throw new Error(`failed to create worktree: ${r.stderr}`);
|
|
1147
|
+
const headSha = git(["rev-parse", "HEAD"], worktreePath);
|
|
1148
|
+
symlinkDirs(repoRoot, worktreePath, ["node_modules", ".bun"]);
|
|
1149
|
+
return { worktreePath, branch, headCommit: headSha.stdout, existed: false, baseBranch };
|
|
1150
|
+
}
|
|
1151
|
+
function symlinkDirs(repoRoot, worktreePath, dirs) {
|
|
1152
|
+
for (const dir of dirs) {
|
|
1153
|
+
const src = join2(repoRoot, dir);
|
|
1154
|
+
const dst = join2(worktreePath, dir);
|
|
1155
|
+
try {
|
|
1156
|
+
statSync2(src);
|
|
1157
|
+
} catch {
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
try {
|
|
1161
|
+
symlinkSync(src, dst, "dir");
|
|
1162
|
+
} catch (e) {
|
|
1163
|
+
if (e?.code !== "EEXIST") console.error(`[worktree] symlink ${dir}: ${e?.message ?? e}`);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function cleanupWorktree(repoRoot, slug2, opts) {
|
|
1168
|
+
validateWorktreeSlug(slug2);
|
|
1169
|
+
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
1170
|
+
const branch = worktreeBranchName(slug2);
|
|
1171
|
+
try {
|
|
1172
|
+
statSync2(worktreePath);
|
|
1173
|
+
} catch {
|
|
1174
|
+
return { removed: false, reason: "not found" };
|
|
1175
|
+
}
|
|
1176
|
+
if (!opts?.force) {
|
|
1177
|
+
const status = git(["status", "--porcelain", "-uno"], worktreePath);
|
|
1178
|
+
if (status.ok && status.stdout) return { removed: false, reason: "uncommitted changes" };
|
|
1179
|
+
}
|
|
1180
|
+
for (const dir of ["node_modules", ".bun"]) {
|
|
1181
|
+
try {
|
|
1182
|
+
rmSync(join2(worktreePath, dir));
|
|
1183
|
+
} catch {
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
const rm = git(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
1187
|
+
if (!rm.ok) return { removed: false, reason: rm.stderr };
|
|
1188
|
+
git(["branch", "-D", branch], repoRoot);
|
|
1189
|
+
return { removed: true };
|
|
1190
|
+
}
|
|
1191
|
+
function getWorktreeSession() {
|
|
1192
|
+
return _session;
|
|
1193
|
+
}
|
|
1194
|
+
function enterWorktree(slug2, cwd) {
|
|
1195
|
+
const repoRoot = findGitRoot(cwd);
|
|
1196
|
+
if (!repoRoot) throw new Error("--worktree requires a git repository");
|
|
1197
|
+
const info = getOrCreateWorktree(repoRoot, slug2);
|
|
1198
|
+
process.chdir(info.worktreePath);
|
|
1199
|
+
_session = {
|
|
1200
|
+
originalCwd: cwd,
|
|
1201
|
+
worktreePath: info.worktreePath,
|
|
1202
|
+
slug: slug2,
|
|
1203
|
+
branch: info.branch,
|
|
1204
|
+
headCommit: info.headCommit
|
|
1205
|
+
};
|
|
1206
|
+
return _session;
|
|
1207
|
+
}
|
|
1208
|
+
function exitWorktree() {
|
|
1209
|
+
if (!_session) return null;
|
|
1210
|
+
const repoRoot = findGitRoot(_session.originalCwd);
|
|
1211
|
+
if (!repoRoot) return null;
|
|
1212
|
+
process.chdir(_session.originalCwd);
|
|
1213
|
+
const result = cleanupWorktree(repoRoot, _session.slug);
|
|
1214
|
+
_session = null;
|
|
1215
|
+
return result;
|
|
1216
|
+
}
|
|
1217
|
+
var VALID_SEGMENT, MAX_SLUG_LENGTH, GIT_NO_PROMPT_ENV, _session;
|
|
1218
|
+
var init_worktree = __esm({
|
|
1219
|
+
"src/worktree.ts"() {
|
|
1220
|
+
"use strict";
|
|
1221
|
+
VALID_SEGMENT = /^[a-zA-Z0-9._-]+$/;
|
|
1222
|
+
MAX_SLUG_LENGTH = 64;
|
|
1223
|
+
GIT_NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "" };
|
|
1224
|
+
_session = null;
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1062
1228
|
// src/NodeDiskFilesystem.ts
|
|
1063
1229
|
var NodeDiskFilesystem_exports = {};
|
|
1064
1230
|
__export(NodeDiskFilesystem_exports, {
|
|
@@ -1420,172 +1586,6 @@ var init_JailedFilesystem = __esm({
|
|
|
1420
1586
|
}
|
|
1421
1587
|
});
|
|
1422
1588
|
|
|
1423
|
-
// src/worktree.ts
|
|
1424
|
-
var worktree_exports = {};
|
|
1425
|
-
__export(worktree_exports, {
|
|
1426
|
-
cleanupWorktree: () => cleanupWorktree,
|
|
1427
|
-
enterWorktree: () => enterWorktree,
|
|
1428
|
-
exitWorktree: () => exitWorktree,
|
|
1429
|
-
findGitRoot: () => findGitRoot,
|
|
1430
|
-
getOrCreateWorktree: () => getOrCreateWorktree,
|
|
1431
|
-
getWorktreeSession: () => getWorktreeSession,
|
|
1432
|
-
validateWorktreeSlug: () => validateWorktreeSlug,
|
|
1433
|
-
worktreeBranchName: () => worktreeBranchName
|
|
1434
|
-
});
|
|
1435
|
-
import { spawnSync } from "child_process";
|
|
1436
|
-
import { symlinkSync, mkdirSync, statSync as statSync2, readFileSync, rmSync } from "fs";
|
|
1437
|
-
import { join as join3 } from "path";
|
|
1438
|
-
function validateWorktreeSlug(slug2) {
|
|
1439
|
-
if (!slug2) throw new Error("worktree slug must not be empty");
|
|
1440
|
-
if (slug2.length > MAX_SLUG_LENGTH) throw new Error(`worktree slug must be \u2264${MAX_SLUG_LENGTH} chars (got ${slug2.length})`);
|
|
1441
|
-
for (const seg of slug2.split("/")) {
|
|
1442
|
-
if (seg === "." || seg === "..") throw new Error(`worktree slug must not contain "." or ".." segments`);
|
|
1443
|
-
if (!VALID_SEGMENT.test(seg)) throw new Error(`worktree slug segment "${seg}" contains invalid characters (allowed: a-z A-Z 0-9 . _ -)`);
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
function flattenSlug(slug2) {
|
|
1447
|
-
return slug2.replaceAll("/", "+");
|
|
1448
|
-
}
|
|
1449
|
-
function worktreeBranchName(slug2) {
|
|
1450
|
-
return `worktree-${flattenSlug(slug2)}`;
|
|
1451
|
-
}
|
|
1452
|
-
function worktreesDir(repoRoot) {
|
|
1453
|
-
return join3(repoRoot, ".claude", "worktrees");
|
|
1454
|
-
}
|
|
1455
|
-
function worktreePathFor(repoRoot, slug2) {
|
|
1456
|
-
return join3(worktreesDir(repoRoot), flattenSlug(slug2));
|
|
1457
|
-
}
|
|
1458
|
-
function git(args, cwd) {
|
|
1459
|
-
const r = spawnSync("git", args, {
|
|
1460
|
-
cwd,
|
|
1461
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
1462
|
-
env: { ...process.env, ...GIT_NO_PROMPT_ENV }
|
|
1463
|
-
});
|
|
1464
|
-
return { ok: r.status === 0, stdout: (r.stdout ?? "").toString().trim(), stderr: (r.stderr ?? "").toString().trim() };
|
|
1465
|
-
}
|
|
1466
|
-
function findGitRoot(from) {
|
|
1467
|
-
const r = git(["rev-parse", "--show-toplevel"], from);
|
|
1468
|
-
return r.ok ? r.stdout : null;
|
|
1469
|
-
}
|
|
1470
|
-
function readWorktreeHead(worktreePath) {
|
|
1471
|
-
try {
|
|
1472
|
-
const gitFile = readFileSync(join3(worktreePath, ".git"), "utf-8").trim();
|
|
1473
|
-
const m = gitFile.match(/^gitdir:\s*(.+)$/);
|
|
1474
|
-
if (!m) return null;
|
|
1475
|
-
const headPath = join3(m[1], "HEAD");
|
|
1476
|
-
const head = readFileSync(headPath, "utf-8").trim();
|
|
1477
|
-
const refMatch = head.match(/^ref:\s*(.+)$/);
|
|
1478
|
-
if (!refMatch) return head.length === 40 ? head : null;
|
|
1479
|
-
const refPath = join3(m[1], "..", refMatch[1]);
|
|
1480
|
-
try {
|
|
1481
|
-
return readFileSync(refPath, "utf-8").trim();
|
|
1482
|
-
} catch {
|
|
1483
|
-
}
|
|
1484
|
-
const r = git(["rev-parse", "HEAD"], worktreePath);
|
|
1485
|
-
return r.ok ? r.stdout : null;
|
|
1486
|
-
} catch {
|
|
1487
|
-
return null;
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
function getOrCreateWorktree(repoRoot, slug2) {
|
|
1491
|
-
validateWorktreeSlug(slug2);
|
|
1492
|
-
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
1493
|
-
const branch = worktreeBranchName(slug2);
|
|
1494
|
-
const existingHead = readWorktreeHead(worktreePath);
|
|
1495
|
-
if (existingHead) return { worktreePath, branch, headCommit: existingHead, existed: true };
|
|
1496
|
-
mkdirSync(worktreesDir(repoRoot), { recursive: true });
|
|
1497
|
-
const defaultBranch = git(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoRoot);
|
|
1498
|
-
let baseBranch = defaultBranch.ok ? defaultBranch.stdout.replace(/^origin\//, "") : "HEAD";
|
|
1499
|
-
const originRef = `origin/${baseBranch}`;
|
|
1500
|
-
const hasOrigin = git(["rev-parse", "--verify", originRef], repoRoot);
|
|
1501
|
-
if (!hasOrigin.ok) {
|
|
1502
|
-
const fetched = git(["fetch", "origin", baseBranch], repoRoot);
|
|
1503
|
-
if (!fetched.ok) baseBranch = "HEAD";
|
|
1504
|
-
}
|
|
1505
|
-
const base = baseBranch === "HEAD" ? "HEAD" : originRef;
|
|
1506
|
-
const r = git(["worktree", "add", "-B", branch, worktreePath, base], repoRoot);
|
|
1507
|
-
if (!r.ok) throw new Error(`failed to create worktree: ${r.stderr}`);
|
|
1508
|
-
const headSha = git(["rev-parse", "HEAD"], worktreePath);
|
|
1509
|
-
symlinkDirs(repoRoot, worktreePath, ["node_modules", ".bun"]);
|
|
1510
|
-
return { worktreePath, branch, headCommit: headSha.stdout, existed: false, baseBranch };
|
|
1511
|
-
}
|
|
1512
|
-
function symlinkDirs(repoRoot, worktreePath, dirs) {
|
|
1513
|
-
for (const dir of dirs) {
|
|
1514
|
-
const src = join3(repoRoot, dir);
|
|
1515
|
-
const dst = join3(worktreePath, dir);
|
|
1516
|
-
try {
|
|
1517
|
-
statSync2(src);
|
|
1518
|
-
} catch {
|
|
1519
|
-
continue;
|
|
1520
|
-
}
|
|
1521
|
-
try {
|
|
1522
|
-
symlinkSync(src, dst, "dir");
|
|
1523
|
-
} catch (e) {
|
|
1524
|
-
if (e?.code !== "EEXIST") console.error(`[worktree] symlink ${dir}: ${e?.message ?? e}`);
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
function cleanupWorktree(repoRoot, slug2, opts) {
|
|
1529
|
-
validateWorktreeSlug(slug2);
|
|
1530
|
-
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
1531
|
-
const branch = worktreeBranchName(slug2);
|
|
1532
|
-
try {
|
|
1533
|
-
statSync2(worktreePath);
|
|
1534
|
-
} catch {
|
|
1535
|
-
return { removed: false, reason: "not found" };
|
|
1536
|
-
}
|
|
1537
|
-
if (!opts?.force) {
|
|
1538
|
-
const status = git(["status", "--porcelain", "-uno"], worktreePath);
|
|
1539
|
-
if (status.ok && status.stdout) return { removed: false, reason: "uncommitted changes" };
|
|
1540
|
-
}
|
|
1541
|
-
for (const dir of ["node_modules", ".bun"]) {
|
|
1542
|
-
try {
|
|
1543
|
-
rmSync(join3(worktreePath, dir));
|
|
1544
|
-
} catch {
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
const rm = git(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
1548
|
-
if (!rm.ok) return { removed: false, reason: rm.stderr };
|
|
1549
|
-
git(["branch", "-D", branch], repoRoot);
|
|
1550
|
-
return { removed: true };
|
|
1551
|
-
}
|
|
1552
|
-
function getWorktreeSession() {
|
|
1553
|
-
return _session;
|
|
1554
|
-
}
|
|
1555
|
-
function enterWorktree(slug2, cwd) {
|
|
1556
|
-
const repoRoot = findGitRoot(cwd);
|
|
1557
|
-
if (!repoRoot) throw new Error("--worktree requires a git repository");
|
|
1558
|
-
const info = getOrCreateWorktree(repoRoot, slug2);
|
|
1559
|
-
process.chdir(info.worktreePath);
|
|
1560
|
-
_session = {
|
|
1561
|
-
originalCwd: cwd,
|
|
1562
|
-
worktreePath: info.worktreePath,
|
|
1563
|
-
slug: slug2,
|
|
1564
|
-
branch: info.branch,
|
|
1565
|
-
headCommit: info.headCommit
|
|
1566
|
-
};
|
|
1567
|
-
return _session;
|
|
1568
|
-
}
|
|
1569
|
-
function exitWorktree() {
|
|
1570
|
-
if (!_session) return null;
|
|
1571
|
-
const repoRoot = findGitRoot(_session.originalCwd);
|
|
1572
|
-
if (!repoRoot) return null;
|
|
1573
|
-
process.chdir(_session.originalCwd);
|
|
1574
|
-
const result = cleanupWorktree(repoRoot, _session.slug);
|
|
1575
|
-
_session = null;
|
|
1576
|
-
return result;
|
|
1577
|
-
}
|
|
1578
|
-
var VALID_SEGMENT, MAX_SLUG_LENGTH, GIT_NO_PROMPT_ENV, _session;
|
|
1579
|
-
var init_worktree = __esm({
|
|
1580
|
-
"src/worktree.ts"() {
|
|
1581
|
-
"use strict";
|
|
1582
|
-
VALID_SEGMENT = /^[a-zA-Z0-9._-]+$/;
|
|
1583
|
-
MAX_SLUG_LENGTH = 64;
|
|
1584
|
-
GIT_NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "" };
|
|
1585
|
-
_session = null;
|
|
1586
|
-
}
|
|
1587
|
-
});
|
|
1588
|
-
|
|
1589
1589
|
// src/shell.sandbox.ts
|
|
1590
1590
|
function writable(cwd, o, tmpDir) {
|
|
1591
1591
|
const set = /* @__PURE__ */ new Set([cwd, "/tmp", "/private/tmp", "/private/var/folders", "/dev", ...tmpDir ? [tmpDir] : [], ...o.writePaths]);
|
|
@@ -1755,7 +1755,7 @@ function makeRealShellTool(options) {
|
|
|
1755
1755
|
proc.stderr?.on("data", collect);
|
|
1756
1756
|
proc.on("error", (err2) => {
|
|
1757
1757
|
if (err2?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
|
|
1758
|
-
|
|
1758
|
+
log13.debug("shell spawn error", err2);
|
|
1759
1759
|
finish(`[exit 1] ${err2?.message ?? err2}${out ? "\n" + clean(out) : ""}`);
|
|
1760
1760
|
});
|
|
1761
1761
|
proc.on("close", (code) => {
|
|
@@ -1817,7 +1817,7 @@ ${clean(out) || "(no output yet)"}`;
|
|
|
1817
1817
|
}
|
|
1818
1818
|
];
|
|
1819
1819
|
}
|
|
1820
|
-
var
|
|
1820
|
+
var log13, clean, DETACHED, SECRET_ENV_RE, _spawn, ShellJobRegistry, NO_JOB2;
|
|
1821
1821
|
var init_tools_shell = __esm({
|
|
1822
1822
|
"src/tools.shell.ts"() {
|
|
1823
1823
|
"use strict";
|
|
@@ -1825,7 +1825,7 @@ var init_tools_shell = __esm({
|
|
|
1825
1825
|
init_redact();
|
|
1826
1826
|
init_logging();
|
|
1827
1827
|
init_shell_sandbox();
|
|
1828
|
-
|
|
1828
|
+
log13 = forComponent("shell");
|
|
1829
1829
|
clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
|
|
1830
1830
|
DETACHED = { stdio: ["ignore", "pipe", "pipe"], detached: true };
|
|
1831
1831
|
SECRET_ENV_RE = /(_API_KEY|_TOKEN|_SECRET|_PASSWORD|_PRIVATE_KEY|^AWS_|^GITHUB_TOKEN$|^OPENAI_|^ANTHROPIC_|^GOOGLE_|^GEMINI_|^GROQ_|^NPM_TOKEN$)/i;
|
|
@@ -1907,7 +1907,7 @@ var init_tools_shell = __esm({
|
|
|
1907
1907
|
|
|
1908
1908
|
// cli/cli.ts
|
|
1909
1909
|
import { createInterface } from "readline/promises";
|
|
1910
|
-
import { existsSync as existsSync10, readFileSync as readFileSync7, appendFileSync, mkdirSync as
|
|
1910
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, appendFileSync, mkdirSync as mkdirSync11, writeFileSync as writeFileSync9, readdirSync as readdirSync4, statSync as statSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
1911
1911
|
import { homedir as homedir9, tmpdir as tmpdir3 } from "os";
|
|
1912
1912
|
|
|
1913
1913
|
// cli/clipboard.ts
|
|
@@ -1975,7 +1975,7 @@ function copyTextToClipboard(text, platform2 = process.platform) {
|
|
|
1975
1975
|
}
|
|
1976
1976
|
|
|
1977
1977
|
// cli/cli.ts
|
|
1978
|
-
import { join as
|
|
1978
|
+
import { join as join14, resolve as resolve3, basename as basename2, extname, dirname as dirname4 } from "path";
|
|
1979
1979
|
import { AIClient, listModels, listProviders, getProviderFromModel, getModelInfo, resolveModel, isModelSupported, disposeCursorSessions } from "ai.libx.js";
|
|
1980
1980
|
|
|
1981
1981
|
// src/llm.ts
|
|
@@ -2622,7 +2622,13 @@ ${sections.join("\n\n---\n\n")}`;
|
|
|
2622
2622
|
|
|
2623
2623
|
// src/subagent.ts
|
|
2624
2624
|
init_tools();
|
|
2625
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2626
|
+
import { join as join4 } from "path";
|
|
2625
2627
|
init_OverlayFilesystem();
|
|
2628
|
+
init_worktree();
|
|
2629
|
+
init_NodeDiskFilesystem();
|
|
2630
|
+
init_logging();
|
|
2631
|
+
var log3 = forComponent("subagent");
|
|
2626
2632
|
async function boundedPool(items, limit, fn) {
|
|
2627
2633
|
const out = new Array(items.length);
|
|
2628
2634
|
let next = 0;
|
|
@@ -2635,13 +2641,86 @@ async function boundedPool(items, limit, fn) {
|
|
|
2635
2641
|
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
2636
2642
|
return out;
|
|
2637
2643
|
}
|
|
2644
|
+
function sanitizeSlug(raw) {
|
|
2645
|
+
return raw.replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").slice(0, 40);
|
|
2646
|
+
}
|
|
2647
|
+
function createWorktreeFs(cwd, label, index) {
|
|
2648
|
+
const repoRoot = findGitRoot(cwd);
|
|
2649
|
+
if (!repoRoot) return 'isolation: "worktree" requires a git repository';
|
|
2650
|
+
const slug2 = `sub-${index}-${sanitizeSlug(label)}`;
|
|
2651
|
+
try {
|
|
2652
|
+
const info = getOrCreateWorktree(repoRoot, slug2);
|
|
2653
|
+
return { repoRoot, slug: slug2, worktreePath: info.worktreePath, branch: info.branch };
|
|
2654
|
+
} catch (e) {
|
|
2655
|
+
return `worktree creation failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
function releaseWorktree(handle) {
|
|
2659
|
+
return cleanupWorktree(handle.repoRoot, handle.slug, { force: true });
|
|
2660
|
+
}
|
|
2661
|
+
function worktreePromptPrefix(branch) {
|
|
2662
|
+
return `You are running in an isolated git worktree on branch "${branch}". When you finish making changes, commit them with a clear message before replying with your summary. Your worktree will be removed after you finish \u2014 only committed work survives (on the branch).
|
|
2663
|
+
|
|
2664
|
+
`;
|
|
2665
|
+
}
|
|
2666
|
+
var traceSeq = 0;
|
|
2667
|
+
function traceFileFor(dir, label, depth) {
|
|
2668
|
+
try {
|
|
2669
|
+
mkdirSync2(dir, { recursive: true });
|
|
2670
|
+
const id = `${Date.now().toString(36)}-${(traceSeq++).toString(36)}`;
|
|
2671
|
+
return join4(dir, `${id}-d${depth}-${sanitizeSlug(label) || "sub"}.json`);
|
|
2672
|
+
} catch (e) {
|
|
2673
|
+
log3.warn(`subagent trace dir unavailable (${dir}) \u2014 running without a trace: ${e instanceof Error ? e.message : String(e)}`);
|
|
2674
|
+
return void 0;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
function flushTrace(file, meta, messages, status) {
|
|
2678
|
+
try {
|
|
2679
|
+
writeFileSync2(file, JSON.stringify({ ...meta, status, updated: Date.now(), messages }, null, 2));
|
|
2680
|
+
} catch (e) {
|
|
2681
|
+
log3.warn(`subagent trace flush failed (${file}): ${e instanceof Error ? e.message : String(e)}`);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
function installTrace(child, file, meta) {
|
|
2685
|
+
const prev = child.options.host;
|
|
2686
|
+
child.options.host = {
|
|
2687
|
+
...prev,
|
|
2688
|
+
notify: (e) => {
|
|
2689
|
+
if (e.kind === "turn_start") flushTrace(file, meta, child.transcript, "running");
|
|
2690
|
+
prev?.notify?.(e);
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
function abortHint(label, res, file) {
|
|
2695
|
+
const last = (res.text || "").trim().slice(0, 500);
|
|
2696
|
+
const lines = [
|
|
2697
|
+
`[sub-task '${label}' did NOT finish \u2014 ${res.finishReason} after ${res.steps} step(s). Treat its work as INCOMPLETE.]`,
|
|
2698
|
+
last ? `Progress so far: ${last}` : "It produced no summary before stopping."
|
|
2699
|
+
];
|
|
2700
|
+
if (file) lines.push(`Full transcript (every step it took): ${file} \u2014 Read it to see what was actually done.`);
|
|
2701
|
+
lines.push("Decide: ignore it, re-delegate a fresh Task that continues from the trace, or start over. Do NOT assume the sub-task completed.");
|
|
2702
|
+
return lines.join("\n");
|
|
2703
|
+
}
|
|
2704
|
+
async function runChildTraced(args) {
|
|
2705
|
+
const { opts, label, agentType, prompt } = args;
|
|
2706
|
+
if (args.signal) args.childOpts.signal = args.signal;
|
|
2707
|
+
const file = opts.traceDir ? traceFileFor(opts.traceDir, label, args.childDepth) : void 0;
|
|
2708
|
+
const meta = { label, agentType, prompt };
|
|
2709
|
+
const child = new Agent(args.childOpts);
|
|
2710
|
+
if (file) installTrace(child, file, meta);
|
|
2711
|
+
const res = await child.run(prompt);
|
|
2712
|
+
if (file) flushTrace(file, meta, child.transcript, res.finishReason);
|
|
2713
|
+
const result = res.finishReason === "stop" ? res.text || `(child '${label}' finished with no summary; finishReason=${res.finishReason})` : abortHint(label, res, file);
|
|
2714
|
+
await opts.hooks?.onSubagentStop?.(result, { label, agentType });
|
|
2715
|
+
return { res, result };
|
|
2716
|
+
}
|
|
2638
2717
|
function childOptionsFor(opts, fs, depth, maxDepth, agentType) {
|
|
2639
2718
|
let def;
|
|
2640
2719
|
if (agentType) {
|
|
2641
2720
|
def = (opts.agents ?? []).find((a) => a.name === agentType);
|
|
2642
2721
|
if (!def) return `no subagent type '${agentType}'. Available: ${(opts.agents ?? []).map((a) => a.name).join(", ") || "(none defined)"}`;
|
|
2643
2722
|
}
|
|
2644
|
-
const childOpts = { ai: opts.ai, fs, model: def?.model ?? opts.model, subagents: true, depth: depth + 1, maxDepth };
|
|
2723
|
+
const childOpts = { ai: opts.ai, fs, model: def?.model ?? opts.model, subagents: true, depth: depth + 1, maxDepth, traceDir: opts.traceDir };
|
|
2645
2724
|
if (opts.maxSteps != null) childOpts.maxSteps = opts.maxSteps;
|
|
2646
2725
|
if (def?.systemPrompt) childOpts.systemPrompt = def.systemPrompt;
|
|
2647
2726
|
if (def?.tools?.length) {
|
|
@@ -2658,7 +2737,7 @@ function makeTaskTool(opts) {
|
|
|
2658
2737
|
const maxDepth = opts.maxDepth ?? 2;
|
|
2659
2738
|
return {
|
|
2660
2739
|
name: "Task",
|
|
2661
|
-
description:
|
|
2740
|
+
description: 'Delegate a self-contained sub-task to a child agent over the same filesystem. It runs autonomously with its own step budget and returns a concise summary \u2014 use to isolate context-heavy work (broad search, a scoped refactor). Provide a short `description` and a full `prompt`. If it is interrupted or fails, the result is a STATUS HINT with a pointer to its full transcript (Read it to recover what it did, then decide whether to continue or restart) \u2014 do not assume an incomplete sub-task succeeded. Set `background: true` to run it detached (returns a job id to poll with JobOutput while you keep working); its file edits are overlay-isolated and commit when it finishes. Set `isolation: "worktree"` to run in a real git worktree (needed when the child uses real shell commands that must see/modify actual files) \u2014 slower (~200ms setup), auto-cleaned if no changes.',
|
|
2662
2741
|
parameters: {
|
|
2663
2742
|
type: "object",
|
|
2664
2743
|
required: ["description", "prompt"],
|
|
@@ -2666,36 +2745,83 @@ function makeTaskTool(opts) {
|
|
|
2666
2745
|
description: { type: "string", description: "a short (3-5 word) label for the sub-task" },
|
|
2667
2746
|
prompt: { type: "string", description: "the full instructions the child agent should carry out" },
|
|
2668
2747
|
agentType: { type: "string", description: "optional named subagent type (its persona, model, and scoped tools) \u2014 see the catalog" },
|
|
2669
|
-
background: { type: "boolean", description: "run detached (overlay-isolated, commits on finish); returns a job id to poll with JobOutput instead of blocking" }
|
|
2748
|
+
background: { type: "boolean", description: "run detached (overlay-isolated, commits on finish); returns a job id to poll with JobOutput instead of blocking" },
|
|
2749
|
+
isolation: { type: "string", enum: ["overlay", "worktree"], description: 'isolation mode: "overlay" (default, VFS layer) or "worktree" (real git worktree for shell-heavy work)' }
|
|
2670
2750
|
}
|
|
2671
2751
|
},
|
|
2672
|
-
async run({ description, prompt, agentType, background }, ctx) {
|
|
2752
|
+
async run({ description, prompt, agentType, background, isolation }, ctx) {
|
|
2673
2753
|
if (depth >= maxDepth) {
|
|
2674
2754
|
return `Error: Task depth limit reached (maxDepth ${maxDepth}). Cannot spawn another child agent \u2014 do this work directly instead.`;
|
|
2675
2755
|
}
|
|
2676
2756
|
const label = String(description ?? agentType ?? "sub-task");
|
|
2757
|
+
const useWorktree = isolation === "worktree";
|
|
2677
2758
|
if (background && ctx.jobs) {
|
|
2678
2759
|
const id = ctx.jobs.start(async ({ signal }) => {
|
|
2679
|
-
|
|
2680
|
-
|
|
2760
|
+
let fs2;
|
|
2761
|
+
let commit;
|
|
2762
|
+
let wtHandle2;
|
|
2763
|
+
if (useWorktree) {
|
|
2764
|
+
const h = createWorktreeFs(process.cwd(), label, 0);
|
|
2765
|
+
if (typeof h === "string") throw new Error(h);
|
|
2766
|
+
wtHandle2 = h;
|
|
2767
|
+
fs2 = new NodeDiskFilesystem(h.worktreePath);
|
|
2768
|
+
commit = async () => {
|
|
2769
|
+
};
|
|
2770
|
+
} else {
|
|
2771
|
+
const overlay = new OverlayFilesystem(opts.fs);
|
|
2772
|
+
fs2 = overlay;
|
|
2773
|
+
commit = () => overlay.commit();
|
|
2774
|
+
}
|
|
2775
|
+
const childOpts2 = childOptionsFor(opts, fs2, depth, maxDepth, agentType);
|
|
2681
2776
|
if (typeof childOpts2 === "string") throw new Error(childOpts2);
|
|
2682
|
-
|
|
2683
|
-
const
|
|
2684
|
-
if (
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2777
|
+
const childPrompt = wtHandle2 ? worktreePromptPrefix(wtHandle2.branch) + String(prompt ?? "") : String(prompt ?? "");
|
|
2778
|
+
const { res, result } = await runChildTraced({ opts, childOpts: childOpts2, prompt: childPrompt, label, agentType, childDepth: depth + 1, signal });
|
|
2779
|
+
if (res.finishReason === "aborted" || signal.aborted) {
|
|
2780
|
+
wtHandle2 && releaseWorktree(wtHandle2);
|
|
2781
|
+
return result;
|
|
2782
|
+
}
|
|
2783
|
+
await commit();
|
|
2784
|
+
let summary = result;
|
|
2785
|
+
if (wtHandle2) {
|
|
2786
|
+
const r = releaseWorktree(wtHandle2);
|
|
2787
|
+
if (!r.removed) summary += `
|
|
2788
|
+
|
|
2789
|
+
[worktree removal failed: ${r.reason}]`;
|
|
2790
|
+
}
|
|
2791
|
+
return summary;
|
|
2689
2792
|
}, { kind: "agent", label });
|
|
2690
2793
|
return `Started background sub-task ${id} ('${label}') \u2014 poll with JobOutput({id:"${id}"}).`;
|
|
2691
2794
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2795
|
+
let fs;
|
|
2796
|
+
let wtHandle;
|
|
2797
|
+
if (useWorktree) {
|
|
2798
|
+
const h = createWorktreeFs(process.cwd(), label, 0);
|
|
2799
|
+
if (typeof h === "string") return `Error: ${h}`;
|
|
2800
|
+
wtHandle = h;
|
|
2801
|
+
fs = new NodeDiskFilesystem(h.worktreePath);
|
|
2802
|
+
} else {
|
|
2803
|
+
fs = opts.fs;
|
|
2804
|
+
}
|
|
2805
|
+
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, agentType);
|
|
2806
|
+
if (typeof childOpts === "string") {
|
|
2807
|
+
wtHandle && releaseWorktree(wtHandle);
|
|
2808
|
+
return `Error: ${childOpts}`;
|
|
2809
|
+
}
|
|
2810
|
+
try {
|
|
2811
|
+
const childPrompt = wtHandle ? worktreePromptPrefix(wtHandle.branch) + String(prompt ?? "") : String(prompt ?? "");
|
|
2812
|
+
const { result } = await runChildTraced({ opts, childOpts, prompt: childPrompt, label, agentType, childDepth: depth + 1, signal: ctx.signal });
|
|
2813
|
+
let summary = result;
|
|
2814
|
+
if (wtHandle) {
|
|
2815
|
+
const r = releaseWorktree(wtHandle);
|
|
2816
|
+
if (!r.removed) summary += `
|
|
2817
|
+
|
|
2818
|
+
[worktree removal failed: ${r.reason}]`;
|
|
2819
|
+
}
|
|
2820
|
+
return summary;
|
|
2821
|
+
} catch (e) {
|
|
2822
|
+
if (wtHandle) releaseWorktree(wtHandle);
|
|
2823
|
+
throw e;
|
|
2824
|
+
}
|
|
2699
2825
|
}
|
|
2700
2826
|
};
|
|
2701
2827
|
}
|
|
@@ -2705,7 +2831,7 @@ function makeTaskBatchTool(opts) {
|
|
|
2705
2831
|
const maxParallel = opts.maxParallel ?? 4;
|
|
2706
2832
|
return {
|
|
2707
2833
|
name: "TaskBatch",
|
|
2708
|
-
description:
|
|
2834
|
+
description: 'Delegate SEVERAL independent sub-tasks to child agents that run concurrently; returns all their summaries. Each child is write-isolated (its file edits are merged back in array order). Use for parallel fan-out (review/search/scoped refactors across files). Provide `tasks: [{ description, prompt, agentType? }]`. Set `isolation: "worktree"` to run each child in its own git worktree (needed for real shell commands) \u2014 slower, auto-cleaned if no changes.',
|
|
2709
2835
|
parameters: {
|
|
2710
2836
|
type: "object",
|
|
2711
2837
|
required: ["tasks"],
|
|
@@ -2722,30 +2848,53 @@ function makeTaskBatchTool(opts) {
|
|
|
2722
2848
|
agentType: { type: "string", description: "optional named subagent type" }
|
|
2723
2849
|
}
|
|
2724
2850
|
}
|
|
2725
|
-
}
|
|
2851
|
+
},
|
|
2852
|
+
isolation: { type: "string", enum: ["overlay", "worktree"], description: 'isolation mode for ALL children: "overlay" (default) or "worktree" (real git worktree each)' }
|
|
2726
2853
|
}
|
|
2727
2854
|
},
|
|
2728
|
-
async run({ tasks },
|
|
2855
|
+
async run({ tasks, isolation }, ctx) {
|
|
2729
2856
|
if (depth >= maxDepth) return `Error: Task depth limit reached (maxDepth ${maxDepth}). Cannot spawn child agents \u2014 do this work directly instead.`;
|
|
2730
2857
|
const list = Array.isArray(tasks) ? tasks : [];
|
|
2731
2858
|
if (!list.length) return "Error: TaskBatch needs a non-empty `tasks` array.";
|
|
2859
|
+
const useWorktree = isolation === "worktree";
|
|
2732
2860
|
const results = await boundedPool(list, maxParallel, async (t, i) => {
|
|
2733
2861
|
const label = String(t?.description ?? t?.agentType ?? `task ${i + 1}`);
|
|
2734
|
-
|
|
2735
|
-
|
|
2862
|
+
let fs;
|
|
2863
|
+
let overlay;
|
|
2864
|
+
let wtHandle;
|
|
2865
|
+
if (useWorktree) {
|
|
2866
|
+
const h = createWorktreeFs(process.cwd(), label, i);
|
|
2867
|
+
if (typeof h === "string") return { label, error: h, ok: false };
|
|
2868
|
+
wtHandle = h;
|
|
2869
|
+
fs = new NodeDiskFilesystem(h.worktreePath);
|
|
2870
|
+
} else {
|
|
2871
|
+
overlay = new OverlayFilesystem(opts.fs);
|
|
2872
|
+
fs = overlay;
|
|
2873
|
+
}
|
|
2874
|
+
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, t?.agentType);
|
|
2736
2875
|
if (typeof childOpts === "string") return { label, error: childOpts, ok: false };
|
|
2737
2876
|
try {
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
return { label, text:
|
|
2877
|
+
const childPrompt = wtHandle ? worktreePromptPrefix(wtHandle.branch) + String(t?.prompt ?? "") : String(t?.prompt ?? "");
|
|
2878
|
+
const { res, result } = await runChildTraced({ opts, childOpts, prompt: childPrompt, label, agentType: t?.agentType, childDepth: depth + 1, signal: ctx.signal });
|
|
2879
|
+
return { label, text: result, overlay, wtHandle, ok: res.finishReason !== "error" && res.finishReason !== "aborted" };
|
|
2741
2880
|
} catch (e) {
|
|
2742
|
-
return { label, error: e instanceof Error ? e.message : String(e), ok: false };
|
|
2881
|
+
return { label, error: e instanceof Error ? e.message : String(e), wtHandle, ok: false };
|
|
2743
2882
|
}
|
|
2744
2883
|
});
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2884
|
+
const lines = [];
|
|
2885
|
+
for (const r of results) {
|
|
2886
|
+
if (r.ok && r.overlay) await r.overlay.commit().catch(() => {
|
|
2887
|
+
});
|
|
2888
|
+
let extra = "";
|
|
2889
|
+
if (r.wtHandle) {
|
|
2890
|
+
const wr = releaseWorktree(r.wtHandle);
|
|
2891
|
+
if (!wr.removed) extra = `
|
|
2892
|
+
[worktree removal failed: ${wr.reason}]`;
|
|
2893
|
+
}
|
|
2894
|
+
lines.push(`### ${lines.length + 1}. ${r.label}
|
|
2895
|
+
${r.error ? `ERROR: ${r.error}` : r.text || "(no summary)"}${extra}`);
|
|
2896
|
+
}
|
|
2897
|
+
return lines.join("\n\n");
|
|
2749
2898
|
}
|
|
2750
2899
|
};
|
|
2751
2900
|
}
|
|
@@ -2990,7 +3139,7 @@ function reasoningToChatFragment(model, effort) {
|
|
|
2990
3139
|
}
|
|
2991
3140
|
|
|
2992
3141
|
// src/Agent.ts
|
|
2993
|
-
var
|
|
3142
|
+
var log4 = forComponent("Agent");
|
|
2994
3143
|
function isAbortError(err2) {
|
|
2995
3144
|
const e = err2;
|
|
2996
3145
|
const blob = `${e?.message ?? ""} ${e?.name ?? ""} ${e?.code ?? ""} ${e?.cause?.name ?? ""}`;
|
|
@@ -3066,6 +3215,11 @@ var AgentOptions = class {
|
|
|
3066
3215
|
depth = 0;
|
|
3067
3216
|
/** Hard ceiling on subagent nesting (beyond it the `Task` tool refuses to spawn). */
|
|
3068
3217
|
maxDepth = 2;
|
|
3218
|
+
/** Real-disk dir where child agents stream their transcript as they run (origin-anchored — survives
|
|
3219
|
+
* worktree teardown / a child's own cwd). When set, an interrupted/failed child commits a STATUS HINT
|
|
3220
|
+
* + a pointer to its trace instead of a dangling tool_call, so the parent can read it and decide
|
|
3221
|
+
* whether to ignore, pick it up, or restart. Unset (library/sandbox default) = no tracing. */
|
|
3222
|
+
traceDir;
|
|
3069
3223
|
/** Stream tokens from the model. Takes effect only with a `host.notify`; off => current (non-stream) behavior. */
|
|
3070
3224
|
stream = false;
|
|
3071
3225
|
/** Fold the dropped middle of an over-long transcript into a synthetic summary (edge-safe, no LLM). Off => drop-oldest. */
|
|
@@ -3186,7 +3340,7 @@ var Agent = class _Agent {
|
|
|
3186
3340
|
const disk = new NodeDiskFilesystem2(process.cwd());
|
|
3187
3341
|
await disk.init();
|
|
3188
3342
|
this.options.fs = new JailedFilesystem2(disk);
|
|
3189
|
-
|
|
3343
|
+
log4.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
|
|
3190
3344
|
}
|
|
3191
3345
|
this.buildCtx();
|
|
3192
3346
|
}
|
|
@@ -3240,7 +3394,7 @@ var Agent = class _Agent {
|
|
|
3240
3394
|
agents = loaded.agents;
|
|
3241
3395
|
if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
|
|
3242
3396
|
}
|
|
3243
|
-
const taskOpts = { ai: o.ai, model: o.model, fs, depth: o.depth, maxDepth: o.maxDepth, agents, hooks: o.hooks };
|
|
3397
|
+
const taskOpts = { ai: o.ai, model: o.model, fs, depth: o.depth, maxDepth: o.maxDepth, agents, hooks: o.hooks, traceDir: o.traceDir };
|
|
3244
3398
|
tools = [...tools, makeTaskTool(taskOpts), makeTaskBatchTool(taskOpts)];
|
|
3245
3399
|
}
|
|
3246
3400
|
if (o.checkpoints) tools = [...tools, ...checkpointTools()];
|
|
@@ -3329,7 +3483,7 @@ var Agent = class _Agent {
|
|
|
3329
3483
|
let lastFp = "";
|
|
3330
3484
|
let repeats = 0;
|
|
3331
3485
|
const kill = (finishReason) => {
|
|
3332
|
-
|
|
3486
|
+
log4.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(usage.totalTokens - 0.9 * usage.cacheReadTokens)}, ms=${Date.now() - start - this.parkedMs}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
|
|
3333
3487
|
this.ctx.jobs?.killAll();
|
|
3334
3488
|
return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated };
|
|
3335
3489
|
};
|
|
@@ -3375,7 +3529,7 @@ var Agent = class _Agent {
|
|
|
3375
3529
|
const transient = !o.signal?.aborted && !isAbortError(err2) && attempt < 2 && (network || serverSide);
|
|
3376
3530
|
if (!transient) throw err2;
|
|
3377
3531
|
const waitMs = 1e3 * (attempt + 1);
|
|
3378
|
-
|
|
3532
|
+
log4.warn(`network drop mid-step (${err2?.message ?? err2}) \u2014 retrying in ${waitMs}ms`);
|
|
3379
3533
|
o.host?.notify?.({ kind: "retry", message: `connection dropped \u2014 retrying step (#${attempt + 1})` });
|
|
3380
3534
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
3381
3535
|
}
|
|
@@ -3391,7 +3545,7 @@ var Agent = class _Agent {
|
|
|
3391
3545
|
bodyStr = void 0;
|
|
3392
3546
|
}
|
|
3393
3547
|
if (bodyStr && err2 instanceof Error && !err2.message.includes(bodyStr)) err2.detail = bodyStr;
|
|
3394
|
-
|
|
3548
|
+
log4.error(`chat() failed: ${err2?.message ?? err2}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, err2);
|
|
3395
3549
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
3396
3550
|
}
|
|
3397
3551
|
if (o.signal?.aborted) return kill("aborted");
|
|
@@ -3418,7 +3572,7 @@ var Agent = class _Agent {
|
|
|
3418
3572
|
});
|
|
3419
3573
|
}
|
|
3420
3574
|
if (toolCalls.length === 0) {
|
|
3421
|
-
|
|
3575
|
+
log4.verbose(`completed in ${steps} step(s)`);
|
|
3422
3576
|
await this.ctx.jobs?.drain();
|
|
3423
3577
|
await this.activeHooks?.onStop?.(res.content ?? "");
|
|
3424
3578
|
return { text: res.content ?? "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated };
|
|
@@ -3484,13 +3638,13 @@ var Agent = class _Agent {
|
|
|
3484
3638
|
const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
|
|
3485
3639
|
if (decision?.block) {
|
|
3486
3640
|
const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
|
|
3487
|
-
|
|
3641
|
+
log4.debug(`${tc.function.name} -> ${blocked}`);
|
|
3488
3642
|
await hooks?.postToolUse?.(call, blocked, meta);
|
|
3489
3643
|
return blocked;
|
|
3490
3644
|
}
|
|
3491
3645
|
this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
|
|
3492
3646
|
if (earlyError) {
|
|
3493
|
-
|
|
3647
|
+
log4.debug(`${tc.function.name} -> ${earlyError}`);
|
|
3494
3648
|
await hooks?.postToolUse?.(call, earlyError, meta);
|
|
3495
3649
|
this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
|
|
3496
3650
|
return earlyError;
|
|
@@ -3499,12 +3653,12 @@ var Agent = class _Agent {
|
|
|
3499
3653
|
let images;
|
|
3500
3654
|
let threw = false;
|
|
3501
3655
|
try {
|
|
3502
|
-
|
|
3656
|
+
log4.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
3503
3657
|
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
3504
3658
|
try {
|
|
3505
3659
|
hooks.onToolOutput(call, chunk, meta);
|
|
3506
3660
|
} catch (e) {
|
|
3507
|
-
|
|
3661
|
+
log4.debug(`onToolOutput hook error: ${e}`);
|
|
3508
3662
|
}
|
|
3509
3663
|
} : void 0;
|
|
3510
3664
|
const raw = await tool.run(args, this.ctx);
|
|
@@ -3516,7 +3670,7 @@ var Agent = class _Agent {
|
|
|
3516
3670
|
}
|
|
3517
3671
|
} catch (e) {
|
|
3518
3672
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3519
|
-
|
|
3673
|
+
log4.debug(`${tc.function.name} -> error: ${msg}`);
|
|
3520
3674
|
result = `Error: ${msg}`;
|
|
3521
3675
|
threw = true;
|
|
3522
3676
|
} finally {
|
|
@@ -3642,7 +3796,7 @@ function fitTokenBudget(messages, maxTokens) {
|
|
|
3642
3796
|
const ids = callIdSet(messages.slice(from));
|
|
3643
3797
|
while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
|
|
3644
3798
|
if (total > maxTokens)
|
|
3645
|
-
|
|
3799
|
+
log4.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
|
|
3646
3800
|
return [...head, ...messages.slice(from)];
|
|
3647
3801
|
}
|
|
3648
3802
|
function compact(m, max, focus) {
|
|
@@ -4199,7 +4353,7 @@ init_tools_web();
|
|
|
4199
4353
|
init_tools();
|
|
4200
4354
|
init_tools_structured();
|
|
4201
4355
|
init_logging();
|
|
4202
|
-
var
|
|
4356
|
+
var log5 = forComponent("scratch");
|
|
4203
4357
|
var SCRATCH_DIR = "/scratch";
|
|
4204
4358
|
function shortArgs(args) {
|
|
4205
4359
|
try {
|
|
@@ -4239,7 +4393,7 @@ var Scratch = class {
|
|
|
4239
4393
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
4240
4394
|
await this.fs.writeFile(path, header + raw);
|
|
4241
4395
|
} catch (e) {
|
|
4242
|
-
|
|
4396
|
+
log5.debug("scratch write failed; returning raw", e);
|
|
4243
4397
|
return raw;
|
|
4244
4398
|
}
|
|
4245
4399
|
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
@@ -4269,7 +4423,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
|
|
|
4269
4423
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
4270
4424
|
await this.fs.writeFile(path, header + full);
|
|
4271
4425
|
} catch (e) {
|
|
4272
|
-
|
|
4426
|
+
log5.debug("scratch spill failed; cropping lossy", e);
|
|
4273
4427
|
return full.slice(0, pageBytes) + `
|
|
4274
4428
|
|
|
4275
4429
|
[output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
|
|
@@ -4315,7 +4469,7 @@ Question: ${q2}`);
|
|
|
4315
4469
|
const answer = (res.text ?? "").trim();
|
|
4316
4470
|
return answer || "(no answer found in scratch)";
|
|
4317
4471
|
} catch (e) {
|
|
4318
|
-
|
|
4472
|
+
log5.debug("Ask peek failed", e);
|
|
4319
4473
|
return `Error querying scratch: ${e?.message ?? e}`;
|
|
4320
4474
|
}
|
|
4321
4475
|
}
|
|
@@ -4324,7 +4478,7 @@ Question: ${q2}`);
|
|
|
4324
4478
|
|
|
4325
4479
|
// src/lessons.ts
|
|
4326
4480
|
init_logging();
|
|
4327
|
-
var
|
|
4481
|
+
var log6 = forComponent("Lessons");
|
|
4328
4482
|
var LessonOptionsDefaults = class {
|
|
4329
4483
|
minRepeats = 2;
|
|
4330
4484
|
};
|
|
@@ -4349,15 +4503,15 @@ function lessonCapture(options) {
|
|
|
4349
4503
|
counts.set(lesson.slug, n);
|
|
4350
4504
|
if (n < o.minRepeats) return;
|
|
4351
4505
|
written.add(lesson.slug);
|
|
4352
|
-
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) =>
|
|
4353
|
-
|
|
4506
|
+
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log6.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
|
|
4507
|
+
log6.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
|
|
4354
4508
|
}
|
|
4355
4509
|
};
|
|
4356
4510
|
}
|
|
4357
4511
|
|
|
4358
4512
|
// src/reflect.ts
|
|
4359
4513
|
init_logging();
|
|
4360
|
-
var
|
|
4514
|
+
var log7 = forComponent("Reflect");
|
|
4361
4515
|
async function reflectOnRun(o) {
|
|
4362
4516
|
const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
|
|
4363
4517
|
if (!digest.trim()) return null;
|
|
@@ -4373,7 +4527,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
4373
4527
|
const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
|
|
4374
4528
|
text = r?.content ?? "";
|
|
4375
4529
|
} catch (e) {
|
|
4376
|
-
|
|
4530
|
+
log7.warn(`reflection call failed: ${e?.message ?? e}`);
|
|
4377
4531
|
return null;
|
|
4378
4532
|
}
|
|
4379
4533
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
@@ -4383,10 +4537,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
4383
4537
|
try {
|
|
4384
4538
|
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
4385
4539
|
} catch (e) {
|
|
4386
|
-
|
|
4540
|
+
log7.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
4387
4541
|
return null;
|
|
4388
4542
|
}
|
|
4389
|
-
|
|
4543
|
+
log7.debug(`reflection persisted ${slug2}`);
|
|
4390
4544
|
return slug2;
|
|
4391
4545
|
}
|
|
4392
4546
|
function digestRun(messages, maxChars) {
|
|
@@ -4403,7 +4557,7 @@ function digestRun(messages, maxChars) {
|
|
|
4403
4557
|
// src/duplex.ts
|
|
4404
4558
|
import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
|
|
4405
4559
|
init_logging();
|
|
4406
|
-
var
|
|
4560
|
+
var log8 = forComponent("DuplexAgent");
|
|
4407
4561
|
function describeCall(call) {
|
|
4408
4562
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
4409
4563
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -4543,7 +4697,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
|
|
|
4543
4697
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER);
|
|
4544
4698
|
if (m) {
|
|
4545
4699
|
this.fabricationCut = true;
|
|
4546
|
-
|
|
4700
|
+
log8.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
4547
4701
|
const safe = this.reflexBuf.slice(this.reflexForwarded, m.index);
|
|
4548
4702
|
if (!safe) return;
|
|
4549
4703
|
if (safe.trim()) this.spokeThisTurn = true;
|
|
@@ -4622,7 +4776,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
|
|
|
4622
4776
|
try {
|
|
4623
4777
|
await this.voice.send("[reminder] You dispatched a task but said nothing to the user. Say ONE short spoken acknowledgement now \u2014 no tools.");
|
|
4624
4778
|
} catch (e) {
|
|
4625
|
-
|
|
4779
|
+
log8.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
4626
4780
|
} finally {
|
|
4627
4781
|
this.nudging = false;
|
|
4628
4782
|
}
|
|
@@ -4770,7 +4924,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
4770
4924
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
4771
4925
|
const cres = await new Agent(agentOpts).run(checkBrief);
|
|
4772
4926
|
if (cres.finishReason !== "stop") {
|
|
4773
|
-
|
|
4927
|
+
log8.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
4774
4928
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
4775
4929
|
}
|
|
4776
4930
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -4873,7 +5027,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
4873
5027
|
}
|
|
4874
5028
|
rec.status = "done";
|
|
4875
5029
|
rec.result = res.text;
|
|
4876
|
-
|
|
5030
|
+
log8.verbose(`task ${id} done (${res.steps} steps)`);
|
|
4877
5031
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
4878
5032
|
id,
|
|
4879
5033
|
text: res.text,
|
|
@@ -4891,7 +5045,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
4891
5045
|
this.dropAsk(rec.id);
|
|
4892
5046
|
rec.status = "error";
|
|
4893
5047
|
rec.result = msg;
|
|
4894
|
-
|
|
5048
|
+
log8.warn(`task ${rec.id} failed: ${msg}`);
|
|
4895
5049
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
|
|
4896
5050
|
this.queueRevoice(`[task ${rec.id} failed] ${msg}`);
|
|
4897
5051
|
}
|
|
@@ -5193,7 +5347,7 @@ init_logging();
|
|
|
5193
5347
|
|
|
5194
5348
|
// src/voice/engine.ts
|
|
5195
5349
|
init_logging();
|
|
5196
|
-
var
|
|
5350
|
+
var log9 = forComponent("VoiceEngine");
|
|
5197
5351
|
var now = () => performance.now();
|
|
5198
5352
|
var forSpeech = (t) => t.replace(/[*_`#]+/g, "").replace(/^[ \t]*[-•]\s+/gm, "").replace(/\s*[\u2013\u2014]\s*/g, ", ").replace(/[\u2010\u2011]/g, "-").replace(/\s*\|\s*/g, ", ").replace(/(\d)\s+%/g, "$1%").replace(/\.{3,}/g, ".");
|
|
5199
5353
|
var VoiceEngineOptions = class {
|
|
@@ -5301,7 +5455,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
5301
5455
|
this.stt.onLevel = (rms) => this.handleLevel(rms);
|
|
5302
5456
|
await Promise.all([this.tts.connect(), this.stt.start()]);
|
|
5303
5457
|
this.setState("listening");
|
|
5304
|
-
|
|
5458
|
+
log9.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
|
|
5305
5459
|
}
|
|
5306
5460
|
get usingAec() {
|
|
5307
5461
|
return this.stt.usingAec;
|
|
@@ -5353,7 +5507,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
5353
5507
|
this.reply += text;
|
|
5354
5508
|
for (const w of this.words(this.reply)) this.echoWords.add(w);
|
|
5355
5509
|
this.tts.speak(forSpeech(text), true);
|
|
5356
|
-
if (!this.spokeDeltas && this.turnStartAt)
|
|
5510
|
+
if (!this.spokeDeltas && this.turnStartAt) log9.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
|
|
5357
5511
|
this.spokeDeltas = true;
|
|
5358
5512
|
this.setState("speaking");
|
|
5359
5513
|
}
|
|
@@ -5374,7 +5528,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
5374
5528
|
}
|
|
5375
5529
|
this.drainTimer = null;
|
|
5376
5530
|
this.speaking = false;
|
|
5377
|
-
if (this.turnStartAt)
|
|
5531
|
+
if (this.turnStartAt) log9.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
|
|
5378
5532
|
this.echoUntil = now() + 2500;
|
|
5379
5533
|
if (!this.usingAec) this.stt.reset();
|
|
5380
5534
|
this.setState("listening");
|
|
@@ -5515,7 +5669,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
5515
5669
|
this.pendingUtt = this.pendingUtt ? `${this.pendingUtt} ${text}` : text;
|
|
5516
5670
|
if (this.pendingTimer) clearTimeout(this.pendingTimer);
|
|
5517
5671
|
if (this.options.incompleteMergeMs && this.looksIncomplete(this.pendingUtt)) {
|
|
5518
|
-
|
|
5672
|
+
log9.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
|
|
5519
5673
|
this.options.onHold();
|
|
5520
5674
|
if (this.options.holdFiller && !this.speaking) {
|
|
5521
5675
|
this.beginSpeech();
|
|
@@ -5613,7 +5767,7 @@ async function resolveAuth(auth) {
|
|
|
5613
5767
|
}
|
|
5614
5768
|
|
|
5615
5769
|
// src/voice/soniox.ts
|
|
5616
|
-
var
|
|
5770
|
+
var log10 = forComponent("SonioxSTT");
|
|
5617
5771
|
var now2 = () => performance.now();
|
|
5618
5772
|
var SonioxSTTOptions = class {
|
|
5619
5773
|
auth = "";
|
|
@@ -5670,9 +5824,9 @@ var SonioxSTT = class {
|
|
|
5670
5824
|
this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
|
|
5671
5825
|
this.ws.onclose = (ev) => {
|
|
5672
5826
|
if (this.stopped) return;
|
|
5673
|
-
|
|
5827
|
+
log10.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
5674
5828
|
this.reset();
|
|
5675
|
-
this.connectWs().catch((e) =>
|
|
5829
|
+
this.connectWs().catch((e) => log10.error(`soniox reconnect failed: ${e.message}`));
|
|
5676
5830
|
};
|
|
5677
5831
|
}
|
|
5678
5832
|
async start() {
|
|
@@ -5682,7 +5836,7 @@ var SonioxSTT = class {
|
|
|
5682
5836
|
this.endpointTimer = setInterval(() => {
|
|
5683
5837
|
const combined = (this.finalText + this.partialText).trim();
|
|
5684
5838
|
if (!combined || now2() - this.lastChangeAt < this.options.silenceEndpointMs) return;
|
|
5685
|
-
if (this.firstTokenAt)
|
|
5839
|
+
if (this.firstTokenAt) log10.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
|
|
5686
5840
|
this.reset();
|
|
5687
5841
|
this.onUtterance(combined, now2());
|
|
5688
5842
|
}, 120);
|
|
@@ -5699,7 +5853,7 @@ var SonioxSTT = class {
|
|
|
5699
5853
|
});
|
|
5700
5854
|
}
|
|
5701
5855
|
handle(m) {
|
|
5702
|
-
if (m.error_message) return
|
|
5856
|
+
if (m.error_message) return log10.error(`soniox: ${m.error_message}`);
|
|
5703
5857
|
let endpoint = false;
|
|
5704
5858
|
for (const t of m.tokens ?? []) {
|
|
5705
5859
|
if (t.text === "<end>") endpoint = true;
|
|
@@ -5715,7 +5869,7 @@ var SonioxSTT = class {
|
|
|
5715
5869
|
this.onPartial(combined);
|
|
5716
5870
|
if (endpoint && this.finalText.trim()) {
|
|
5717
5871
|
const utterance = this.finalText.trim();
|
|
5718
|
-
if (this.firstTokenAt)
|
|
5872
|
+
if (this.firstTokenAt) log10.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
|
|
5719
5873
|
this.reset();
|
|
5720
5874
|
this.onUtterance(utterance, now2());
|
|
5721
5875
|
}
|
|
@@ -5737,7 +5891,7 @@ var SonioxSTT = class {
|
|
|
5737
5891
|
|
|
5738
5892
|
// src/voice/cartesia.ts
|
|
5739
5893
|
init_logging();
|
|
5740
|
-
var
|
|
5894
|
+
var log11 = forComponent("CartesiaTTS");
|
|
5741
5895
|
var now3 = () => performance.now();
|
|
5742
5896
|
var CartesiaTTSOptions = class {
|
|
5743
5897
|
auth = "";
|
|
@@ -5787,9 +5941,9 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
5787
5941
|
this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
|
|
5788
5942
|
});
|
|
5789
5943
|
this.ws.onclose = (ev) => {
|
|
5790
|
-
|
|
5944
|
+
log11.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
5791
5945
|
if (!this.closed) {
|
|
5792
|
-
this.connecting = this.doConnect().catch((e) =>
|
|
5946
|
+
this.connecting = this.doConnect().catch((e) => log11.error(`cartesia reconnect failed: ${e.message}`));
|
|
5793
5947
|
}
|
|
5794
5948
|
};
|
|
5795
5949
|
this.ws.onmessage = (ev) => {
|
|
@@ -5811,11 +5965,11 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
5811
5965
|
this.down = true;
|
|
5812
5966
|
this.downAt = now3();
|
|
5813
5967
|
this.consecutiveOk = 0;
|
|
5814
|
-
|
|
5968
|
+
log11.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
|
|
5815
5969
|
this.onDone();
|
|
5816
5970
|
this.startProbe();
|
|
5817
5971
|
} else if (!this.down) {
|
|
5818
|
-
|
|
5972
|
+
log11.warn(`cartesia: ${JSON.stringify(m)}`);
|
|
5819
5973
|
}
|
|
5820
5974
|
}
|
|
5821
5975
|
};
|
|
@@ -5829,7 +5983,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
5829
5983
|
this.consecutiveOk = 0;
|
|
5830
5984
|
this.stopProbe();
|
|
5831
5985
|
const downMs = this.downAt ? now3() - this.downAt : 0;
|
|
5832
|
-
(downMs < 2e3 ?
|
|
5986
|
+
(downMs < 2e3 ? log11.debug : log11.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
|
|
5833
5987
|
}
|
|
5834
5988
|
/** Ensure the WS is open before sending — reconnects if idle-closed. */
|
|
5835
5989
|
async ensureConnected() {
|
|
@@ -5909,7 +6063,7 @@ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor a
|
|
|
5909
6063
|
init_logging();
|
|
5910
6064
|
import { spawn } from "child_process";
|
|
5911
6065
|
import { createHash } from "crypto";
|
|
5912
|
-
var
|
|
6066
|
+
var log12 = forComponent("mcp");
|
|
5913
6067
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
5914
6068
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
5915
6069
|
var StdioTransport = class {
|
|
@@ -5928,7 +6082,7 @@ var StdioTransport = class {
|
|
|
5928
6082
|
proc.stdout.setEncoding("utf8");
|
|
5929
6083
|
proc.stdout.on("data", (chunk) => this.onData(chunk));
|
|
5930
6084
|
proc.stderr.setEncoding("utf8");
|
|
5931
|
-
proc.stderr.on("data", (chunk) =>
|
|
6085
|
+
proc.stderr.on("data", (chunk) => log12.debug(`[${command}] stderr:`, chunk.trimEnd()));
|
|
5932
6086
|
proc.on("exit", (code) => this.failAll(new Error(`MCP server "${command}" exited (code ${code})`)));
|
|
5933
6087
|
proc.on("error", (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));
|
|
5934
6088
|
}
|
|
@@ -5942,7 +6096,7 @@ var StdioTransport = class {
|
|
|
5942
6096
|
try {
|
|
5943
6097
|
this.dispatch(JSON.parse(line));
|
|
5944
6098
|
} catch (e) {
|
|
5945
|
-
|
|
6099
|
+
log12.debug("dropping non-JSON line from MCP server:", line, e);
|
|
5946
6100
|
}
|
|
5947
6101
|
}
|
|
5948
6102
|
}
|
|
@@ -5991,7 +6145,7 @@ var StdioTransport = class {
|
|
|
5991
6145
|
try {
|
|
5992
6146
|
this.proc?.stdin?.end();
|
|
5993
6147
|
} catch (e) {
|
|
5994
|
-
|
|
6148
|
+
log12.debug("stdin end failed", e);
|
|
5995
6149
|
}
|
|
5996
6150
|
this.proc?.kill();
|
|
5997
6151
|
}
|
|
@@ -6060,7 +6214,7 @@ function parseSseResponse(body) {
|
|
|
6060
6214
|
const obj = JSON.parse(trimmed.slice(5).trim());
|
|
6061
6215
|
if (obj && (obj.result !== void 0 || obj.error !== void 0)) return obj;
|
|
6062
6216
|
} catch (e) {
|
|
6063
|
-
|
|
6217
|
+
log12.debug("skipping unparseable SSE data line", e);
|
|
6064
6218
|
}
|
|
6065
6219
|
}
|
|
6066
6220
|
return {};
|
|
@@ -6128,7 +6282,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
|
|
|
6128
6282
|
return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg };
|
|
6129
6283
|
})(), mountTimeoutMs, name);
|
|
6130
6284
|
} catch (e) {
|
|
6131
|
-
await client.close().catch((err2) =>
|
|
6285
|
+
await client.close().catch((err2) => log12.debug(`close after failed mount of "${name}": ${err2}`));
|
|
6132
6286
|
throw e;
|
|
6133
6287
|
}
|
|
6134
6288
|
}
|
|
@@ -6139,15 +6293,15 @@ function validEntries(servers) {
|
|
|
6139
6293
|
return Object.entries(servers).filter(([name, cfg]) => {
|
|
6140
6294
|
if (!cfg || cfg.disabled) return false;
|
|
6141
6295
|
if (!cfg.command && !cfg.url) {
|
|
6142
|
-
|
|
6296
|
+
log12.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
|
|
6143
6297
|
return false;
|
|
6144
6298
|
}
|
|
6145
6299
|
return true;
|
|
6146
6300
|
});
|
|
6147
6301
|
}
|
|
6148
6302
|
function logMountFailure(name, e) {
|
|
6149
|
-
if (e instanceof McpAuthError)
|
|
6150
|
-
else
|
|
6303
|
+
if (e instanceof McpAuthError) log12.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
|
|
6304
|
+
else log12.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
|
|
6151
6305
|
}
|
|
6152
6306
|
async function mountMcpServers(servers = {}, opts = {}) {
|
|
6153
6307
|
const entries = validEntries(servers);
|
|
@@ -6157,7 +6311,7 @@ async function mountMcpServers(servers = {}, opts = {}) {
|
|
|
6157
6311
|
const name = entries[i][0];
|
|
6158
6312
|
if (r.status === "fulfilled") {
|
|
6159
6313
|
out.push(r.value);
|
|
6160
|
-
|
|
6314
|
+
log12.debug(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
|
|
6161
6315
|
} else logMountFailure(name, r.reason);
|
|
6162
6316
|
});
|
|
6163
6317
|
return out;
|
|
@@ -6197,7 +6351,7 @@ var McpPool = class {
|
|
|
6197
6351
|
const prev = this.warm.get(key);
|
|
6198
6352
|
if (prev) {
|
|
6199
6353
|
clearTimeout(prev.timer);
|
|
6200
|
-
if (prev.client !== client) void prev.client.close().catch((err2) =>
|
|
6354
|
+
if (prev.client !== client) void prev.client.close().catch((err2) => log12.debug(`warm-pool replace close failed: ${err2}`));
|
|
6201
6355
|
}
|
|
6202
6356
|
const e = { client, timer: void 0 };
|
|
6203
6357
|
this.warm.set(key, e);
|
|
@@ -6214,7 +6368,7 @@ var McpPool = class {
|
|
|
6214
6368
|
const e = this.warm.get(key);
|
|
6215
6369
|
if (!e) return;
|
|
6216
6370
|
this.warm.delete(key);
|
|
6217
|
-
await e.client.close().catch((err2) =>
|
|
6371
|
+
await e.client.close().catch((err2) => log12.debug(`warm-pool evict close failed: ${err2}`));
|
|
6218
6372
|
}
|
|
6219
6373
|
async closeAll() {
|
|
6220
6374
|
for (const e of this.warm.values()) {
|
|
@@ -6231,7 +6385,7 @@ var defaultPool = new McpPool();
|
|
|
6231
6385
|
// cli/mcpOAuth.ts
|
|
6232
6386
|
import { createServer } from "http";
|
|
6233
6387
|
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
6234
|
-
import { readFileSync as readFileSync2, writeFileSync as
|
|
6388
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync } from "fs";
|
|
6235
6389
|
import { dirname as dirname2 } from "path";
|
|
6236
6390
|
var McpOAuthOptions = class {
|
|
6237
6391
|
/** Where to persist tokens. Mode 0600. */
|
|
@@ -6265,8 +6419,8 @@ var McpOAuth = class {
|
|
|
6265
6419
|
}
|
|
6266
6420
|
}
|
|
6267
6421
|
save(store) {
|
|
6268
|
-
|
|
6269
|
-
|
|
6422
|
+
mkdirSync3(dirname2(this.options.storePath), { recursive: true });
|
|
6423
|
+
writeFileSync3(this.options.storePath, JSON.stringify(store, null, 2), { mode: 384 });
|
|
6270
6424
|
}
|
|
6271
6425
|
// -- ATTENDED: one-time authorization ----------------------------------------
|
|
6272
6426
|
/** Run the authorization-code + PKCE flow and persist the resulting tokens for `serverUrl`. */
|
|
@@ -6424,15 +6578,15 @@ function defaultOpenBrowser(url) {
|
|
|
6424
6578
|
// cli/core.ts
|
|
6425
6579
|
import { randomUUID } from "crypto";
|
|
6426
6580
|
import { execFile as execFile2 } from "child_process";
|
|
6427
|
-
import { resolve, basename, join as
|
|
6428
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
6581
|
+
import { resolve, basename, join as join5 } from "path";
|
|
6582
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync4 } from "fs";
|
|
6429
6583
|
import { platform, arch, release, userInfo, homedir as homedir2, tmpdir } from "os";
|
|
6430
6584
|
init_tools_shell();
|
|
6431
6585
|
|
|
6432
6586
|
// src/tools.notify.ts
|
|
6433
6587
|
init_logging();
|
|
6434
6588
|
import { execFile } from "child_process";
|
|
6435
|
-
var
|
|
6589
|
+
var log14 = forComponent("notify");
|
|
6436
6590
|
function makeNotifyTool(opts = {}) {
|
|
6437
6591
|
const platform2 = opts.platform ?? process.platform;
|
|
6438
6592
|
const run = opts.exec ?? execFile;
|
|
@@ -6456,7 +6610,7 @@ function makeNotifyTool(opts = {}) {
|
|
|
6456
6610
|
return new Promise((resolve4) => {
|
|
6457
6611
|
run(argv[0], argv[1], { timeout: 5e3 }, (e) => {
|
|
6458
6612
|
if (e) {
|
|
6459
|
-
|
|
6613
|
+
log14.debug("notification failed", e);
|
|
6460
6614
|
resolve4(`Notification failed: ${e.message}`);
|
|
6461
6615
|
} else resolve4("Notification shown.");
|
|
6462
6616
|
});
|
|
@@ -6472,7 +6626,7 @@ import { BodDB as BodDB2 } from "@bod.ee/db";
|
|
|
6472
6626
|
init_logging();
|
|
6473
6627
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
6474
6628
|
import { homedir } from "os";
|
|
6475
|
-
var
|
|
6629
|
+
var log15 = forComponent("cli-util");
|
|
6476
6630
|
function dotDirs(base, sub, opts = {}) {
|
|
6477
6631
|
const home = opts.home ?? homedir();
|
|
6478
6632
|
const dirs = [`${base}/.agent/${sub}`, `${base}/.claude/${sub}`, `${home}/.agent/${sub}`, `${home}/.claude/${sub}`];
|
|
@@ -6489,7 +6643,7 @@ function parseJson(text, fallback, what = "json") {
|
|
|
6489
6643
|
try {
|
|
6490
6644
|
return JSON.parse(text);
|
|
6491
6645
|
} catch (e) {
|
|
6492
|
-
|
|
6646
|
+
log15.debug(`parseJson(${what}) failed: ${e.message}`);
|
|
6493
6647
|
return fallback;
|
|
6494
6648
|
}
|
|
6495
6649
|
}
|
|
@@ -6499,7 +6653,7 @@ function readJsonFile(path, fallback) {
|
|
|
6499
6653
|
try {
|
|
6500
6654
|
text = readFileSync3(path, "utf8");
|
|
6501
6655
|
} catch (e) {
|
|
6502
|
-
|
|
6656
|
+
log15.debug(`readJsonFile(${path}) unreadable: ${e.message}`);
|
|
6503
6657
|
return fallback;
|
|
6504
6658
|
}
|
|
6505
6659
|
return parseJson(text, fallback, path);
|
|
@@ -6583,8 +6737,8 @@ async function buildAgent(o) {
|
|
|
6583
6737
|
fs = mem;
|
|
6584
6738
|
} else if (o.boddb) {
|
|
6585
6739
|
const root = resolve(o.boddb);
|
|
6586
|
-
|
|
6587
|
-
const db = new BodDB2({ path:
|
|
6740
|
+
mkdirSync4(root, { recursive: true });
|
|
6741
|
+
const db = new BodDB2({ path: join5(root, "meta.db"), sweepInterval: 0, vfs: { storageRoot: join5(root, "files") } });
|
|
6588
6742
|
const bod = new BodDbFilesystem(db);
|
|
6589
6743
|
const firstRun = (await bod.readDir("/").catch(() => [])).length === 0;
|
|
6590
6744
|
await mkdirp(bod, cwd);
|
|
@@ -6711,6 +6865,9 @@ The filesystem root '/' is the real machine root \u2014 you have full filesystem
|
|
|
6711
6865
|
...scratch ? { capToolResult: (full, info) => scratch.spill(full, info) } : {},
|
|
6712
6866
|
backgroundJobs: o.backgroundJobs ?? virtual,
|
|
6713
6867
|
// default ON in virtual modes (no real shell there); disk uses ShellJobRegistry
|
|
6868
|
+
// Subagent transcript traces, origin-anchored under .agent/traces (disk mode only — sandbox/db modes
|
|
6869
|
+
// must never write the real disk). Lets an interrupted/failed child leave a readable trail + pointer.
|
|
6870
|
+
...!virtual ? { traceDir: `${cwd}/.agent/traces` } : {},
|
|
6714
6871
|
skillsDir: dots("skills"),
|
|
6715
6872
|
commandsDir: dots("commands"),
|
|
6716
6873
|
memoryDir,
|
|
@@ -6744,11 +6901,11 @@ var trunc = (s, n) => (s == null ? "" : String(s).length > n ? String(s).slice(0
|
|
|
6744
6901
|
// cli/voice.ts
|
|
6745
6902
|
init_logging();
|
|
6746
6903
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
6747
|
-
import { existsSync as existsSync4, mkdirSync as
|
|
6904
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, statSync as statSync3 } from "fs";
|
|
6748
6905
|
import { homedir as homedir3 } from "os";
|
|
6749
|
-
import { dirname as dirname3, join as
|
|
6906
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
6750
6907
|
import { fileURLToPath } from "url";
|
|
6751
|
-
var
|
|
6908
|
+
var log16 = forComponent("VoiceIO");
|
|
6752
6909
|
var now4 = () => performance.now();
|
|
6753
6910
|
var Player = class {
|
|
6754
6911
|
proc = null;
|
|
@@ -6762,7 +6919,7 @@ var Player = class {
|
|
|
6762
6919
|
["-loglevel", "quiet", "-nodisp", "-fflags", "nobuffer", "-flags", "low_delay", "-probesize", "32", "-f", "s16le", "-ar", String(TTS_SAMPLE_RATE), "-ch_layout", "mono", "-i", "-"],
|
|
6763
6920
|
{ stdio: ["pipe", "ignore", "ignore"] }
|
|
6764
6921
|
);
|
|
6765
|
-
this.proc.on("error", (e) =>
|
|
6922
|
+
this.proc.on("error", (e) => log16.warn(`ffplay error: ${e.message}`));
|
|
6766
6923
|
this.proc.stdin.on("error", () => {
|
|
6767
6924
|
});
|
|
6768
6925
|
this.bytesWritten = 0;
|
|
@@ -6789,7 +6946,7 @@ var Player = class {
|
|
|
6789
6946
|
this.proc = null;
|
|
6790
6947
|
}
|
|
6791
6948
|
};
|
|
6792
|
-
var nativeDir = () =>
|
|
6949
|
+
var nativeDir = () => join6(dirname3(fileURLToPath(import.meta.url)), "native");
|
|
6793
6950
|
function detectFfmpegMic() {
|
|
6794
6951
|
if (process.env.MIC_DEVICE) return process.env.MIC_DEVICE;
|
|
6795
6952
|
const out = spawnSync2("ffmpeg", ["-f", "avfoundation", "-list_devices", "true", "-i", ""], { encoding: "utf8" }).stderr;
|
|
@@ -6797,7 +6954,7 @@ function detectFfmpegMic() {
|
|
|
6797
6954
|
const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
|
|
6798
6955
|
const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
|
|
6799
6956
|
if (!mic) throw new Error("no audio input device found");
|
|
6800
|
-
|
|
6957
|
+
log16.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
|
|
6801
6958
|
return `:${mic.idx}`;
|
|
6802
6959
|
}
|
|
6803
6960
|
function detectedInputDevice() {
|
|
@@ -6825,23 +6982,23 @@ function detectedInputDevice() {
|
|
|
6825
6982
|
}
|
|
6826
6983
|
function resolveAecBinary() {
|
|
6827
6984
|
if (process.env.MIC_AEC === "0" || process.platform !== "darwin") return null;
|
|
6828
|
-
const src =
|
|
6829
|
-
const plist =
|
|
6985
|
+
const src = join6(nativeDir(), "mic-aec.swift");
|
|
6986
|
+
const plist = join6(nativeDir(), "Info.plist");
|
|
6830
6987
|
if (!existsSync4(src)) return null;
|
|
6831
|
-
const cacheDir =
|
|
6832
|
-
const bin =
|
|
6988
|
+
const cacheDir = join6(homedir3(), ".agent", "cache");
|
|
6989
|
+
const bin = join6(cacheDir, "mic-aec");
|
|
6833
6990
|
if (existsSync4(bin) && statSync3(bin).mtimeMs >= statSync3(src).mtimeMs) return bin;
|
|
6834
6991
|
if (spawnSync2("which", ["swiftc"]).status !== 0) return null;
|
|
6835
|
-
|
|
6836
|
-
|
|
6992
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
6993
|
+
log16.info("compiling AEC mic helper (first run)\u2026");
|
|
6837
6994
|
const build = spawnSync2("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
|
|
6838
6995
|
if (build.status !== 0) {
|
|
6839
|
-
|
|
6996
|
+
log16.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
|
|
6840
6997
|
return null;
|
|
6841
6998
|
}
|
|
6842
6999
|
const sign = spawnSync2("codesign", ["-fs", "-", bin], { encoding: "utf8" });
|
|
6843
7000
|
if (sign.status !== 0) {
|
|
6844
|
-
|
|
7001
|
+
log16.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
|
|
6845
7002
|
return null;
|
|
6846
7003
|
}
|
|
6847
7004
|
return bin;
|
|
@@ -6860,16 +7017,16 @@ var NodeMicSource = class {
|
|
|
6860
7017
|
this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
|
|
6861
7018
|
} else {
|
|
6862
7019
|
if (spawnSync2("which", ["ffmpeg"]).status !== 0) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
|
|
6863
|
-
|
|
7020
|
+
log16.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
|
|
6864
7021
|
this.proc = spawn2(
|
|
6865
7022
|
"ffmpeg",
|
|
6866
7023
|
["-loglevel", "error", "-f", "avfoundation", "-i", detectFfmpegMic(), "-ar", String(STT_SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"],
|
|
6867
7024
|
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
6868
7025
|
);
|
|
6869
|
-
this.proc.stderr.on("data", (d) =>
|
|
7026
|
+
this.proc.stderr.on("data", (d) => log16.warn(`ffmpeg: ${String(d).trim()}`));
|
|
6870
7027
|
}
|
|
6871
7028
|
this.proc.on("exit", (c) => {
|
|
6872
|
-
if (c && !this.stopped)
|
|
7029
|
+
if (c && !this.stopped) log16.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
|
|
6873
7030
|
});
|
|
6874
7031
|
this.proc.stdout.on("data", (chunk) => onChunk(chunk));
|
|
6875
7032
|
}
|
|
@@ -6903,11 +7060,11 @@ var AecDuplexAudio = class {
|
|
|
6903
7060
|
this.proc.stdin.on("error", () => {
|
|
6904
7061
|
});
|
|
6905
7062
|
this.proc.on("exit", (c) => {
|
|
6906
|
-
if (c && !this.stopped)
|
|
7063
|
+
if (c && !this.stopped) log16.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
|
|
6907
7064
|
});
|
|
6908
7065
|
this.proc.stdout.on("data", (chunk) => onChunk(chunk));
|
|
6909
7066
|
this.proc.stderr.on("data", (d) => {
|
|
6910
|
-
for (const ln of String(d).split("\n")) if (ln.trim())
|
|
7067
|
+
for (const ln of String(d).split("\n")) if (ln.trim()) log16.debug(`mic-aec: ${ln.trim()}`);
|
|
6911
7068
|
});
|
|
6912
7069
|
}
|
|
6913
7070
|
stop() {
|
|
@@ -7029,12 +7186,12 @@ var VoiceIO = class extends VoiceEngine {
|
|
|
7029
7186
|
// cli/config.ts
|
|
7030
7187
|
import { homedir as homedir4 } from "os";
|
|
7031
7188
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
7032
|
-
import { join as
|
|
7189
|
+
import { join as join7 } from "path";
|
|
7033
7190
|
import { pathToFileURL } from "url";
|
|
7034
7191
|
var FILES = ["config.ts", "config.js", "config.mjs", "config.json"];
|
|
7035
7192
|
async function loadFrom(dir) {
|
|
7036
7193
|
for (const f of FILES) {
|
|
7037
|
-
const p =
|
|
7194
|
+
const p = join7(dir, ".agent", f);
|
|
7038
7195
|
if (!existsSync5(p)) continue;
|
|
7039
7196
|
try {
|
|
7040
7197
|
const mod = await import(pathToFileURL(p).href, f.endsWith(".json") ? { with: { type: "json" } } : void 0);
|
|
@@ -7047,7 +7204,7 @@ async function loadFrom(dir) {
|
|
|
7047
7204
|
return {};
|
|
7048
7205
|
}
|
|
7049
7206
|
function loadSettings(dir) {
|
|
7050
|
-
const p =
|
|
7207
|
+
const p = join7(dir, ".agent", "settings.json");
|
|
7051
7208
|
if (!existsSync5(p)) return {};
|
|
7052
7209
|
try {
|
|
7053
7210
|
const raw = JSON.parse(readFileSync4(p, "utf8"));
|
|
@@ -7084,7 +7241,7 @@ async function loadConfig(cwd) {
|
|
|
7084
7241
|
|
|
7085
7242
|
// cli/hooks-config.ts
|
|
7086
7243
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7087
|
-
var
|
|
7244
|
+
var log17 = forComponent("hooks");
|
|
7088
7245
|
var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
7089
7246
|
function ruleMatches(rule, toolName) {
|
|
7090
7247
|
if (!rule.tool || rule.tool === "*") return true;
|
|
@@ -7101,7 +7258,7 @@ function runCmd(rule, env) {
|
|
|
7101
7258
|
});
|
|
7102
7259
|
return { code: r.status ?? 1, out: ((r.stdout ?? "") + (r.stderr ?? "")).trim() };
|
|
7103
7260
|
} catch (e) {
|
|
7104
|
-
|
|
7261
|
+
log17.debug(`hook command failed: ${rule.command}`, e);
|
|
7105
7262
|
return { code: 1, out: String(e?.message ?? e) };
|
|
7106
7263
|
}
|
|
7107
7264
|
}
|
|
@@ -7205,15 +7362,15 @@ function formatDiff(ops, opts = {}) {
|
|
|
7205
7362
|
}
|
|
7206
7363
|
|
|
7207
7364
|
// cli/session.ts
|
|
7208
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
7365
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4, readdirSync, renameSync, symlinkSync as symlinkSync2, unlinkSync, readlinkSync } from "fs";
|
|
7209
7366
|
import { homedir as homedir5 } from "os";
|
|
7210
|
-
import { join as
|
|
7211
|
-
var
|
|
7212
|
-
var globalDir = () =>
|
|
7367
|
+
import { join as join8 } from "path";
|
|
7368
|
+
var log18 = forComponent("session");
|
|
7369
|
+
var globalDir = () => join8(homedir5(), ".agent", "sessions");
|
|
7213
7370
|
var SessionStore = class {
|
|
7214
7371
|
dir;
|
|
7215
7372
|
constructor(cwd) {
|
|
7216
|
-
this.dir =
|
|
7373
|
+
this.dir = join8(cwd, ".agent", "sessions");
|
|
7217
7374
|
}
|
|
7218
7375
|
/** Sortable, human-readable id: `YYYYMMDD-HHMMSS-<folder>`. */
|
|
7219
7376
|
newId(now5 = Date.now(), cwd) {
|
|
@@ -7221,10 +7378,10 @@ var SessionStore = class {
|
|
|
7221
7378
|
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
7222
7379
|
const slug2 = (cwd ?? process.cwd()).split("/").pop()?.replace(/[^A-Za-z0-9_-]/g, "") || "session";
|
|
7223
7380
|
let id = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}-${slug2}`;
|
|
7224
|
-
if (existsSync6(this.dir) && existsSync6(
|
|
7381
|
+
if (existsSync6(this.dir) && existsSync6(join8(this.dir, `${id}.json`))) {
|
|
7225
7382
|
for (let i = 2; i <= 99; i++) {
|
|
7226
7383
|
const c = `${id}-${i}`;
|
|
7227
|
-
if (!existsSync6(
|
|
7384
|
+
if (!existsSync6(join8(this.dir, `${c}.json`))) {
|
|
7228
7385
|
id = c;
|
|
7229
7386
|
break;
|
|
7230
7387
|
}
|
|
@@ -7238,30 +7395,30 @@ var SessionStore = class {
|
|
|
7238
7395
|
}
|
|
7239
7396
|
save(data) {
|
|
7240
7397
|
if (!this.safeId(data.meta.id)) throw new Error(`unsafe session id: ${data.meta.id}`);
|
|
7241
|
-
if (!existsSync6(this.dir))
|
|
7242
|
-
const path =
|
|
7398
|
+
if (!existsSync6(this.dir)) mkdirSync6(this.dir, { recursive: true });
|
|
7399
|
+
const path = join8(this.dir, `${data.meta.id}.json`);
|
|
7243
7400
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
7244
|
-
|
|
7401
|
+
writeFileSync4(tmp, JSON.stringify(data));
|
|
7245
7402
|
renameSync(tmp, path);
|
|
7246
7403
|
try {
|
|
7247
7404
|
const gd = globalDir();
|
|
7248
|
-
if (!existsSync6(gd))
|
|
7249
|
-
const link2 =
|
|
7405
|
+
if (!existsSync6(gd)) mkdirSync6(gd, { recursive: true });
|
|
7406
|
+
const link2 = join8(gd, `${data.meta.id}.json`);
|
|
7250
7407
|
if (!existsSync6(link2)) symlinkSync2(path, link2);
|
|
7251
7408
|
} catch {
|
|
7252
7409
|
}
|
|
7253
7410
|
}
|
|
7254
7411
|
load(id) {
|
|
7255
7412
|
if (!this.safeId(id)) {
|
|
7256
|
-
|
|
7413
|
+
log18.debug(`rejecting unsafe session id: ${id}`);
|
|
7257
7414
|
return void 0;
|
|
7258
7415
|
}
|
|
7259
|
-
const path =
|
|
7416
|
+
const path = join8(this.dir, `${id}.json`);
|
|
7260
7417
|
if (!existsSync6(path)) return void 0;
|
|
7261
7418
|
try {
|
|
7262
7419
|
return JSON.parse(readFileSync5(path, "utf8"));
|
|
7263
7420
|
} catch (e) {
|
|
7264
|
-
|
|
7421
|
+
log18.debug(`unreadable session ${id} \u2014 ignoring`, e);
|
|
7265
7422
|
return void 0;
|
|
7266
7423
|
}
|
|
7267
7424
|
}
|
|
@@ -7272,9 +7429,9 @@ var SessionStore = class {
|
|
|
7272
7429
|
for (const f of readdirSync(this.dir)) {
|
|
7273
7430
|
if (!f.endsWith(".json")) continue;
|
|
7274
7431
|
try {
|
|
7275
|
-
metas.push(JSON.parse(readFileSync5(
|
|
7432
|
+
metas.push(JSON.parse(readFileSync5(join8(this.dir, f), "utf8")).meta);
|
|
7276
7433
|
} catch (e) {
|
|
7277
|
-
|
|
7434
|
+
log18.debug(`skipping unreadable session file ${f}`, e);
|
|
7278
7435
|
}
|
|
7279
7436
|
}
|
|
7280
7437
|
return metas.sort((a, b) => b.updated - a.updated);
|
|
@@ -7291,7 +7448,7 @@ var SessionStore = class {
|
|
|
7291
7448
|
function globalSessionLoad(idOrPrefix) {
|
|
7292
7449
|
const gd = globalDir();
|
|
7293
7450
|
if (!existsSync6(gd)) return void 0;
|
|
7294
|
-
const exact =
|
|
7451
|
+
const exact = join8(gd, `${idOrPrefix}.json`);
|
|
7295
7452
|
if (existsSync6(exact)) {
|
|
7296
7453
|
try {
|
|
7297
7454
|
const target = readlinkSync(exact);
|
|
@@ -7305,7 +7462,7 @@ function globalSessionLoad(idOrPrefix) {
|
|
|
7305
7462
|
if (!f.endsWith(".json")) continue;
|
|
7306
7463
|
const base = f.slice(0, -5);
|
|
7307
7464
|
if (base.includes(idOrPrefix) || base.endsWith(idOrPrefix)) {
|
|
7308
|
-
const target = readlinkSync(
|
|
7465
|
+
const target = readlinkSync(join8(gd, f));
|
|
7309
7466
|
return JSON.parse(readFileSync5(target, "utf8"));
|
|
7310
7467
|
}
|
|
7311
7468
|
}
|
|
@@ -7320,10 +7477,10 @@ function globalSessionList() {
|
|
|
7320
7477
|
for (const f of readdirSync(gd)) {
|
|
7321
7478
|
if (!f.endsWith(".json")) continue;
|
|
7322
7479
|
try {
|
|
7323
|
-
const target = readlinkSync(
|
|
7480
|
+
const target = readlinkSync(join8(gd, f));
|
|
7324
7481
|
if (!existsSync6(target)) {
|
|
7325
7482
|
try {
|
|
7326
|
-
unlinkSync(
|
|
7483
|
+
unlinkSync(join8(gd, f));
|
|
7327
7484
|
} catch {
|
|
7328
7485
|
}
|
|
7329
7486
|
continue;
|
|
@@ -7440,9 +7597,9 @@ ${formatDiff(ops)}`);
|
|
|
7440
7597
|
// cli/gitCheckpoints.ts
|
|
7441
7598
|
import { execFile as execFile3 } from "child_process";
|
|
7442
7599
|
import { promisify } from "util";
|
|
7443
|
-
import { writeFileSync as
|
|
7444
|
-
import { join as
|
|
7445
|
-
var
|
|
7600
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync7, existsSync as existsSync7 } from "fs";
|
|
7601
|
+
import { join as join9, resolve as resolve2, sep as sep2 } from "path";
|
|
7602
|
+
var log19 = forComponent("checkpoints");
|
|
7446
7603
|
var exec = promisify(execFile3);
|
|
7447
7604
|
var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
|
|
7448
7605
|
var ShadowRepo = class {
|
|
@@ -7474,13 +7631,13 @@ var ShadowRepo = class {
|
|
|
7474
7631
|
try {
|
|
7475
7632
|
await exec(this.git, ["--version"]);
|
|
7476
7633
|
if (!existsSync7(this.gitDir)) {
|
|
7477
|
-
|
|
7634
|
+
mkdirSync7(this.gitDir, { recursive: true });
|
|
7478
7635
|
await this.run("init", "-q");
|
|
7479
7636
|
}
|
|
7480
|
-
|
|
7637
|
+
writeFileSync5(join9(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
|
|
7481
7638
|
this.ready = true;
|
|
7482
7639
|
} catch (e) {
|
|
7483
|
-
|
|
7640
|
+
log19.debug(`git checkpoints unavailable for ${this.workTree}`, e);
|
|
7484
7641
|
this.ready = false;
|
|
7485
7642
|
}
|
|
7486
7643
|
return this.ready;
|
|
@@ -7491,7 +7648,7 @@ var ShadowRepo = class {
|
|
|
7491
7648
|
}
|
|
7492
7649
|
async commit(label, forced = []) {
|
|
7493
7650
|
await this.run("add", "-A");
|
|
7494
|
-
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) =>
|
|
7651
|
+
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) => log19.debug(`force-add failed: ${p}`, e));
|
|
7495
7652
|
await this.run("commit", "--allow-empty", "-q", "-m", label);
|
|
7496
7653
|
}
|
|
7497
7654
|
/** Inject the CURRENT (pre-edit) content of `paths` into the turn-open restore point by amending it.
|
|
@@ -7499,8 +7656,8 @@ var ShadowRepo = class {
|
|
|
7499
7656
|
* turn-boundary `add -A` would never have captured it. Amend (vs a new commit) keeps one restore
|
|
7500
7657
|
* point per turn, so the REPL's turn↔frame mapping stays intact. */
|
|
7501
7658
|
async amendForced(paths) {
|
|
7502
|
-
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) =>
|
|
7503
|
-
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) =>
|
|
7659
|
+
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) => log19.debug(`force-capture failed: ${p}`, e));
|
|
7660
|
+
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) => log19.debug("amend failed", e));
|
|
7504
7661
|
}
|
|
7505
7662
|
/** Commits on `ref`, oldest-first (canonical index space). */
|
|
7506
7663
|
async log(ref) {
|
|
@@ -7560,7 +7717,7 @@ var ShadowRepo = class {
|
|
|
7560
7717
|
await this.run("gc", "--auto", "-q").catch(() => {
|
|
7561
7718
|
});
|
|
7562
7719
|
} catch (e) {
|
|
7563
|
-
|
|
7720
|
+
log19.debug("checkpoint prune failed", e);
|
|
7564
7721
|
}
|
|
7565
7722
|
}
|
|
7566
7723
|
};
|
|
@@ -7592,7 +7749,7 @@ var GitCheckpoints = class {
|
|
|
7592
7749
|
const abs = resolve2(d);
|
|
7593
7750
|
if (abs === cwd || abs.startsWith(cwd + sep2)) continue;
|
|
7594
7751
|
if (cwd.startsWith(abs + sep2)) continue;
|
|
7595
|
-
out.push({ workTree: abs, gitDir:
|
|
7752
|
+
out.push({ workTree: abs, gitDir: join9(abs, ".agent", "checkpoints.git") });
|
|
7596
7753
|
}
|
|
7597
7754
|
return out;
|
|
7598
7755
|
}
|
|
@@ -7619,7 +7776,7 @@ var GitCheckpoints = class {
|
|
|
7619
7776
|
use(sessionId) {
|
|
7620
7777
|
if (sessionId === this.session) return;
|
|
7621
7778
|
this.session = sessionId;
|
|
7622
|
-
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) =>
|
|
7779
|
+
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log19.debug("re-point failed", e));
|
|
7623
7780
|
}
|
|
7624
7781
|
async begin(label) {
|
|
7625
7782
|
if (!await this.start()) return;
|
|
@@ -7631,7 +7788,7 @@ var GitCheckpoints = class {
|
|
|
7631
7788
|
try {
|
|
7632
7789
|
await this.repos[i].commit(msg, forced);
|
|
7633
7790
|
} catch (e) {
|
|
7634
|
-
|
|
7791
|
+
log19.debug("checkpoint commit failed", e);
|
|
7635
7792
|
}
|
|
7636
7793
|
}
|
|
7637
7794
|
if (slow) clearTimeout(slow);
|
|
@@ -7661,7 +7818,7 @@ var GitCheckpoints = class {
|
|
|
7661
7818
|
if (this.forced.has(abs)) continue;
|
|
7662
7819
|
this.forced.add(abs);
|
|
7663
7820
|
const i = this.repoIndexFor(abs);
|
|
7664
|
-
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) =>
|
|
7821
|
+
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) => log19.debug("amendForced failed", e));
|
|
7665
7822
|
}
|
|
7666
7823
|
}
|
|
7667
7824
|
};
|
|
@@ -7713,7 +7870,7 @@ var GitCheckpointsOptions = class {
|
|
|
7713
7870
|
/** Real working tree to snapshot (the launch cwd). */
|
|
7714
7871
|
workTree = process.cwd();
|
|
7715
7872
|
/** Isolated git dir for the cwd shadow repo (kept out of the user's real .git). */
|
|
7716
|
-
gitDir =
|
|
7873
|
+
gitDir = join9(process.cwd(), ".agent", "checkpoints.git");
|
|
7717
7874
|
/** Extra mounted dirs (`--add-dir`); those outside cwd each get their own shadow repo. */
|
|
7718
7875
|
addDirs = [];
|
|
7719
7876
|
/** Conversation id → per-session restore-point ref. */
|
|
@@ -7727,9 +7884,9 @@ var GitCheckpointsOptions = class {
|
|
|
7727
7884
|
};
|
|
7728
7885
|
|
|
7729
7886
|
// cli/permissions.ts
|
|
7730
|
-
import { writeFileSync as
|
|
7887
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "fs";
|
|
7731
7888
|
import { homedir as homedir6 } from "os";
|
|
7732
|
-
import { join as
|
|
7889
|
+
import { join as join10 } from "path";
|
|
7733
7890
|
var RULE_RE = /^(\w+)(?:\((.+)\))?$/;
|
|
7734
7891
|
function parseOne(raw, decision) {
|
|
7735
7892
|
const m = RULE_RE.exec(raw.trim());
|
|
@@ -7751,13 +7908,13 @@ function parsePermRules(perms) {
|
|
|
7751
7908
|
function describeRule(r) {
|
|
7752
7909
|
return `${r.decision.padEnd(5)} ${r.tool ?? "*"}${r.pathGlob ? `(${r.pathGlob})` : ""}`;
|
|
7753
7910
|
}
|
|
7754
|
-
var PERM_FILE = (cwd) =>
|
|
7911
|
+
var PERM_FILE = (cwd) => join10(cwd, ".agent", "permissions.json");
|
|
7755
7912
|
function loadPersistedRules(cwd) {
|
|
7756
7913
|
const j = readJsonFile(PERM_FILE(cwd), null);
|
|
7757
7914
|
return j ? { allow: j.allow ?? [], ask: j.ask ?? [], deny: j.deny ?? [] } : {};
|
|
7758
7915
|
}
|
|
7759
7916
|
function loadClaudeSettings(cwd, home = homedir6()) {
|
|
7760
|
-
const files = [
|
|
7917
|
+
const files = [join10(home, ".claude", "settings.json"), join10(cwd, ".claude", "settings.json"), join10(cwd, ".claude", "settings.local.json")];
|
|
7761
7918
|
let out = {};
|
|
7762
7919
|
for (const p of files) {
|
|
7763
7920
|
const perms = readJsonFile(p, null)?.permissions;
|
|
@@ -7770,8 +7927,8 @@ function persistRule(cwd, decision, ruleStr) {
|
|
|
7770
7927
|
const list = cur[decision] ??= [];
|
|
7771
7928
|
if (!list.includes(ruleStr)) list.push(ruleStr);
|
|
7772
7929
|
try {
|
|
7773
|
-
|
|
7774
|
-
|
|
7930
|
+
mkdirSync8(join10(cwd, ".agent"), { recursive: true });
|
|
7931
|
+
writeFileSync6(PERM_FILE(cwd), JSON.stringify(cur, null, 2) + "\n");
|
|
7775
7932
|
} catch {
|
|
7776
7933
|
}
|
|
7777
7934
|
}
|
|
@@ -7784,7 +7941,7 @@ function mergePerms(a, b) {
|
|
|
7784
7941
|
}
|
|
7785
7942
|
return Object.keys(out).length ? out : void 0;
|
|
7786
7943
|
}
|
|
7787
|
-
var TRUST_FILE =
|
|
7944
|
+
var TRUST_FILE = join10(homedir6(), ".agent", "trusted.json");
|
|
7788
7945
|
function isTrusted(cwd, file = TRUST_FILE) {
|
|
7789
7946
|
const list = readJsonFile(file, []);
|
|
7790
7947
|
return Array.isArray(list) && list.includes(cwd);
|
|
@@ -7794,8 +7951,8 @@ function trustDir(cwd, file = TRUST_FILE) {
|
|
|
7794
7951
|
if (!Array.isArray(list)) list = [];
|
|
7795
7952
|
if (!list.includes(cwd)) list.push(cwd);
|
|
7796
7953
|
try {
|
|
7797
|
-
|
|
7798
|
-
|
|
7954
|
+
mkdirSync8(join10(file, ".."), { recursive: true });
|
|
7955
|
+
writeFileSync6(file, JSON.stringify(list, null, 2) + "\n");
|
|
7799
7956
|
} catch {
|
|
7800
7957
|
}
|
|
7801
7958
|
}
|
|
@@ -7855,9 +8012,9 @@ function completePath(listDir, ref) {
|
|
|
7855
8012
|
// cli/lineEditor.ts
|
|
7856
8013
|
import { emitKeypressEvents } from "readline";
|
|
7857
8014
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
7858
|
-
import { writeFileSync as
|
|
8015
|
+
import { writeFileSync as writeFileSync7, readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
7859
8016
|
import { tmpdir as tmpdir2 } from "os";
|
|
7860
|
-
import { join as
|
|
8017
|
+
import { join as join11 } from "path";
|
|
7861
8018
|
|
|
7862
8019
|
// cli/bidi.ts
|
|
7863
8020
|
var RTL_RE = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufdff\ufe70-\ufeff]/;
|
|
@@ -8934,9 +9091,9 @@ function createLineEditor(out) {
|
|
|
8934
9091
|
function externalEdit(s) {
|
|
8935
9092
|
const spec = process.env.VISUAL || process.env.EDITOR || "vi";
|
|
8936
9093
|
const [cmd, ...cargs] = spec.split(" ").filter(Boolean);
|
|
8937
|
-
const file =
|
|
9094
|
+
const file = join11(tmpdir2(), `agentx-edit-${process.pid}-${Date.now()}.md`);
|
|
8938
9095
|
try {
|
|
8939
|
-
|
|
9096
|
+
writeFileSync7(file, s.buf);
|
|
8940
9097
|
process.stdin.setRawMode(false);
|
|
8941
9098
|
out.write("\x1B[?2004l");
|
|
8942
9099
|
const r = spawnSync4(cmd, [...cargs, file], { stdio: "inherit" });
|
|
@@ -9364,23 +9521,23 @@ function readPlainLine() {
|
|
|
9364
9521
|
|
|
9365
9522
|
// cli/osScheduler.ts
|
|
9366
9523
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
9367
|
-
import { writeFileSync as
|
|
9524
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync8 } from "fs";
|
|
9368
9525
|
import { homedir as homedir7 } from "os";
|
|
9369
|
-
import { join as
|
|
9370
|
-
var
|
|
9526
|
+
import { join as join12 } from "path";
|
|
9527
|
+
var log20 = forComponent("os-sched");
|
|
9371
9528
|
var OsScheduler = class {
|
|
9372
9529
|
options;
|
|
9373
9530
|
constructor(options) {
|
|
9374
9531
|
this.options = { ...new OsSchedulerOptions(), ...options };
|
|
9375
9532
|
}
|
|
9376
9533
|
get dir() {
|
|
9377
|
-
return
|
|
9534
|
+
return join12(this.options.home, ".agent", "sched");
|
|
9378
9535
|
}
|
|
9379
9536
|
label(id) {
|
|
9380
9537
|
return `cc.livx.agentx.sched-${id}`;
|
|
9381
9538
|
}
|
|
9382
9539
|
plistPath(id) {
|
|
9383
|
-
return
|
|
9540
|
+
return join12(this.options.home, "Library", "LaunchAgents", `${this.label(id)}.plist`);
|
|
9384
9541
|
}
|
|
9385
9542
|
run(cmd, args, input) {
|
|
9386
9543
|
return this.options.exec(cmd, args, input);
|
|
@@ -9391,16 +9548,16 @@ var OsScheduler = class {
|
|
|
9391
9548
|
/** Register the job with the OS. Returns a human description of the mechanism used. Throws on failure. */
|
|
9392
9549
|
schedule(spec) {
|
|
9393
9550
|
if (!this.available()) throw new Error(`no OS scheduler on ${this.options.platform}`);
|
|
9394
|
-
|
|
9551
|
+
mkdirSync9(this.dir, { recursive: true });
|
|
9395
9552
|
const oneOff = "at" in spec.trigger;
|
|
9396
9553
|
const script = this.writeScript(spec, oneOff);
|
|
9397
9554
|
const mechanism = this.options.platform === "darwin" ? this.scheduleDarwin(spec, script) : this.scheduleLinux(spec, script, oneOff);
|
|
9398
9555
|
const meta = { ...spec, created: Date.now(), mechanism };
|
|
9399
|
-
|
|
9556
|
+
writeFileSync8(join12(this.dir, `${spec.id}.json`), JSON.stringify(meta, null, 2));
|
|
9400
9557
|
return mechanism;
|
|
9401
9558
|
}
|
|
9402
9559
|
cancel(id) {
|
|
9403
|
-
const meta = readJsonFile(
|
|
9560
|
+
const meta = readJsonFile(join12(this.dir, `${id}.json`), null);
|
|
9404
9561
|
if (!meta) return false;
|
|
9405
9562
|
try {
|
|
9406
9563
|
if (this.options.platform === "darwin") {
|
|
@@ -9429,11 +9586,11 @@ var OsScheduler = class {
|
|
|
9429
9586
|
}
|
|
9430
9587
|
}
|
|
9431
9588
|
} catch (e) {
|
|
9432
|
-
|
|
9589
|
+
log20.debug(`cancel ${id}`, e);
|
|
9433
9590
|
}
|
|
9434
9591
|
for (const f of [`${id}.json`, `${id}.sh`]) {
|
|
9435
9592
|
try {
|
|
9436
|
-
unlinkSync3(
|
|
9593
|
+
unlinkSync3(join12(this.dir, f));
|
|
9437
9594
|
} catch {
|
|
9438
9595
|
}
|
|
9439
9596
|
}
|
|
@@ -9441,19 +9598,19 @@ var OsScheduler = class {
|
|
|
9441
9598
|
}
|
|
9442
9599
|
list() {
|
|
9443
9600
|
if (!existsSync8(this.dir)) return [];
|
|
9444
|
-
return readdirSync2(this.dir).filter((f) => f.endsWith(".json")).map((f) => readJsonFile(
|
|
9601
|
+
return readdirSync2(this.dir).filter((f) => f.endsWith(".json")).map((f) => readJsonFile(join12(this.dir, f), null)).filter(Boolean);
|
|
9445
9602
|
}
|
|
9446
9603
|
/** The per-job runner script: cd to the project, headless-resume the session, log, notify. */
|
|
9447
9604
|
writeScript(spec, oneOff) {
|
|
9448
|
-
const p =
|
|
9605
|
+
const p = join12(this.dir, `${spec.id}.sh`);
|
|
9449
9606
|
const q2 = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
9450
|
-
const cleanup = oneOff ? this.options.platform === "darwin" ? `launchctl remove ${this.label(spec.id)} 2>/dev/null; rm -f ${q2(this.plistPath(spec.id))} ${q2(
|
|
9451
|
-
` : `rm -f ${q2(
|
|
9607
|
+
const cleanup = oneOff ? this.options.platform === "darwin" ? `launchctl remove ${this.label(spec.id)} 2>/dev/null; rm -f ${q2(this.plistPath(spec.id))} ${q2(join12(this.dir, `${spec.id}.json`))} ${q2(p)}
|
|
9608
|
+
` : `rm -f ${q2(join12(this.dir, `${spec.id}.json`))} ${q2(p)}
|
|
9452
9609
|
` : "";
|
|
9453
|
-
|
|
9610
|
+
writeFileSync8(p, `#!/bin/sh
|
|
9454
9611
|
# agentx scheduled job ${spec.id}${spec.label ? ` \u2014 ${spec.label}` : ""}
|
|
9455
9612
|
cd ${q2(spec.cwd)} || exit 1
|
|
9456
|
-
${this.options.agentx} -p ${q2(spec.prompt)} --resume ${q2(spec.sessionId)} --yes >> ${q2(
|
|
9613
|
+
${this.options.agentx} -p ${q2(spec.prompt)} --resume ${q2(spec.sessionId)} --yes >> ${q2(join12(this.dir, `${spec.id}.log`))} 2>&1
|
|
9457
9614
|
${cleanup}`);
|
|
9458
9615
|
chmodSync(p, 493);
|
|
9459
9616
|
return p;
|
|
@@ -9490,8 +9647,8 @@ ${trigger}
|
|
|
9490
9647
|
<key>RunAtLoad</key><false/>
|
|
9491
9648
|
</dict></plist>
|
|
9492
9649
|
`;
|
|
9493
|
-
|
|
9494
|
-
|
|
9650
|
+
mkdirSync9(join12(this.options.home, "Library", "LaunchAgents"), { recursive: true });
|
|
9651
|
+
writeFileSync8(this.plistPath(spec.id), plist);
|
|
9495
9652
|
this.run("launchctl", ["load", this.plistPath(spec.id)]);
|
|
9496
9653
|
return `launchd:${this.label(spec.id)}`;
|
|
9497
9654
|
}
|
|
@@ -9543,12 +9700,12 @@ function routeTrigger(trigger, backendHint, now5 = Date.now()) {
|
|
|
9543
9700
|
// cli/remoteTrigger.ts
|
|
9544
9701
|
import { createServer as createServer2, createConnection } from "net";
|
|
9545
9702
|
import { spawn as spawn3 } from "child_process";
|
|
9546
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
9703
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync10, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
|
|
9547
9704
|
import { homedir as homedir8 } from "os";
|
|
9548
|
-
import { join as
|
|
9549
|
-
var
|
|
9550
|
-
var TRIGGER_DIR = () =>
|
|
9551
|
-
var sockPath = (sessionId, dir = TRIGGER_DIR()) =>
|
|
9705
|
+
import { join as join13 } from "path";
|
|
9706
|
+
var log21 = forComponent("remote-trigger");
|
|
9707
|
+
var TRIGGER_DIR = () => join13(homedir8(), ".agent", "triggers");
|
|
9708
|
+
var sockPath = (sessionId, dir = TRIGGER_DIR()) => join13(dir, `${sessionId}.sock`);
|
|
9552
9709
|
var TriggerServer = class {
|
|
9553
9710
|
constructor(onTrigger) {
|
|
9554
9711
|
this.onTrigger = onTrigger;
|
|
@@ -9560,7 +9717,7 @@ var TriggerServer = class {
|
|
|
9560
9717
|
start(sessionId, dir = TRIGGER_DIR()) {
|
|
9561
9718
|
this.stop();
|
|
9562
9719
|
try {
|
|
9563
|
-
|
|
9720
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
9564
9721
|
const p = sockPath(sessionId, dir);
|
|
9565
9722
|
try {
|
|
9566
9723
|
unlinkSync4(p);
|
|
@@ -9584,13 +9741,13 @@ var TriggerServer = class {
|
|
|
9584
9741
|
conn.end(JSON.stringify({ ok: false, error: String(e) }) + "\n");
|
|
9585
9742
|
}
|
|
9586
9743
|
});
|
|
9587
|
-
conn.on("error", (e) =>
|
|
9744
|
+
conn.on("error", (e) => log21.debug("trigger conn error", e));
|
|
9588
9745
|
});
|
|
9589
|
-
this.server.on("error", (e) =>
|
|
9746
|
+
this.server.on("error", (e) => log21.debug("trigger server error", e));
|
|
9590
9747
|
this.server.listen(p);
|
|
9591
9748
|
this.path = p;
|
|
9592
9749
|
} catch (e) {
|
|
9593
|
-
|
|
9750
|
+
log21.debug("trigger server unavailable", e);
|
|
9594
9751
|
}
|
|
9595
9752
|
}
|
|
9596
9753
|
/** Re-bind on /resume (the session id changed). */
|
|
@@ -9711,7 +9868,7 @@ var italic = C("3");
|
|
|
9711
9868
|
var strike = C("9");
|
|
9712
9869
|
var link = (text, url) => useColor ? `\x1B]8;;${url}\x1B\\${cyan(text)}\x1B]8;;\x1B\\` : `${text} (${url})`;
|
|
9713
9870
|
var err = (s) => process.stderr.write(s);
|
|
9714
|
-
var
|
|
9871
|
+
var log22 = forComponent("cli");
|
|
9715
9872
|
var VERSION = (() => {
|
|
9716
9873
|
try {
|
|
9717
9874
|
return JSON.parse(readFileSync7(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
|
|
@@ -9939,9 +10096,9 @@ function resolveModelOrNewest(model) {
|
|
|
9939
10096
|
var ENV_KEY_ALIASES = { google: ["GEMINI_API_KEY"] };
|
|
9940
10097
|
function loadInstallEnv() {
|
|
9941
10098
|
let dir = dirname4(import.meta.path);
|
|
9942
|
-
for (let i = 0; i < 5 && !existsSync10(
|
|
10099
|
+
for (let i = 0; i < 5 && !existsSync10(join14(dir, "package.json")); i++) dir = dirname4(dir);
|
|
9943
10100
|
for (const name of [".env", ".env.local"]) {
|
|
9944
|
-
const file =
|
|
10101
|
+
const file = join14(dir, name);
|
|
9945
10102
|
if (!existsSync10(file)) continue;
|
|
9946
10103
|
for (const line of readFileSync7(file, "utf8").split("\n")) {
|
|
9947
10104
|
const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
@@ -10302,7 +10459,7 @@ function costOf(pricing, promptTokens = 0, completionTokens = 0, cacheCreationTo
|
|
|
10302
10459
|
function turnCost(model, usage) {
|
|
10303
10460
|
return costOf(getModelInfo(model)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0, usage?.cacheCreationTokens ?? 0, usage?.cacheReadTokens ?? 0, model);
|
|
10304
10461
|
}
|
|
10305
|
-
async function evaluateGoal(ai, condition, transcript,
|
|
10462
|
+
async function evaluateGoal(ai, condition, transcript, log23) {
|
|
10306
10463
|
const recent = transcript.filter((m) => m.role === "assistant").slice(-8).map((m) => {
|
|
10307
10464
|
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
|
|
10308
10465
|
return text.slice(0, 600);
|
|
@@ -10322,7 +10479,7 @@ ${recent}` }
|
|
|
10322
10479
|
const match = r.content.match(/\{[\s\S]*\}/);
|
|
10323
10480
|
if (match) return JSON.parse(match[0]);
|
|
10324
10481
|
} catch (e) {
|
|
10325
|
-
|
|
10482
|
+
log23(dim(` (goal evaluator error: ${e?.message ?? e})
|
|
10326
10483
|
`));
|
|
10327
10484
|
}
|
|
10328
10485
|
return { met: false, reason: "evaluation unclear" };
|
|
@@ -10529,13 +10686,13 @@ function mcpAgentTools(mounted, opts) {
|
|
|
10529
10686
|
return tools;
|
|
10530
10687
|
}
|
|
10531
10688
|
async function closeMcp(mounted) {
|
|
10532
|
-
await Promise.all(mounted.map((m) => m.client.close().catch((e) =>
|
|
10689
|
+
await Promise.all(mounted.map((m) => m.client.close().catch((e) => log22.debug("mcp close failed", e))));
|
|
10533
10690
|
}
|
|
10534
10691
|
var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
|
|
10535
10692
|
function mentionRefs(line) {
|
|
10536
10693
|
return [...line.matchAll(/(?:^|\s)@(?:"([^"]+)"|(\S+))/g)].map((m) => m[1] ?? m[2].replace(/[?!.,;:)\]}'">]+$/, "")).filter(Boolean);
|
|
10537
10694
|
}
|
|
10538
|
-
var untilde = (p) => p.startsWith("~/") ?
|
|
10695
|
+
var untilde = (p) => p.startsWith("~/") ? join14(homedir9(), p.slice(2)) : p;
|
|
10539
10696
|
function readImageParts(cwd, line) {
|
|
10540
10697
|
const refs = mentionRefs(line);
|
|
10541
10698
|
const parts = [];
|
|
@@ -10579,7 +10736,7 @@ async function expandMentions(fs, line) {
|
|
|
10579
10736
|
if (loaded.includes(ref) || missing.includes(ref)) continue;
|
|
10580
10737
|
if (ref.includes(":") && mcpMentionResolver) {
|
|
10581
10738
|
const body = await mcpMentionResolver(ref).catch((e) => {
|
|
10582
|
-
|
|
10739
|
+
log22.debug("mcp mention resolve failed", e);
|
|
10583
10740
|
return null;
|
|
10584
10741
|
});
|
|
10585
10742
|
if (body != null) {
|
|
@@ -10662,7 +10819,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
10662
10819
|
try {
|
|
10663
10820
|
store.save(session);
|
|
10664
10821
|
} catch (ex) {
|
|
10665
|
-
|
|
10822
|
+
log22.debug("mid-turn session flush failed", ex);
|
|
10666
10823
|
}
|
|
10667
10824
|
}
|
|
10668
10825
|
return origNotify(e);
|
|
@@ -10775,25 +10932,25 @@ var AGENTS_MD_TEMPLATE = `# ${"${name}"}
|
|
|
10775
10932
|
`;
|
|
10776
10933
|
function initInstructions(cwd) {
|
|
10777
10934
|
for (const f of ["AGENTS.md", "CLAUDE.md"]) {
|
|
10778
|
-
if (existsSync10(
|
|
10935
|
+
if (existsSync10(join14(cwd, f))) {
|
|
10779
10936
|
err(yellow(` ${f} already exists \u2014 leaving it as-is
|
|
10780
10937
|
`));
|
|
10781
10938
|
return;
|
|
10782
10939
|
}
|
|
10783
10940
|
}
|
|
10784
|
-
const path =
|
|
10785
|
-
|
|
10941
|
+
const path = join14(cwd, "AGENTS.md");
|
|
10942
|
+
writeFileSync9(path, AGENTS_MD_TEMPLATE.replace("${name}", basename2(cwd)));
|
|
10786
10943
|
err(green(` created ${path}
|
|
10787
10944
|
`) + dim(" edit it, then it auto-loads into every run.\n"));
|
|
10788
10945
|
}
|
|
10789
10946
|
function persistSetting(cwd, key, value) {
|
|
10790
|
-
const path =
|
|
10947
|
+
const path = join14(cwd, ".agent", "settings.json");
|
|
10791
10948
|
try {
|
|
10792
10949
|
const obj = existsSync10(path) ? JSON.parse(readFileSync7(path, "utf8")) : {};
|
|
10793
10950
|
if (obj[key] === value) return;
|
|
10794
10951
|
obj[key] = value;
|
|
10795
|
-
|
|
10796
|
-
|
|
10952
|
+
mkdirSync11(dirname4(path), { recursive: true });
|
|
10953
|
+
writeFileSync9(path, JSON.stringify(obj, null, 2) + "\n");
|
|
10797
10954
|
} catch (e) {
|
|
10798
10955
|
err(yellow(` \u26A0 couldn't persist ${key} to ${path} \u2014 ${e?.message ?? e}
|
|
10799
10956
|
`));
|
|
@@ -10809,14 +10966,14 @@ var isCancelTeardown = (e) => {
|
|
|
10809
10966
|
function installCancelGuards(mounted) {
|
|
10810
10967
|
process.on("unhandledRejection", (e) => {
|
|
10811
10968
|
if (isCancelTeardown(e)) {
|
|
10812
|
-
|
|
10969
|
+
log22.debug("suppressed unhandledRejection (cursor stream cancel)", e);
|
|
10813
10970
|
return;
|
|
10814
10971
|
}
|
|
10815
|
-
|
|
10972
|
+
log22.error("unhandledRejection", e);
|
|
10816
10973
|
});
|
|
10817
10974
|
process.on("uncaughtException", (e) => {
|
|
10818
10975
|
if (isCancelTeardown(e)) {
|
|
10819
|
-
|
|
10976
|
+
log22.debug("suppressed uncaughtException (cursor stream cancel)", e);
|
|
10820
10977
|
return;
|
|
10821
10978
|
}
|
|
10822
10979
|
console.error(e);
|
|
@@ -10825,7 +10982,7 @@ function installCancelGuards(mounted) {
|
|
|
10825
10982
|
});
|
|
10826
10983
|
}
|
|
10827
10984
|
async function repl(args, ai, cfg, cwd) {
|
|
10828
|
-
const oauth = new McpOAuth({ storePath:
|
|
10985
|
+
const oauth = new McpOAuth({ storePath: join14(cwd, ".agent", "mcp-auth.json") });
|
|
10829
10986
|
const mounted = await mountMcp(cfg, oauth);
|
|
10830
10987
|
let mcpToolNames = [];
|
|
10831
10988
|
const initialMcpTools = mcpAgentTools(mounted);
|
|
@@ -11033,7 +11190,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
11033
11190
|
quickLook: {
|
|
11034
11191
|
branch: () => {
|
|
11035
11192
|
try {
|
|
11036
|
-
const head = readFileSync7(
|
|
11193
|
+
const head = readFileSync7(join14(cwd, ".git", "HEAD"), "utf8").trim();
|
|
11037
11194
|
return head.startsWith("ref: refs/heads/") ? `branch: ${head.slice("ref: refs/heads/".length)}` : `detached HEAD at ${head.slice(0, 12)}`;
|
|
11038
11195
|
} catch {
|
|
11039
11196
|
return "not a git repository";
|
|
@@ -11162,9 +11319,9 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
11162
11319
|
const pendingImages = [];
|
|
11163
11320
|
const bangContext = [];
|
|
11164
11321
|
const grabClipboardAttachment = () => {
|
|
11165
|
-
const dir =
|
|
11322
|
+
const dir = join14(tmpdir3(), "agentx-pasted");
|
|
11166
11323
|
try {
|
|
11167
|
-
|
|
11324
|
+
mkdirSync11(dir, { recursive: true });
|
|
11168
11325
|
} catch {
|
|
11169
11326
|
}
|
|
11170
11327
|
const img = grabClipboardImage(dir, String(Date.now()));
|
|
@@ -11215,7 +11372,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
11215
11372
|
err(dim(` \u23F0 ${scheduler.size} scheduled job(s) re-armed
|
|
11216
11373
|
`));
|
|
11217
11374
|
}
|
|
11218
|
-
const checkpoints = args.vfs || args.boddb ? new CheckpointStack(agent.options.fs) : new GitCheckpoints({ workTree: cwd, gitDir:
|
|
11375
|
+
const checkpoints = args.vfs || args.boddb ? new CheckpointStack(agent.options.fs) : new GitCheckpoints({ workTree: cwd, gitDir: join14(cwd, ".agent", "checkpoints.git"), addDirs: args.addDirs, sessionId: session.meta.id });
|
|
11219
11376
|
const cpHooks = checkpoints.hooks?.();
|
|
11220
11377
|
if (cpHooks) {
|
|
11221
11378
|
agent.options.hooks = composeHooks(agent.options.hooks, cpHooks);
|
|
@@ -11267,14 +11424,14 @@ ${lines.join("\n")}
|
|
|
11267
11424
|
Added entries are loadable now via the Skill/SlashCommand tools; removed ones are gone even if still listed in the system prompt.
|
|
11268
11425
|
</system-reminder>`;
|
|
11269
11426
|
};
|
|
11270
|
-
const histPath =
|
|
11427
|
+
const histPath = join14(cwd, ".agent", "history");
|
|
11271
11428
|
const history = existsSync10(histPath) ? readFileSync7(histPath, "utf8").split("\n").filter(Boolean).reverse().slice(0, 500) : [];
|
|
11272
11429
|
const remember = (line) => {
|
|
11273
11430
|
try {
|
|
11274
|
-
|
|
11431
|
+
mkdirSync11(join14(cwd, ".agent"), { recursive: true });
|
|
11275
11432
|
appendFileSync(histPath, line + "\n");
|
|
11276
11433
|
} catch (e) {
|
|
11277
|
-
|
|
11434
|
+
log22.debug("history write failed", e);
|
|
11278
11435
|
}
|
|
11279
11436
|
};
|
|
11280
11437
|
const ago = (t) => {
|
|
@@ -11345,7 +11502,7 @@ Added entries are loadable now via the Skill/SlashCommand tools; removed ones ar
|
|
|
11345
11502
|
try {
|
|
11346
11503
|
store.save(session);
|
|
11347
11504
|
} catch (e) {
|
|
11348
|
-
|
|
11505
|
+
log22.debug("session save after rewind failed", e);
|
|
11349
11506
|
}
|
|
11350
11507
|
err(green(" \u27F2 jumped back") + dim(` \u2014 ${face.transcript.length} message(s) kept; edit + resend
|
|
11351
11508
|
`));
|
|
@@ -11379,7 +11536,7 @@ ${task}`;
|
|
|
11379
11536
|
bangContext.length = 0;
|
|
11380
11537
|
}
|
|
11381
11538
|
const delta = await refreshCatalogs().catch((e) => {
|
|
11382
|
-
|
|
11539
|
+
log22.debug("catalog refresh failed", e);
|
|
11383
11540
|
return "";
|
|
11384
11541
|
});
|
|
11385
11542
|
if (delta) {
|
|
@@ -11596,8 +11753,8 @@ ${task}`;
|
|
|
11596
11753
|
cfgFiles.length ? ok(`config: ${cfgFiles.join(", ")}`) : warn("no .agent/config.* found (project or ~) \u2014 running on defaults");
|
|
11597
11754
|
try {
|
|
11598
11755
|
const probe = `${cwd}/.agent/sessions/.doctor-probe`;
|
|
11599
|
-
|
|
11600
|
-
|
|
11756
|
+
mkdirSync11(`${cwd}/.agent/sessions`, { recursive: true });
|
|
11757
|
+
writeFileSync9(probe, "ok");
|
|
11601
11758
|
unlinkSync5(probe);
|
|
11602
11759
|
ok(`session store writable (${cwd}/.agent/sessions)`);
|
|
11603
11760
|
} catch (e) {
|
|
@@ -11625,7 +11782,7 @@ ${task}`;
|
|
|
11625
11782
|
desc: "rescan skills/commands dirs and rebuild the system prompt (one cache miss) \u2014 picks up entries created mid-session",
|
|
11626
11783
|
run: async () => {
|
|
11627
11784
|
await refreshCatalogs().catch((e) => {
|
|
11628
|
-
|
|
11785
|
+
log22.debug("catalog refresh failed", e);
|
|
11629
11786
|
});
|
|
11630
11787
|
face.reprepare();
|
|
11631
11788
|
err(green(` \u2713 reloaded \u2014 ${skills.length} skill(s), ${cmds.length} command(s); system prompt rebuilds on next message
|
|
@@ -12082,7 +12239,7 @@ ${task}`;
|
|
|
12082
12239
|
try {
|
|
12083
12240
|
for (const def of (await loadAgents(fs2, d)).agents) if (!seen.has(def.name)) seen.set(def.name, { def, from: d });
|
|
12084
12241
|
} catch (e) {
|
|
12085
|
-
|
|
12242
|
+
log22.debug(`loadAgents(${d}) failed`, e);
|
|
12086
12243
|
}
|
|
12087
12244
|
}
|
|
12088
12245
|
if (!seen.size) {
|
|
@@ -12170,7 +12327,7 @@ ${task}`;
|
|
|
12170
12327
|
}
|
|
12171
12328
|
if (idx >= 0) {
|
|
12172
12329
|
const old = mounted.splice(idx, 1)[0];
|
|
12173
|
-
await old.client.close().catch((e) =>
|
|
12330
|
+
await old.client.close().catch((e) => log22.debug("mcp close failed", e));
|
|
12174
12331
|
}
|
|
12175
12332
|
try {
|
|
12176
12333
|
const m = await mountMcpServer(name, conf);
|
|
@@ -12198,7 +12355,7 @@ ${task}`;
|
|
|
12198
12355
|
}
|
|
12199
12356
|
const m = mounted.splice(idx, 1)[0];
|
|
12200
12357
|
remountMcpTools();
|
|
12201
|
-
await m.client.close().catch((e) =>
|
|
12358
|
+
await m.client.close().catch((e) => log22.debug("mcp close failed", e));
|
|
12202
12359
|
err(dim(` removed "${name}"
|
|
12203
12360
|
`));
|
|
12204
12361
|
return;
|
|
@@ -12330,11 +12487,11 @@ ${task}`;
|
|
|
12330
12487
|
return;
|
|
12331
12488
|
}
|
|
12332
12489
|
const md = exportMarkdown(session.meta, shown);
|
|
12333
|
-
const name = a[0] ? extname(a[0]) ? a[0] : a[0] + ".md" :
|
|
12490
|
+
const name = a[0] ? extname(a[0]) ? a[0] : a[0] + ".md" : join14(".agent", "exports", `${session.meta.id}.md`);
|
|
12334
12491
|
const path = resolve3(cwd, name);
|
|
12335
12492
|
try {
|
|
12336
|
-
|
|
12337
|
-
|
|
12493
|
+
mkdirSync11(dirname4(path), { recursive: true });
|
|
12494
|
+
writeFileSync9(path, md);
|
|
12338
12495
|
err(green(` \u2713 exported \u2192 ${path}
|
|
12339
12496
|
`) + dim(` ${shown.length} message(s) \xB7 ${md.length} chars
|
|
12340
12497
|
`));
|
|
@@ -12396,9 +12553,9 @@ ${task}`;
|
|
|
12396
12553
|
if (dx) banner(dim(`\u25D1 duplex \u2014 reflex: ${dx.options.reflexModel} \xB7 act: ${work.model}${dx.options.thinkModel !== false ? ` \xB7 think: ${dx.options.thinkModel}` : ""} (real work runs in background tasks, re-voiced when done)`));
|
|
12397
12554
|
const listDir = (absDir) => {
|
|
12398
12555
|
try {
|
|
12399
|
-
return readdirSync4(
|
|
12556
|
+
return readdirSync4(join14(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
|
|
12400
12557
|
} catch (e) {
|
|
12401
|
-
|
|
12558
|
+
log22.debug("completion readdir failed", absDir, e);
|
|
12402
12559
|
return null;
|
|
12403
12560
|
}
|
|
12404
12561
|
};
|
|
@@ -12525,18 +12682,18 @@ ${out}
|
|
|
12525
12682
|
return;
|
|
12526
12683
|
}
|
|
12527
12684
|
await refreshCatalogs().catch((e) => {
|
|
12528
|
-
|
|
12685
|
+
log22.debug("catalog refresh failed", e);
|
|
12529
12686
|
});
|
|
12530
|
-
const c = cmds.find((x) => x.name === name);
|
|
12531
|
-
if (c) {
|
|
12532
|
-
await runCommand(c, a.join(" "));
|
|
12533
|
-
return;
|
|
12534
|
-
}
|
|
12535
12687
|
const sk = skills.find((x) => x.name === name);
|
|
12536
12688
|
if (sk) {
|
|
12537
12689
|
await runSkill(sk, a.join(" "));
|
|
12538
12690
|
return;
|
|
12539
12691
|
}
|
|
12692
|
+
const c = cmds.find((x) => x.name === name);
|
|
12693
|
+
if (c) {
|
|
12694
|
+
await runCommand(c, a.join(" "));
|
|
12695
|
+
return;
|
|
12696
|
+
}
|
|
12540
12697
|
const known = Object.keys(builtins).filter((k) => k !== "quit").map((k) => "/" + k);
|
|
12541
12698
|
const custom = [...cmds.map((x) => x.name), ...skills.map((x) => x.name)].map((n) => "/" + n);
|
|
12542
12699
|
err(red(` unknown command /${name}
|
|
@@ -12925,7 +13082,7 @@ async function main() {
|
|
|
12925
13082
|
}
|
|
12926
13083
|
});
|
|
12927
13084
|
if (args.task) {
|
|
12928
|
-
const mounted = await mountMcp(cfg, new McpOAuth({ storePath:
|
|
13085
|
+
const mounted = await mountMcp(cfg, new McpOAuth({ storePath: join14(cwd, ".agent", "mcp-auth.json") }));
|
|
12929
13086
|
const agent = await makeAgent(args, ai, cfg, mcpAgentTools(mounted));
|
|
12930
13087
|
const store = new SessionStore(cwd);
|
|
12931
13088
|
const session = startSession(args, store, agent, cwd);
|