@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/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
- log12.debug("shell spawn error", err2);
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 log12, clean, DETACHED, SECRET_ENV_RE, _spawn, ShellJobRegistry, NO_JOB2;
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
- log12 = forComponent("shell");
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 mkdirSync10, writeFileSync as writeFileSync8, readdirSync as readdirSync4, statSync as statSync4, unlinkSync as unlinkSync5 } from "fs";
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 join13, resolve as resolve3, basename as basename2, extname, dirname as dirname4 } from "path";
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: "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`. 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.",
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
- const overlay = new OverlayFilesystem(opts.fs);
2680
- const childOpts2 = childOptionsFor(opts, overlay, depth, maxDepth, agentType);
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
- childOpts2.signal = signal;
2683
- const res2 = await new Agent(childOpts2).run(String(prompt ?? ""));
2684
- if (signal.aborted) return "[killed before commit]";
2685
- await overlay.commit();
2686
- const summary2 = res2.text || `(child '${label}' finished with no summary; finishReason=${res2.finishReason})`;
2687
- await opts.hooks?.onSubagentStop?.(summary2, { label, agentType });
2688
- return summary2;
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
- const childOpts = childOptionsFor(opts, opts.fs, depth, maxDepth, agentType);
2693
- if (typeof childOpts === "string") return `Error: ${childOpts}`;
2694
- const child = new Agent(childOpts);
2695
- const res = await child.run(String(prompt ?? ""));
2696
- const summary = res.text || `(child agent finished '${label}' with no summary; finishReason=${res.finishReason})`;
2697
- await opts.hooks?.onSubagentStop?.(summary, { label, agentType });
2698
- return summary;
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: "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? }]`.",
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 }, _ctx) {
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
- const overlay = new OverlayFilesystem(opts.fs);
2735
- const childOpts = childOptionsFor(opts, overlay, depth, maxDepth, t?.agentType);
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 res = await new Agent(childOpts).run(String(t?.prompt ?? ""));
2739
- await opts.hooks?.onSubagentStop?.(res.text, { label, agentType: t?.agentType });
2740
- return { label, text: res.text, overlay, ok: res.finishReason !== "error" };
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
- for (const r of results) if (r.ok && r.overlay) await r.overlay.commit().catch(() => {
2746
- });
2747
- return results.map((r, i) => `### ${i + 1}. ${r.label}
2748
- ${r.error ? `ERROR: ${r.error}` : r.text || "(no summary)"}`).join("\n\n");
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 log3 = forComponent("Agent");
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
- log3.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
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
- log3.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` : ""})`);
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
- log3.warn(`network drop mid-step (${err2?.message ?? err2}) \u2014 retrying in ${waitMs}ms`);
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
- log3.error(`chat() failed: ${err2?.message ?? err2}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, err2);
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
- log3.verbose(`completed in ${steps} step(s)`);
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
- log3.debug(`${tc.function.name} -> ${blocked}`);
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
- log3.debug(`${tc.function.name} -> ${earlyError}`);
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
- log3.debug(`${tc.function.name}(${tc.function.arguments})`);
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
- log3.debug(`onToolOutput hook error: ${e}`);
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
- log3.debug(`${tc.function.name} -> error: ${msg}`);
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
- log3.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
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 log4 = forComponent("scratch");
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
- log4.debug("scratch write failed; returning raw", e);
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
- log4.debug("scratch spill failed; cropping lossy", e);
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
- log4.debug("Ask peek failed", e);
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 log5 = forComponent("Lessons");
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) => log5.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
4353
- log5.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
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 log6 = forComponent("Reflect");
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
- log6.warn(`reflection call failed: ${e?.message ?? e}`);
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
- log6.warn(`could not persist lesson: ${e?.message ?? e}`);
4540
+ log7.warn(`could not persist lesson: ${e?.message ?? e}`);
4387
4541
  return null;
4388
4542
  }
4389
- log6.debug(`reflection persisted ${slug2}`);
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 log7 = forComponent("DuplexAgent");
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
- log7.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
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
- log7.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
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
- log7.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
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
- log7.verbose(`task ${id} done (${res.steps} steps)`);
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
- log7.warn(`task ${rec.id} failed: ${msg}`);
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 log8 = forComponent("VoiceEngine");
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
- log8.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
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) log8.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
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) log8.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
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
- log8.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
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 log9 = forComponent("SonioxSTT");
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
- log9.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
5827
+ log10.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
5674
5828
  this.reset();
5675
- this.connectWs().catch((e) => log9.error(`soniox reconnect failed: ${e.message}`));
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) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
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 log9.error(`soniox: ${m.error_message}`);
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) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
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 log10 = forComponent("CartesiaTTS");
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
- log10.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
5944
+ log11.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
5791
5945
  if (!this.closed) {
5792
- this.connecting = this.doConnect().catch((e) => log10.error(`cartesia reconnect failed: ${e.message}`));
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
- log10.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
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
- log10.warn(`cartesia: ${JSON.stringify(m)}`);
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 ? log10.debug : log10.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
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 log11 = forComponent("mcp");
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) => log11.debug(`[${command}] stderr:`, chunk.trimEnd()));
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
- log11.debug("dropping non-JSON line from MCP server:", line, e);
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
- log11.debug("stdin end failed", e);
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
- log11.debug("skipping unparseable SSE data line", e);
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) => log11.debug(`close after failed mount of "${name}": ${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
- log11.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
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) log11.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
6150
- else log11.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
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
- log11.debug(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
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) => log11.debug(`warm-pool replace close failed: ${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) => log11.debug(`warm-pool evict close failed: ${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 writeFileSync2, mkdirSync as mkdirSync2, existsSync } from "fs";
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
- mkdirSync2(dirname2(this.options.storePath), { recursive: true });
6269
- writeFileSync2(this.options.storePath, JSON.stringify(store, null, 2), { mode: 384 });
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 join4 } from "path";
6428
- import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
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 log13 = forComponent("notify");
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
- log13.debug("notification failed", e);
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 log14 = forComponent("cli-util");
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
- log14.debug(`parseJson(${what}) failed: ${e.message}`);
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
- log14.debug(`readJsonFile(${path}) unreadable: ${e.message}`);
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
- mkdirSync3(root, { recursive: true });
6587
- const db = new BodDB2({ path: join4(root, "meta.db"), sweepInterval: 0, vfs: { storageRoot: join4(root, "files") } });
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 mkdirSync4, statSync as statSync3 } from "fs";
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 join5 } from "path";
6906
+ import { dirname as dirname3, join as join6 } from "path";
6750
6907
  import { fileURLToPath } from "url";
6751
- var log15 = forComponent("VoiceIO");
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) => log15.warn(`ffplay error: ${e.message}`));
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 = () => join5(dirname3(fileURLToPath(import.meta.url)), "native");
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
- log15.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
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 = join5(nativeDir(), "mic-aec.swift");
6829
- const plist = join5(nativeDir(), "Info.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 = join5(homedir3(), ".agent", "cache");
6832
- const bin = join5(cacheDir, "mic-aec");
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
- mkdirSync4(cacheDir, { recursive: true });
6836
- log15.info("compiling AEC mic helper (first run)\u2026");
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
- log15.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
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
- log15.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
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
- log15.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
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) => log15.warn(`ffmpeg: ${String(d).trim()}`));
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) log15.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
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) log15.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
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()) log15.debug(`mic-aec: ${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 join6 } from "path";
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 = join6(dir, ".agent", f);
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 = join6(dir, ".agent", "settings.json");
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 log16 = forComponent("hooks");
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
- log16.debug(`hook command failed: ${rule.command}`, e);
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 mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync, renameSync, symlinkSync as symlinkSync2, unlinkSync, readlinkSync } from "fs";
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 join7 } from "path";
7211
- var log17 = forComponent("session");
7212
- var globalDir = () => join7(homedir5(), ".agent", "sessions");
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 = join7(cwd, ".agent", "sessions");
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(join7(this.dir, `${id}.json`))) {
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(join7(this.dir, `${c}.json`))) {
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)) mkdirSync5(this.dir, { recursive: true });
7242
- const path = join7(this.dir, `${data.meta.id}.json`);
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
- writeFileSync3(tmp, JSON.stringify(data));
7401
+ writeFileSync4(tmp, JSON.stringify(data));
7245
7402
  renameSync(tmp, path);
7246
7403
  try {
7247
7404
  const gd = globalDir();
7248
- if (!existsSync6(gd)) mkdirSync5(gd, { recursive: true });
7249
- const link2 = join7(gd, `${data.meta.id}.json`);
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
- log17.debug(`rejecting unsafe session id: ${id}`);
7413
+ log18.debug(`rejecting unsafe session id: ${id}`);
7257
7414
  return void 0;
7258
7415
  }
7259
- const path = join7(this.dir, `${id}.json`);
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
- log17.debug(`unreadable session ${id} \u2014 ignoring`, e);
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(join7(this.dir, f), "utf8")).meta);
7432
+ metas.push(JSON.parse(readFileSync5(join8(this.dir, f), "utf8")).meta);
7276
7433
  } catch (e) {
7277
- log17.debug(`skipping unreadable session file ${f}`, e);
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 = join7(gd, `${idOrPrefix}.json`);
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(join7(gd, f));
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(join7(gd, f));
7480
+ const target = readlinkSync(join8(gd, f));
7324
7481
  if (!existsSync6(target)) {
7325
7482
  try {
7326
- unlinkSync(join7(gd, f));
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 writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync7 } from "fs";
7444
- import { join as join8, resolve as resolve2, sep as sep2 } from "path";
7445
- var log18 = forComponent("checkpoints");
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
- mkdirSync6(this.gitDir, { recursive: true });
7634
+ mkdirSync7(this.gitDir, { recursive: true });
7478
7635
  await this.run("init", "-q");
7479
7636
  }
7480
- writeFileSync4(join8(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
7637
+ writeFileSync5(join9(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
7481
7638
  this.ready = true;
7482
7639
  } catch (e) {
7483
- log18.debug(`git checkpoints unavailable for ${this.workTree}`, e);
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) => log18.debug(`force-add failed: ${p}`, 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) => log18.debug(`force-capture failed: ${p}`, e));
7503
- await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) => log18.debug("amend failed", 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
- log18.debug("checkpoint prune failed", e);
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: join8(abs, ".agent", "checkpoints.git") });
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) => log18.debug("re-point failed", 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
- log18.debug("checkpoint commit failed", e);
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) => log18.debug("amendForced failed", 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 = join8(process.cwd(), ".agent", "checkpoints.git");
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 writeFileSync5, mkdirSync as mkdirSync7 } from "fs";
7887
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "fs";
7731
7888
  import { homedir as homedir6 } from "os";
7732
- import { join as join9 } from "path";
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) => join9(cwd, ".agent", "permissions.json");
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 = [join9(home, ".claude", "settings.json"), join9(cwd, ".claude", "settings.json"), join9(cwd, ".claude", "settings.local.json")];
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
- mkdirSync7(join9(cwd, ".agent"), { recursive: true });
7774
- writeFileSync5(PERM_FILE(cwd), JSON.stringify(cur, null, 2) + "\n");
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 = join9(homedir6(), ".agent", "trusted.json");
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
- mkdirSync7(join9(file, ".."), { recursive: true });
7798
- writeFileSync5(file, JSON.stringify(list, null, 2) + "\n");
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 writeFileSync6, readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "fs";
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 join10 } from "path";
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 = join10(tmpdir2(), `agentx-edit-${process.pid}-${Date.now()}.md`);
9094
+ const file = join11(tmpdir2(), `agentx-edit-${process.pid}-${Date.now()}.md`);
8938
9095
  try {
8939
- writeFileSync6(file, s.buf);
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 writeFileSync7, mkdirSync as mkdirSync8, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync8 } from "fs";
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 join11 } from "path";
9370
- var log19 = forComponent("os-sched");
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 join11(this.options.home, ".agent", "sched");
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 join11(this.options.home, "Library", "LaunchAgents", `${this.label(id)}.plist`);
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
- mkdirSync8(this.dir, { recursive: true });
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
- writeFileSync7(join11(this.dir, `${spec.id}.json`), JSON.stringify(meta, null, 2));
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(join11(this.dir, `${id}.json`), null);
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
- log19.debug(`cancel ${id}`, e);
9589
+ log20.debug(`cancel ${id}`, e);
9433
9590
  }
9434
9591
  for (const f of [`${id}.json`, `${id}.sh`]) {
9435
9592
  try {
9436
- unlinkSync3(join11(this.dir, f));
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(join11(this.dir, f), null)).filter(Boolean);
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 = join11(this.dir, `${spec.id}.sh`);
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(join11(this.dir, `${spec.id}.json`))} ${q2(p)}
9451
- ` : `rm -f ${q2(join11(this.dir, `${spec.id}.json`))} ${q2(p)}
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
- writeFileSync7(p, `#!/bin/sh
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(join11(this.dir, `${spec.id}.log`))} 2>&1
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
- mkdirSync8(join11(this.options.home, "Library", "LaunchAgents"), { recursive: true });
9494
- writeFileSync7(this.plistPath(spec.id), plist);
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 mkdirSync9, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
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 join12 } from "path";
9549
- var log20 = forComponent("remote-trigger");
9550
- var TRIGGER_DIR = () => join12(homedir8(), ".agent", "triggers");
9551
- var sockPath = (sessionId, dir = TRIGGER_DIR()) => join12(dir, `${sessionId}.sock`);
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
- mkdirSync9(dir, { recursive: true, mode: 448 });
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) => log20.debug("trigger conn error", e));
9744
+ conn.on("error", (e) => log21.debug("trigger conn error", e));
9588
9745
  });
9589
- this.server.on("error", (e) => log20.debug("trigger server 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
- log20.debug("trigger server unavailable", e);
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 log21 = forComponent("cli");
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(join13(dir, "package.json")); i++) dir = dirname4(dir);
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 = join13(dir, name);
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, log22) {
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
- log22(dim(` (goal evaluator error: ${e?.message ?? e})
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) => log21.debug("mcp close failed", 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("~/") ? join13(homedir9(), p.slice(2)) : p;
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
- log21.debug("mcp mention resolve failed", e);
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
- log21.debug("mid-turn session flush failed", ex);
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(join13(cwd, f))) {
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 = join13(cwd, "AGENTS.md");
10785
- writeFileSync8(path, AGENTS_MD_TEMPLATE.replace("${name}", basename2(cwd)));
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 = join13(cwd, ".agent", "settings.json");
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
- mkdirSync10(dirname4(path), { recursive: true });
10796
- writeFileSync8(path, JSON.stringify(obj, null, 2) + "\n");
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
- log21.debug("suppressed unhandledRejection (cursor stream cancel)", e);
10969
+ log22.debug("suppressed unhandledRejection (cursor stream cancel)", e);
10813
10970
  return;
10814
10971
  }
10815
- log21.error("unhandledRejection", e);
10972
+ log22.error("unhandledRejection", e);
10816
10973
  });
10817
10974
  process.on("uncaughtException", (e) => {
10818
10975
  if (isCancelTeardown(e)) {
10819
- log21.debug("suppressed uncaughtException (cursor stream cancel)", e);
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: join13(cwd, ".agent", "mcp-auth.json") });
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(join13(cwd, ".git", "HEAD"), "utf8").trim();
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 = join13(tmpdir3(), "agentx-pasted");
11322
+ const dir = join14(tmpdir3(), "agentx-pasted");
11166
11323
  try {
11167
- mkdirSync10(dir, { recursive: true });
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: join13(cwd, ".agent", "checkpoints.git"), addDirs: args.addDirs, sessionId: session.meta.id });
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 = join13(cwd, ".agent", "history");
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
- mkdirSync10(join13(cwd, ".agent"), { recursive: true });
11431
+ mkdirSync11(join14(cwd, ".agent"), { recursive: true });
11275
11432
  appendFileSync(histPath, line + "\n");
11276
11433
  } catch (e) {
11277
- log21.debug("history write failed", e);
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
- log21.debug("session save after rewind failed", e);
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
- log21.debug("catalog refresh failed", e);
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
- mkdirSync10(`${cwd}/.agent/sessions`, { recursive: true });
11600
- writeFileSync8(probe, "ok");
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
- log21.debug("catalog refresh failed", e);
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
- log21.debug(`loadAgents(${d}) failed`, e);
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) => log21.debug("mcp close failed", 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) => log21.debug("mcp close failed", 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" : join13(".agent", "exports", `${session.meta.id}.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
- mkdirSync10(dirname4(path), { recursive: true });
12337
- writeFileSync8(path, md);
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(join13(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
12556
+ return readdirSync4(join14(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
12400
12557
  } catch (e) {
12401
- log21.debug("completion readdir failed", absDir, e);
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
- log21.debug("catalog refresh failed", e);
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: join13(cwd, ".agent", "mcp-auth.json") }));
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);