@oxagen/cli 2.1.4-443 → 2.1.4-453

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.
Files changed (2) hide show
  1. package/oxagen.mjs +719 -319
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -7838,6 +7838,7 @@ function sessionScopeForEnrollment(next, previous) {
7838
7838
  function harnessFilesRecord(paths) {
7839
7839
  return {
7840
7840
  claude_settings: paths.claudeSettings,
7841
+ claude_user_config: paths.claudeUserConfig,
7841
7842
  codex_hooks: paths.codexHooks,
7842
7843
  cursor_hooks: [...paths.cursorHooks],
7843
7844
  stella_toml: paths.stellaToml,
@@ -7852,6 +7853,9 @@ function withRecordedHarnessFiles(paths, host) {
7852
7853
  return {
7853
7854
  ...paths,
7854
7855
  claudeSettings,
7856
+ // A record from before #5287 has no entry, and the host it describes
7857
+ // never had the Oxagen MCP server written, so this process's path stands.
7858
+ claudeUserConfig: record.claude_user_config ?? paths.claudeUserConfig,
7855
7859
  claudeProjects: record.claude_settings !== void 0 ? join3(dirname3(claudeSettings), "projects") : paths.claudeProjects,
7856
7860
  codexHooks: record.codex_hooks ?? paths.codexHooks,
7857
7861
  cursorHooks: record.cursor_hooks ?? paths.cursorHooks,
@@ -8022,6 +8026,8 @@ var init_host_file = __esm({
8022
8026
  HOST_FILE_SCHEMA = "tacho.host.v1";
8023
8027
  harnessFilesRecordSchema = external_exports.object({
8024
8028
  claude_settings: external_exports.string().min(1),
8029
+ /** Claude Code's user config, which holds the Oxagen MCP server entry. */
8030
+ claude_user_config: external_exports.string().min(1),
8025
8031
  codex_hooks: external_exports.string().min(1),
8026
8032
  cursor_hooks: external_exports.array(external_exports.string().min(1)),
8027
8033
  stella_toml: external_exports.string().min(1),
@@ -8161,6 +8167,22 @@ var init_host_file = __esm({
8161
8167
  * beside ours; this is the `displaced_env` of the connected tier.
8162
8168
  */
8163
8169
  displaced_mcp_servers: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.unknown())).default({}),
8170
+ /**
8171
+ * When this enrollment last wrote the `oxagen` server into a wrapped
8172
+ * harness's user MCP config, by harness (#5287). Today that is only
8173
+ * `claude-code`. Two readers use it:
8174
+ *
8175
+ * - `oxagen agent status` adds the server once to a live enrollment
8176
+ * made before enroll wrote it, and an entry here says it already
8177
+ * did. A person who later removes the server keeps it removed until
8178
+ * they enroll again.
8179
+ * - The Stop hook asks only a session that started after this time.
8180
+ * Claude Code loads MCP servers when a session starts, so a session
8181
+ * already running when the entry was written does not have the tool.
8182
+ *
8183
+ * Optional: a host enrolled before #5287 has none.
8184
+ */
8185
+ mcp_registered_at: external_exports.record(external_exports.string(), external_exports.string()).optional(),
8164
8186
  /**
8165
8187
  * Where the harness files were when this host enrolled
8166
8188
  * (`harnessFilesRecord`). Optional: a host enrolled before this field
@@ -9022,6 +9044,9 @@ import { join as join6 } from "node:path";
9022
9044
  function claudeConfigDirFor(env, home) {
9023
9045
  return env["CLAUDE_CONFIG_DIR"] ?? join6(home, ".claude");
9024
9046
  }
9047
+ function claudeUserConfigFor(env, home) {
9048
+ return join6(env["CLAUDE_CONFIG_DIR"] ?? home, ".claude.json");
9049
+ }
9025
9050
  function codexHomeFor(env, home) {
9026
9051
  return env["CODEX_HOME"] ?? join6(home, ".codex");
9027
9052
  }
@@ -9044,6 +9069,7 @@ function tachoHome(env = process.env, home = homedir(), platform = process.platf
9044
9069
  log: join6(tachoDir, "tachod.log"),
9045
9070
  daemonLauncher: join6(tachoDir, "tachod.cmd"),
9046
9071
  claudeSettings: join6(claudeConfigDir, "settings.json"),
9072
+ claudeUserConfig: claudeUserConfigFor(env, home),
9047
9073
  claudeProjects: join6(claudeConfigDir, "projects"),
9048
9074
  codexHooks: join6(codexHome, "hooks.json"),
9049
9075
  cursorHooks: cursorHooksPaths(home, platform, env),
@@ -9060,6 +9086,7 @@ function homeOf(paths) {
9060
9086
  log: paths.log,
9061
9087
  daemonLauncher: paths.daemonLauncher,
9062
9088
  claudeSettings: paths.claudeSettings,
9089
+ claudeUserConfig: paths.claudeUserConfig,
9063
9090
  claudeProjects: paths.claudeProjects,
9064
9091
  codexHooks: paths.codexHooks,
9065
9092
  cursorHooks: paths.cursorHooks,
@@ -10044,20 +10071,26 @@ function parsePsStarts(pids, result2) {
10044
10071
  }
10045
10072
  return out;
10046
10073
  }
10074
+ function psStartCommand(pids) {
10075
+ return [
10076
+ "env",
10077
+ [...PS_ZONE, "ps", "-o", "pid=,lstart=", "-p", pids.join(",")]
10078
+ ];
10079
+ }
10047
10080
  function readProcessStarts(pids, exec4 = psExec, platform = process.platform, read2 = readText) {
10048
10081
  if (platform === "win32" || pids.length === 0) return void 0;
10049
10082
  if (platform === "linux") return readProcStarts(pids, read2);
10050
- return parsePsStarts(pids, exec4("ps", PS_START_ARGS(pids)));
10083
+ return parsePsStarts(pids, exec4(...psStartCommand(pids)));
10051
10084
  }
10052
10085
  async function readProcessStartsAsync(pids, exec4 = psExecAsync, platform = process.platform, read2 = readText) {
10053
10086
  if (platform === "win32" || pids.length === 0) return void 0;
10054
10087
  if (platform === "linux") return readProcStarts(pids, read2);
10055
- return parsePsStarts(pids, await exec4("ps", PS_START_ARGS(pids)));
10088
+ return parsePsStarts(pids, await exec4(...psStartCommand(pids)));
10056
10089
  }
10057
10090
  function readProcessStartsNoWait(pids, exec4 = psExecAsync, platform = process.platform, read2 = readText) {
10058
10091
  if (platform === "win32" || pids.length === 0) return void 0;
10059
10092
  if (platform === "linux") return readProcStarts(pids, read2);
10060
- return exec4("ps", PS_START_ARGS(pids)).then(
10093
+ return exec4(...psStartCommand(pids)).then(
10061
10094
  (result2) => parsePsStarts(pids, result2),
10062
10095
  () => void 0
10063
10096
  );
@@ -10109,20 +10142,15 @@ function processExecutable(pid) {
10109
10142
  return void 0;
10110
10143
  }
10111
10144
  }
10112
- var CLAUDE_BINARY, CLAUDE_VERSIONS_DIR, PROCESS_START_TIMEOUT_MS, PS_ENV, PS_START_ARGS, DAEMON_IMAGES;
10145
+ var CLAUDE_BINARY, CLAUDE_VERSIONS_DIR, PROCESS_START_TIMEOUT_MS, PS_ZONE, PS_ENV, DAEMON_IMAGES;
10113
10146
  var init_process_scan = __esm({
10114
10147
  "../../packages/tacho/src/host/process-scan.ts"() {
10115
10148
  "use strict";
10116
10149
  CLAUDE_BINARY = /(^|\/)claude(\s|$)/;
10117
10150
  CLAUDE_VERSIONS_DIR = /\/claude\/versions\/\d+\.\d+\.\d+/;
10118
10151
  PROCESS_START_TIMEOUT_MS = 2e3;
10152
+ PS_ZONE = ["TZ=UTC", "LC_ALL=C", "LANG=C"];
10119
10153
  PS_ENV = { ...process.env, TZ: "UTC", LC_ALL: "C", LANG: "C" };
10120
- PS_START_ARGS = (pids) => [
10121
- "-o",
10122
- "pid=,lstart=",
10123
- "-p",
10124
- pids.join(",")
10125
- ];
10126
10154
  DAEMON_IMAGES = ["tacho", "node"];
10127
10155
  }
10128
10156
  });
@@ -20366,6 +20394,304 @@ var init_backfill3 = __esm({
20366
20394
  }
20367
20395
  });
20368
20396
 
20397
+ // ../../packages/tacho/src/host/claude-code-mcp-writer.ts
20398
+ function mergeClaudeCodeMcpConfig(existing, config) {
20399
+ return mergeOxagenMcpServer(existing, config, "stdio");
20400
+ }
20401
+ function stripClaudeCodeMcpConfig(existing, enrollmentId, restore = {}) {
20402
+ return stripOxagenMcpServer(existing, enrollmentId, restore);
20403
+ }
20404
+ function claudeCodeSessionHasOxagenTools(check2) {
20405
+ if (!check2.hasGatewayKey || check2.registeredAt === void 0) return false;
20406
+ const registered = Date.parse(check2.registeredAt);
20407
+ const started = Date.parse(check2.sessionStartedAt);
20408
+ if (!Number.isFinite(registered) || !Number.isFinite(started)) return false;
20409
+ if (started < registered) return false;
20410
+ return oxagenMcpPresence(check2.readUserConfig(), check2.enrollmentId).present;
20411
+ }
20412
+ function claudeUserConfigShapeProblem(document) {
20413
+ return mcpConfigShapeProblem(document);
20414
+ }
20415
+ var init_claude_code_mcp_writer = __esm({
20416
+ "../../packages/tacho/src/host/claude-code-mcp-writer.ts"() {
20417
+ "use strict";
20418
+ init_mcp_config_writer();
20419
+ }
20420
+ });
20421
+
20422
+ // ../../packages/tacho/src/host/install-lock.ts
20423
+ import { randomBytes as randomBytes3 } from "node:crypto";
20424
+ import {
20425
+ closeSync as closeSync3,
20426
+ existsSync as existsSync4,
20427
+ linkSync,
20428
+ mkdirSync as mkdirSync4,
20429
+ openSync as openSync3,
20430
+ readdirSync as readdirSync5,
20431
+ readFileSync as readFileSync5,
20432
+ renameSync as renameSync3,
20433
+ rmdirSync as rmdirSync3,
20434
+ unlinkSync as unlinkSync3,
20435
+ writeSync as writeSync2
20436
+ } from "node:fs";
20437
+ import { dirname as dirname4, join as join12 } from "node:path";
20438
+ function readLock(path2) {
20439
+ try {
20440
+ return readFileSync5(path2, "utf8");
20441
+ } catch {
20442
+ return void 0;
20443
+ }
20444
+ }
20445
+ function holderOf(text4) {
20446
+ try {
20447
+ return JSON.parse(text4 ?? "");
20448
+ } catch {
20449
+ return {};
20450
+ }
20451
+ }
20452
+ function removeQuietly(path2) {
20453
+ try {
20454
+ unlinkSync3(path2);
20455
+ } catch {
20456
+ }
20457
+ }
20458
+ function pidAlive(pid) {
20459
+ try {
20460
+ process.kill(pid, 0);
20461
+ return true;
20462
+ } catch (error) {
20463
+ return error.code === "EPERM";
20464
+ }
20465
+ }
20466
+ function acquireInstallLock(root, now = Date.now, alive = pidAlive) {
20467
+ const path2 = join12(root, "install.lock");
20468
+ const created = [];
20469
+ let dir = root;
20470
+ while (!existsSync4(dir) && dirname4(dir) !== dir) {
20471
+ created.unshift(dir);
20472
+ dir = dirname4(dir);
20473
+ }
20474
+ mkdirSync4(root, { recursive: true, mode: 448 });
20475
+ const token = randomBytes3(12).toString("hex");
20476
+ const liveHolder = (holder) => typeof holder.pid === "number" && typeof holder.at === "number" && now() - holder.at < STALE_AFTER_MS && alive(holder.pid) ? holder.pid : void 0;
20477
+ for (let attempt = 0; attempt < 2; attempt += 1) {
20478
+ try {
20479
+ const fd2 = openSync3(path2, "wx", 384);
20480
+ writeSync2(fd2, JSON.stringify({ pid: process.pid, at: now(), token }));
20481
+ closeSync3(fd2);
20482
+ return {
20483
+ release: () => {
20484
+ if (holderOf(readLock(path2)).token === token) removeQuietly(path2);
20485
+ for (const made of [...created].reverse()) {
20486
+ try {
20487
+ if (readdirSync5(made).length === 0) rmdirSync3(made);
20488
+ } catch {
20489
+ }
20490
+ }
20491
+ }
20492
+ };
20493
+ } catch (error) {
20494
+ if (error.code !== "EEXIST") throw error;
20495
+ const live = liveHolder(holderOf(readLock(path2)));
20496
+ if (live !== void 0) return { heldBy: live };
20497
+ const aside = `${path2}.${process.pid}.${token}.stale`;
20498
+ try {
20499
+ renameSync3(path2, aside);
20500
+ } catch (renameError) {
20501
+ if (renameError.code === "ENOENT") continue;
20502
+ throw renameError;
20503
+ }
20504
+ const moved = liveHolder(holderOf(readLock(aside)));
20505
+ if (moved !== void 0) {
20506
+ try {
20507
+ linkSync(aside, path2);
20508
+ } catch {
20509
+ }
20510
+ removeQuietly(aside);
20511
+ return { heldBy: moved };
20512
+ }
20513
+ removeQuietly(aside);
20514
+ }
20515
+ }
20516
+ return { heldBy: -1 };
20517
+ }
20518
+ var STALE_AFTER_MS;
20519
+ var init_install_lock = __esm({
20520
+ "../../packages/tacho/src/host/install-lock.ts"() {
20521
+ "use strict";
20522
+ STALE_AFTER_MS = 10 * 6e4;
20523
+ }
20524
+ });
20525
+
20526
+ // ../../packages/tacho/src/cli/agent-deps.ts
20527
+ function agentDeps(deps, paths) {
20528
+ if (JSON.stringify(paths) === JSON.stringify(deps.paths)) return deps;
20529
+ const rebound = deps.atAgent?.(paths) ?? { ...deps, paths };
20530
+ return {
20531
+ ...deps,
20532
+ ...rebound,
20533
+ paths,
20534
+ serviceManager: deps.serviceManager,
20535
+ out: deps.out,
20536
+ err: deps.err
20537
+ };
20538
+ }
20539
+ function depsForHarness(deps, harness) {
20540
+ const agent = agentHolding(deps.paths, harness);
20541
+ return agent === void 0 ? deps : depsForAgent(deps, agent);
20542
+ }
20543
+ function depsForAgent(deps, agent) {
20544
+ const host = agent.host ?? readHostFileLenient(agent.paths.hostFile).salvaged;
20545
+ return agentDeps(deps, withRecordedHarnessFiles(agent.paths, host));
20546
+ }
20547
+ var init_agent_deps = __esm({
20548
+ "../../packages/tacho/src/cli/agent-deps.ts"() {
20549
+ "use strict";
20550
+ init_agents();
20551
+ init_host_file();
20552
+ }
20553
+ });
20554
+
20555
+ // ../../packages/tacho/src/cli/claude-code-mcp.ts
20556
+ function canServeOxagenTools(host) {
20557
+ return host.gateway_api_key !== void 0;
20558
+ }
20559
+ function registerClaudeCodeMcp(host, deps) {
20560
+ const edit = deps.editClaudeUserConfig;
20561
+ if (edit === void 0)
20562
+ return {
20563
+ host,
20564
+ result: "skipped",
20565
+ reason: "this build cannot edit Claude Code's user config",
20566
+ displaced: false
20567
+ };
20568
+ if (!canServeOxagenTools(host))
20569
+ return {
20570
+ host,
20571
+ result: "skipped",
20572
+ reason: "this enrollment has no gateway key, so the collector cannot serve Oxagen's tools. Run `oxagen agent enroll --force` to enroll this machine again with one",
20573
+ displaced: false
20574
+ };
20575
+ const path2 = deps.paths.claudeUserConfig;
20576
+ const shim = host.mcp_stdio_command ?? deps.runtime.mcpStdioCommand;
20577
+ let current = host;
20578
+ if (current.harness_files?.claude_user_config !== path2) {
20579
+ current = {
20580
+ ...current,
20581
+ harness_files: { ...current.harness_files, claude_user_config: path2 }
20582
+ };
20583
+ writeHostFile(deps.paths.hostFile, current);
20584
+ }
20585
+ let result2 = "present";
20586
+ let displaced = false;
20587
+ let wroteEntry = false;
20588
+ edit((document) => {
20589
+ const problem = claudeUserConfigShapeProblem(document);
20590
+ if (problem !== void 0) throw new HarnessFileError(path2, problem);
20591
+ const merged = mergeClaudeCodeMcpConfig(document, {
20592
+ enrollmentId: current.host_enrollment_id,
20593
+ port: current.port,
20594
+ localToken: current.local_token,
20595
+ shimCommand: shim[0],
20596
+ shimArgs: shim.slice(1, -1),
20597
+ ...deps.env["TACHO_HOME"] !== void 0 ? { tachoHome: deps.env["TACHO_HOME"] } : {}
20598
+ });
20599
+ if (!merged.changed) return void 0;
20600
+ if (Object.keys(merged.displaced).length > 0) {
20601
+ displaced = true;
20602
+ current = {
20603
+ ...current,
20604
+ displaced_mcp_servers: {
20605
+ ...current.displaced_mcp_servers,
20606
+ [HARNESS]: {
20607
+ ...current.displaced_mcp_servers[HARNESS],
20608
+ // The server the person put under the name most recently wins:
20609
+ // one there now replaced whatever an earlier run moved aside.
20610
+ ...merged.displaced
20611
+ }
20612
+ }
20613
+ };
20614
+ writeHostFile(deps.paths.hostFile, current);
20615
+ }
20616
+ result2 = "written";
20617
+ wroteEntry = true;
20618
+ return merged.config;
20619
+ });
20620
+ if (wroteEntry || current.mcp_registered_at?.[HARNESS] === void 0) {
20621
+ current = {
20622
+ ...current,
20623
+ mcp_registered_at: {
20624
+ ...current.mcp_registered_at,
20625
+ [HARNESS]: new Date(deps.now()).toISOString()
20626
+ }
20627
+ };
20628
+ writeHostFile(deps.paths.hostFile, current);
20629
+ }
20630
+ return { host: current, result: result2, displaced };
20631
+ }
20632
+ function needsClaudeCodeMcp(host) {
20633
+ return host.revoked_at === null && host.harnesses.includes(HARNESS) && canServeOxagenTools(host) && host.mcp_registered_at?.[HARNESS] === void 0;
20634
+ }
20635
+ function addMissingClaudeCodeMcp(deps) {
20636
+ if (deps.editClaudeUserConfig === void 0) return [];
20637
+ const pending = listAgents(deps.paths).filter(
20638
+ (agent) => agentIsLive(agent) && needsClaudeCodeMcp(agent.host)
20639
+ );
20640
+ if (pending.length === 0) return [];
20641
+ const lock = acquireInstallLock(deps.paths.tachoDir, deps.now);
20642
+ if ("heldBy" in lock) return [];
20643
+ const added = [];
20644
+ try {
20645
+ for (const agent of pending) {
20646
+ const own = depsForAgent(deps, agent);
20647
+ const host = readHostFile(own.paths.hostFile);
20648
+ if (host === void 0 || !needsClaudeCodeMcp(host)) continue;
20649
+ const recorded = host.harness_files;
20650
+ if (recorded?.claude_user_config === void 0 && recorded?.claude_settings !== void 0 && recorded.claude_settings !== agent.paths.claudeSettings) {
20651
+ added.push({
20652
+ agentKey: host.agent_key,
20653
+ path: own.paths.claudeUserConfig,
20654
+ ok: false,
20655
+ problem: `this agent was enrolled with Claude Code's settings at ${recorded.claude_settings}, and this command finds them at ${agent.paths.claudeSettings}, so it cannot tell which .claude.json Claude Code reads. Run \`oxagen agent enroll\` from a shell with the CLAUDE_CONFIG_DIR you enrolled with`
20656
+ });
20657
+ continue;
20658
+ }
20659
+ try {
20660
+ const outcome = registerClaudeCodeMcp(host, own);
20661
+ added.push({
20662
+ agentKey: host.agent_key,
20663
+ path: own.paths.claudeUserConfig,
20664
+ ok: outcome.result !== "skipped",
20665
+ ...outcome.reason !== void 0 ? { problem: outcome.reason } : {}
20666
+ });
20667
+ } catch (error) {
20668
+ added.push({
20669
+ agentKey: host.agent_key,
20670
+ path: own.paths.claudeUserConfig,
20671
+ ok: false,
20672
+ problem: error instanceof Error ? error.message : String(error)
20673
+ });
20674
+ }
20675
+ }
20676
+ } finally {
20677
+ lock.release();
20678
+ }
20679
+ return added;
20680
+ }
20681
+ var HARNESS;
20682
+ var init_claude_code_mcp = __esm({
20683
+ "../../packages/tacho/src/cli/claude-code-mcp.ts"() {
20684
+ "use strict";
20685
+ init_agents();
20686
+ init_claude_code_mcp_writer();
20687
+ init_harness_file();
20688
+ init_host_file();
20689
+ init_install_lock();
20690
+ init_agent_deps();
20691
+ HARNESS = "claude-code";
20692
+ }
20693
+ });
20694
+
20369
20695
  // ../../packages/tacho/src/host/model-base-url-toml.ts
20370
20696
  function splitLines(text4) {
20371
20697
  const lines = [];
@@ -20479,13 +20805,13 @@ var init_model_base_url_toml = __esm({
20479
20805
  import { createHash as createHash5 } from "node:crypto";
20480
20806
  import {
20481
20807
  chmodSync as chmodSync3,
20482
- existsSync as existsSync4,
20483
- mkdirSync as mkdirSync4,
20484
- readFileSync as readFileSync5,
20808
+ existsSync as existsSync5,
20809
+ mkdirSync as mkdirSync5,
20810
+ readFileSync as readFileSync6,
20485
20811
  statSync as statSync3,
20486
- unlinkSync as unlinkSync3
20812
+ unlinkSync as unlinkSync4
20487
20813
  } from "node:fs";
20488
- import { basename as basename3, dirname as dirname4, join as join12 } from "node:path";
20814
+ import { basename as basename3, dirname as dirname5, join as join13 } from "node:path";
20489
20815
  function claudeToolSearchEnabled(value) {
20490
20816
  return typeof value === "string" && /^(true|auto(:\d+)?)$/i.test(value);
20491
20817
  }
@@ -20499,19 +20825,19 @@ function isModelProxyBaseUrl(harness, value) {
20499
20825
  }
20500
20826
  function modelBaseUrlFile(harness, homes) {
20501
20827
  if (harness === "claude-code")
20502
- return join12(
20828
+ return join13(
20503
20829
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
20504
20830
  "settings.json"
20505
20831
  );
20506
20832
  if (harness === "codex")
20507
- return join12(
20833
+ return join13(
20508
20834
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
20509
20835
  "config.toml"
20510
20836
  );
20511
- const stellaHome = homes.stellaHome ?? join12(homes.home, ".stella");
20512
- const toml = join12(stellaHome, "stella.toml");
20513
- const json2 = join12(stellaHome, "settings.json");
20514
- return !existsSync4(toml) && existsSync4(json2) ? json2 : toml;
20837
+ const stellaHome = homes.stellaHome ?? join13(homes.home, ".stella");
20838
+ const toml = join13(stellaHome, "stella.toml");
20839
+ const json2 = join13(stellaHome, "settings.json");
20840
+ return !existsSync5(toml) && existsSync5(json2) ? json2 : toml;
20515
20841
  }
20516
20842
  function keyLabel(harness) {
20517
20843
  if (harness === "claude-code") return `env.${CLAUDE_KEY}`;
@@ -20521,7 +20847,7 @@ function isJsonFile(file) {
20521
20847
  return file.endsWith(".json");
20522
20848
  }
20523
20849
  function sidecarFor(file) {
20524
- return join12(dirname4(file), `.${basename3(file)}.oxagen-model-base-url.json`);
20850
+ return join13(dirname5(file), `.${basename3(file)}.oxagen-model-base-url.json`);
20525
20851
  }
20526
20852
  function modelBaseUrlBackupPath(harness, home, stellaHome, dirs = {}) {
20527
20853
  return sidecarFor(fileFor(harness, homesOf(home, stellaHome, dirs)));
@@ -20538,9 +20864,9 @@ function sha256(text4) {
20538
20864
  return createHash5("sha256").update(text4, "utf8").digest("hex");
20539
20865
  }
20540
20866
  function readSidecar(path2) {
20541
- if (!existsSync4(path2)) return void 0;
20867
+ if (!existsSync5(path2)) return void 0;
20542
20868
  try {
20543
- const parsed = JSON.parse(readFileSync5(path2, "utf8"));
20869
+ const parsed = JSON.parse(readFileSync6(path2, "utf8"));
20544
20870
  if (parsed.schema !== SIDECAR_SCHEMA) return void 0;
20545
20871
  if (typeof parsed.written_sha256 !== "string") return void 0;
20546
20872
  return {
@@ -20565,7 +20891,7 @@ function readSidecar(path2) {
20565
20891
  }
20566
20892
  }
20567
20893
  function writeAtomicPreserving(path2, data) {
20568
- mkdirSync4(dirname4(path2), { recursive: true });
20894
+ mkdirSync5(dirname5(path2), { recursive: true });
20569
20895
  let mode = 384;
20570
20896
  let owner;
20571
20897
  try {
@@ -20581,7 +20907,7 @@ function writeAtomicPreserving(path2, data) {
20581
20907
  }
20582
20908
  function readTextIfExists(path2) {
20583
20909
  try {
20584
- return readFileSync5(path2, "utf8");
20910
+ return readFileSync6(path2, "utf8");
20585
20911
  } catch (error) {
20586
20912
  if (error.code === "ENOENT") return void 0;
20587
20913
  throw error;
@@ -20672,9 +20998,9 @@ function stellaManagedSettingsPath(platform = process.platform, env = process.en
20672
20998
  const named = env["STELLA_MANAGED_SETTINGS"];
20673
20999
  if (named !== void 0 && named.length > 0) return named;
20674
21000
  const dir = platform === "darwin" ? "/Library/Application Support/stella" : "/etc/stella";
20675
- const toml = join12(dir, "stella.toml");
20676
- const json2 = join12(dir, "settings.json");
20677
- return !existsSync4(toml) && existsSync4(json2) ? json2 : toml;
21001
+ const toml = join13(dir, "stella.toml");
21002
+ const json2 = join13(dir, "settings.json");
21003
+ return !existsSync5(toml) && existsSync5(json2) ? json2 : toml;
20678
21004
  }
20679
21005
  function managedShadow(harness, managedFile, expected) {
20680
21006
  try {
@@ -20933,7 +21259,7 @@ function applyOne(harness, options) {
20933
21259
  original_base64: orphan ? "" : (
20934
21260
  // The raw bytes, not the decoded text: restore is byte-exact even
20935
21261
  // for a file that is not clean UTF-8.
20936
- (text4 === void 0 ? Buffer.alloc(0) : readFileSync5(file)).toString("base64")
21262
+ (text4 === void 0 ? Buffer.alloc(0) : readFileSync6(file)).toString("base64")
20937
21263
  ),
20938
21264
  written_sha256: orphan ? "" : sha256(next),
20939
21265
  previous,
@@ -20954,7 +21280,7 @@ function restoreOne(harness, options) {
20954
21280
  const sidecar = readSidecar(backup);
20955
21281
  const text4 = readTextIfExists(file);
20956
21282
  const dropSidecar = () => {
20957
- if (existsSync4(backup)) unlinkSync3(backup);
21283
+ if (existsSync5(backup)) unlinkSync4(backup);
20958
21284
  };
20959
21285
  if (text4 === void 0) {
20960
21286
  dropSidecar();
@@ -20967,7 +21293,7 @@ function restoreOne(harness, options) {
20967
21293
  Buffer.from(sidecar.original_base64, "base64")
20968
21294
  );
20969
21295
  } else {
20970
- unlinkSync3(file);
21296
+ unlinkSync4(file);
20971
21297
  }
20972
21298
  dropSidecar();
20973
21299
  return true;
@@ -21069,8 +21395,8 @@ var init_model_base_url = __esm({
21069
21395
  });
21070
21396
 
21071
21397
  // ../../packages/tacho/src/host/run-token.ts
21072
- import { createHmac, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
21073
- import { readFileSync as readFileSync6 } from "node:fs";
21398
+ import { createHmac, randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
21399
+ import { readFileSync as readFileSync7 } from "node:fs";
21074
21400
  function keyFromBytes(bytes) {
21075
21401
  if (bytes.length !== KEY_BYTES)
21076
21402
  throw new Error(
@@ -21080,7 +21406,7 @@ function keyFromBytes(bytes) {
21080
21406
  return { bytes, id };
21081
21407
  }
21082
21408
  function generateRunTokenKey() {
21083
- return keyFromBytes(randomBytes3(KEY_BYTES));
21409
+ return keyFromBytes(randomBytes4(KEY_BYTES));
21084
21410
  }
21085
21411
  function runTokenKeyFromHex(hex) {
21086
21412
  const trimmed = hex.trim();
@@ -21091,7 +21417,7 @@ function runTokenKeyFromHex(hex) {
21091
21417
  function loadOrCreateRunTokenKey(path2) {
21092
21418
  try {
21093
21419
  return {
21094
- key: runTokenKeyFromHex(readFileSync6(path2, "utf8")),
21420
+ key: runTokenKeyFromHex(readFileSync7(path2, "utf8")),
21095
21421
  created: false
21096
21422
  };
21097
21423
  } catch (error) {
@@ -21104,7 +21430,7 @@ function loadOrCreateRunTokenKey(path2) {
21104
21430
  }
21105
21431
  function readRunTokenKey(path2) {
21106
21432
  try {
21107
- return runTokenKeyFromHex(readFileSync6(path2, "utf8"));
21433
+ return runTokenKeyFromHex(readFileSync7(path2, "utf8"));
21108
21434
  } catch (error) {
21109
21435
  if (error.code === "ENOENT") return void 0;
21110
21436
  throw error;
@@ -21127,7 +21453,7 @@ function mintRunToken(options) {
21127
21453
  throw new Error("run token would already be expired when minted");
21128
21454
  const claims = {
21129
21455
  v: 1,
21130
- tid: `rt_${randomBytes3(10).toString("hex")}`,
21456
+ tid: `rt_${randomBytes4(10).toString("hex")}`,
21131
21457
  host: options.host,
21132
21458
  harness: options.harness,
21133
21459
  provider: options.provider,
@@ -21238,14 +21564,14 @@ var init_run_token = __esm({
21238
21564
  import { createHash as createHash6 } from "node:crypto";
21239
21565
  import {
21240
21566
  chmodSync as chmodSync4,
21241
- existsSync as existsSync5,
21567
+ existsSync as existsSync6,
21242
21568
  lstatSync as lstatSync2,
21243
- mkdirSync as mkdirSync5,
21244
- readFileSync as readFileSync7,
21569
+ mkdirSync as mkdirSync6,
21570
+ readFileSync as readFileSync8,
21245
21571
  statSync as statSync4,
21246
- unlinkSync as unlinkSync4
21572
+ unlinkSync as unlinkSync5
21247
21573
  } from "node:fs";
21248
- import { basename as basename4, dirname as dirname5, join as join13 } from "node:path";
21574
+ import { basename as basename4, dirname as dirname6, join as join14 } from "node:path";
21249
21575
  function isTachoHelper(value) {
21250
21576
  return typeof value === "string" && HELPER_MARK.test(value);
21251
21577
  }
@@ -21253,16 +21579,16 @@ function helperCommandFor(tachoCommand) {
21253
21579
  return `${tachoCommand} credential issue --harness claude-code`;
21254
21580
  }
21255
21581
  function fileFor2(harness, homes) {
21256
- return harness === "claude-code" ? join13(
21582
+ return harness === "claude-code" ? join14(
21257
21583
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
21258
21584
  "settings.json"
21259
- ) : join13(
21585
+ ) : join14(
21260
21586
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
21261
21587
  "auth.json"
21262
21588
  );
21263
21589
  }
21264
21590
  function sidecarFor2(file) {
21265
- return join13(dirname5(file), `.${basename4(file)}.oxagen-model-credential.json`);
21591
+ return join14(dirname6(file), `.${basename4(file)}.oxagen-model-credential.json`);
21266
21592
  }
21267
21593
  function modelCredentialBackupPath(harness, home, dirs = {}) {
21268
21594
  return sidecarFor2(fileFor2(harness, { ...dirs, home }));
@@ -21271,9 +21597,9 @@ function sha2562(text4) {
21271
21597
  return createHash6("sha256").update(text4, "utf8").digest("hex");
21272
21598
  }
21273
21599
  function readSidecar2(path2) {
21274
- if (!existsSync5(path2)) return void 0;
21600
+ if (!existsSync6(path2)) return void 0;
21275
21601
  try {
21276
- const parsed = JSON.parse(readFileSync7(path2, "utf8"));
21602
+ const parsed = JSON.parse(readFileSync8(path2, "utf8"));
21277
21603
  if (parsed.schema !== SIDECAR_SCHEMA2) return void 0;
21278
21604
  if (typeof parsed.written_sha256 !== "string") return void 0;
21279
21605
  return {
@@ -21293,7 +21619,7 @@ function readSidecar2(path2) {
21293
21619
  }
21294
21620
  }
21295
21621
  function writeAtomicPreserving2(path2, data) {
21296
- mkdirSync5(dirname5(path2), { recursive: true });
21622
+ mkdirSync6(dirname6(path2), { recursive: true });
21297
21623
  let mode = 384;
21298
21624
  let owner;
21299
21625
  try {
@@ -21309,7 +21635,7 @@ function writeAtomicPreserving2(path2, data) {
21309
21635
  }
21310
21636
  function readTextIfExists2(path2) {
21311
21637
  try {
21312
- return readFileSync7(path2, "utf8");
21638
+ return readFileSync8(path2, "utf8");
21313
21639
  } catch (error) {
21314
21640
  if (error.code === "ENOENT") return void 0;
21315
21641
  throw error;
@@ -21542,7 +21868,7 @@ function restoreClaude(options, released) {
21542
21868
  const sidecar = readSidecar2(backup);
21543
21869
  const text4 = readTextIfExists2(file);
21544
21870
  const dropSidecar = () => {
21545
- if (existsSync5(backup)) unlinkSync4(backup);
21871
+ if (existsSync6(backup)) unlinkSync5(backup);
21546
21872
  };
21547
21873
  if (text4 === void 0) {
21548
21874
  if (released !== void 0) {
@@ -21599,7 +21925,7 @@ function restoreCodex(options, released) {
21599
21925
  const sidecar = readSidecar2(backup);
21600
21926
  const text4 = readTextIfExists2(file);
21601
21927
  const dropSidecar = () => {
21602
- if (existsSync5(backup)) unlinkSync4(backup);
21928
+ if (existsSync6(backup)) unlinkSync5(backup);
21603
21929
  };
21604
21930
  if (text4 === void 0) {
21605
21931
  if (released !== void 0) {
@@ -21626,7 +21952,7 @@ function restoreCodex(options, released) {
21626
21952
  if (released !== void 0) auth[CODEX_KEY2] = released.secret;
21627
21953
  else delete auth[CODEX_KEY2];
21628
21954
  if (sidecar !== void 0 && !sidecar.existed && Object.keys(auth).length === 0) {
21629
- unlinkSync4(file);
21955
+ unlinkSync5(file);
21630
21956
  dropSidecar();
21631
21957
  return { changed: true, secretRestored: released !== void 0 };
21632
21958
  }
@@ -21771,42 +22097,13 @@ var init_model_credential = __esm({
21771
22097
  }
21772
22098
  });
21773
22099
 
21774
- // ../../packages/tacho/src/cli/agent-deps.ts
21775
- function agentDeps(deps, paths) {
21776
- if (JSON.stringify(paths) === JSON.stringify(deps.paths)) return deps;
21777
- const rebound = deps.atAgent?.(paths) ?? { ...deps, paths };
21778
- return {
21779
- ...deps,
21780
- ...rebound,
21781
- paths,
21782
- serviceManager: deps.serviceManager,
21783
- out: deps.out,
21784
- err: deps.err
21785
- };
21786
- }
21787
- function depsForHarness(deps, harness) {
21788
- const agent = agentHolding(deps.paths, harness);
21789
- return agent === void 0 ? deps : depsForAgent(deps, agent);
21790
- }
21791
- function depsForAgent(deps, agent) {
21792
- const host = agent.host ?? readHostFileLenient(agent.paths.hostFile).salvaged;
21793
- return agentDeps(deps, withRecordedHarnessFiles(agent.paths, host));
21794
- }
21795
- var init_agent_deps = __esm({
21796
- "../../packages/tacho/src/cli/agent-deps.ts"() {
21797
- "use strict";
21798
- init_agents();
21799
- init_host_file();
21800
- }
21801
- });
21802
-
21803
22100
  // ../../packages/tacho/src/cli/credential.ts
21804
- import { existsSync as existsSync6 } from "node:fs";
21805
- import { dirname as dirname6 } from "node:path";
22101
+ import { existsSync as existsSync7 } from "node:fs";
22102
+ import { dirname as dirname7 } from "node:path";
21806
22103
  function harnessDirsOf(deps) {
21807
22104
  return {
21808
- claudeConfigDir: dirname6(deps.paths.claudeSettings),
21809
- codexHome: dirname6(deps.paths.codexHooks)
22105
+ claudeConfigDir: dirname7(deps.paths.claudeSettings),
22106
+ codexHome: dirname7(deps.paths.codexHooks)
21810
22107
  };
21811
22108
  }
21812
22109
  function parseCredentialMode(value) {
@@ -22057,7 +22354,7 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
22057
22354
  try {
22058
22355
  if (host !== void 0 && !host.harnesses.includes(harness)) {
22059
22356
  const dirs = harnessDirsOf(deps);
22060
- const hasReceipt = existsSync6(
22357
+ const hasReceipt = existsSync7(
22061
22358
  modelCredentialBackupPath(harness, deps.home, dirs)
22062
22359
  );
22063
22360
  if (!hasReceipt && !hasOrphanedModelCredential(harness, deps.home, dirs))
@@ -22288,20 +22585,20 @@ var init_codex_app_server = __esm({
22288
22585
  });
22289
22586
 
22290
22587
  // ../../packages/tacho/src/host/credential-store.ts
22291
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes4 } from "node:crypto";
22588
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
22292
22589
  import {
22293
- closeSync as closeSync3,
22294
- existsSync as existsSync7,
22590
+ closeSync as closeSync4,
22591
+ existsSync as existsSync8,
22295
22592
  fsyncSync as fsyncSync2,
22296
- openSync as openSync3,
22297
- readFileSync as readFileSync8,
22593
+ openSync as openSync4,
22594
+ readFileSync as readFileSync9,
22298
22595
  statSync as statSync5,
22299
- unlinkSync as unlinkSync5,
22300
- writeSync as writeSync2
22596
+ unlinkSync as unlinkSync6,
22597
+ writeSync as writeSync3
22301
22598
  } from "node:fs";
22302
22599
  function readKey(path2) {
22303
22600
  try {
22304
- const hex = readFileSync8(path2, "utf8").trim();
22601
+ const hex = readFileSync9(path2, "utf8").trim();
22305
22602
  if (!/^[0-9a-f]{64}$/i.test(hex))
22306
22603
  throw new Error(`${path2} does not hold a 32-byte key`);
22307
22604
  return Buffer.from(hex, "hex");
@@ -22313,7 +22610,7 @@ function readKey(path2) {
22313
22610
  function loadOrCreateKey(path2) {
22314
22611
  const existing = readKey(path2);
22315
22612
  if (existing !== void 0) return existing;
22316
- const key = randomBytes4(KEY_BYTES2);
22613
+ const key = randomBytes5(KEY_BYTES2);
22317
22614
  writeSensitiveFileAtomic(path2, `${key.toString("hex")}
22318
22615
  `);
22319
22616
  return key;
@@ -22330,16 +22627,16 @@ function overwriteInPlace(path2) {
22330
22627
  let fd2;
22331
22628
  try {
22332
22629
  const size = statSync5(path2).size;
22333
- fd2 = openSync3(path2, "r+");
22334
- writeSync2(fd2, randomBytes4(Math.max(size, KEY_BYTES2 * 2)), 0, void 0, 0);
22630
+ fd2 = openSync4(path2, "r+");
22631
+ writeSync3(fd2, randomBytes5(Math.max(size, KEY_BYTES2 * 2)), 0, void 0, 0);
22335
22632
  fsyncSync2(fd2);
22336
22633
  } catch {
22337
22634
  } finally {
22338
- if (fd2 !== void 0) closeSync3(fd2);
22635
+ if (fd2 !== void 0) closeSync4(fd2);
22339
22636
  }
22340
22637
  }
22341
22638
  function seal(key, secret, aad) {
22342
- const nonce = randomBytes4(12);
22639
+ const nonce = randomBytes5(12);
22343
22640
  const cipher = createCipheriv("aes-256-gcm", key, nonce);
22344
22641
  cipher.setAAD(Buffer.from(aad, "utf8"));
22345
22642
  const ciphertext = Buffer.concat([
@@ -22384,7 +22681,7 @@ function openCredentialStore(paths) {
22384
22681
  function readAll() {
22385
22682
  let text4;
22386
22683
  try {
22387
- text4 = readFileSync8(paths.file, "utf8");
22684
+ text4 = readFileSync9(paths.file, "utf8");
22388
22685
  } catch (error) {
22389
22686
  if (error.code === "ENOENT") return [];
22390
22687
  throw error;
@@ -22398,7 +22695,7 @@ function openCredentialStore(paths) {
22398
22695
  }
22399
22696
  function writeAll2(entries2) {
22400
22697
  if (entries2.length === 0) {
22401
- if (existsSync7(paths.file)) unlinkSync5(paths.file);
22698
+ if (existsSync8(paths.file)) unlinkSync6(paths.file);
22402
22699
  return;
22403
22700
  }
22404
22701
  writeSensitiveFileAtomic(
@@ -22453,9 +22750,9 @@ function openCredentialStore(paths) {
22453
22750
  },
22454
22751
  status: () => readAll().map(custodyOf),
22455
22752
  shred: () => {
22456
- if (existsSync7(paths.key)) overwriteInPlace(paths.key);
22753
+ if (existsSync8(paths.key)) overwriteInPlace(paths.key);
22457
22754
  for (const path2 of [paths.file, paths.key])
22458
- if (existsSync7(path2)) unlinkSync5(path2);
22755
+ if (existsSync8(path2)) unlinkSync6(path2);
22459
22756
  }
22460
22757
  };
22461
22758
  }
@@ -22499,16 +22796,87 @@ var init_credential_store = __esm({
22499
22796
  }
22500
22797
  });
22501
22798
 
22799
+ // ../../packages/tacho/src/host/claude-config-lock.ts
22800
+ import { mkdirSync as mkdirSync7, rmdirSync as rmdirSync4, statSync as statSync6 } from "node:fs";
22801
+ function sleepSync(ms) {
22802
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
22803
+ }
22804
+ function claudeConfigLockPath(file) {
22805
+ return `${file}.lock`;
22806
+ }
22807
+ function withClaudeConfigLock(file, edit, options = {}) {
22808
+ const lock = claudeConfigLockPath(file);
22809
+ const now = options.now ?? Date.now;
22810
+ const sleep3 = options.sleep ?? sleepSync;
22811
+ const staleMs = options.staleMs ?? CLAUDE_CONFIG_LOCK_STALE_MS;
22812
+ const waitMs = options.waitMs ?? CLAUDE_CONFIG_LOCK_WAIT_MS;
22813
+ const deadline = now() + waitMs;
22814
+ let held = false;
22815
+ for (; ; ) {
22816
+ try {
22817
+ mkdirSync7(lock);
22818
+ held = true;
22819
+ break;
22820
+ } catch (error) {
22821
+ const code = error.code;
22822
+ if (code === "ENOENT") break;
22823
+ if (code !== "EEXIST") throw error;
22824
+ }
22825
+ let modified;
22826
+ try {
22827
+ modified = statSync6(lock).mtimeMs;
22828
+ } catch {
22829
+ modified = void 0;
22830
+ }
22831
+ let retryNow = modified === void 0;
22832
+ if (modified !== void 0 && now() - modified > staleMs) {
22833
+ try {
22834
+ if (statSync6(lock).mtimeMs === modified) {
22835
+ rmdirSync4(lock);
22836
+ retryNow = true;
22837
+ }
22838
+ } catch {
22839
+ }
22840
+ }
22841
+ if (now() >= deadline)
22842
+ throw new HarnessFileError(
22843
+ file,
22844
+ `is being saved by Claude Code, which held its lock (${lock}) for more than ${Math.round(waitMs / 1e3)} seconds; nothing was written, so run the command again in a moment`
22845
+ );
22846
+ if (!retryNow) sleep3(RETRY_MS);
22847
+ }
22848
+ try {
22849
+ return edit();
22850
+ } finally {
22851
+ if (held) {
22852
+ try {
22853
+ rmdirSync4(lock);
22854
+ } catch {
22855
+ }
22856
+ }
22857
+ }
22858
+ }
22859
+ var CLAUDE_CONFIG_LOCK_STALE_MS, CLAUDE_CONFIG_LOCK_WAIT_MS, RETRY_MS;
22860
+ var init_claude_config_lock = __esm({
22861
+ "../../packages/tacho/src/host/claude-config-lock.ts"() {
22862
+ "use strict";
22863
+ init_harness_file();
22864
+ CLAUDE_CONFIG_LOCK_STALE_MS = 1e4;
22865
+ CLAUDE_CONFIG_LOCK_WAIT_MS = 3e3;
22866
+ RETRY_MS = 25;
22867
+ }
22868
+ });
22869
+
22502
22870
  // ../../packages/tacho/src/host/service.ts
22503
- import { join as join14 } from "node:path";
22871
+ import { join as join15 } from "node:path";
22504
22872
  import {
22505
- existsSync as existsSync8,
22506
- readdirSync as readdirSync5,
22507
- readFileSync as readFileSync9,
22508
- rmdirSync as rmdirSync3,
22509
- unlinkSync as unlinkSync6
22873
+ existsSync as existsSync9,
22874
+ readdirSync as readdirSync6,
22875
+ readFileSync as readFileSync10,
22876
+ rmdirSync as rmdirSync5,
22877
+ unlinkSync as unlinkSync7
22510
22878
  } from "node:fs";
22511
- import { randomBytes as randomBytes5 } from "node:crypto";
22879
+ import { randomBytes as randomBytes6 } from "node:crypto";
22512
22880
  function xmlEscape(value) {
22513
22881
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22514
22882
  }
@@ -22580,8 +22948,8 @@ function blockingSleep(ms) {
22580
22948
  }
22581
22949
  function launchdManager(options) {
22582
22950
  const sleep3 = options.sleep ?? blockingSleep;
22583
- const dir = join14(options.home, "Library", "LaunchAgents");
22584
- const unitPath = join14(dir, `${SERVICE_LABEL}.plist`);
22951
+ const dir = join15(options.home, "Library", "LaunchAgents");
22952
+ const unitPath = join15(dir, `${SERVICE_LABEL}.plist`);
22585
22953
  const domain = `gui/${options.uid ?? process.getuid?.() ?? 501}`;
22586
22954
  const target2 = `${domain}/${SERVICE_LABEL}`;
22587
22955
  const loaded = () => options.exec("launchctl", ["print", target2]).status === 0;
@@ -22600,7 +22968,7 @@ function launchdManager(options) {
22600
22968
  unitPath,
22601
22969
  install: (spec) => {
22602
22970
  const plist = renderLaunchdPlist(spec);
22603
- const unchanged = existsSync8(unitPath) && readFileSync9(unitPath, "utf8") === plist;
22971
+ const unchanged = existsSync9(unitPath) && readFileSync10(unitPath, "utf8") === plist;
22604
22972
  if (unchanged && loaded()) {
22605
22973
  const kicked = options.exec("launchctl", ["kickstart", "-k", target2]);
22606
22974
  if (kicked.status === 0 && loaded()) return;
@@ -22629,10 +22997,10 @@ function launchdManager(options) {
22629
22997
  uninstall: () => {
22630
22998
  options.exec("launchctl", ["bootout", target2]);
22631
22999
  if (!waitUntilGone()) throw stillLoaded("unenroll");
22632
- if (existsSync8(unitPath)) unlinkSync6(unitPath);
23000
+ if (existsSync9(unitPath)) unlinkSync7(unitPath);
22633
23001
  },
22634
23002
  status: () => {
22635
- const installed = existsSync8(unitPath);
23003
+ const installed = existsSync9(unitPath);
22636
23004
  const result2 = options.exec("launchctl", [
22637
23005
  "print",
22638
23006
  `${domain}/${SERVICE_LABEL}`
@@ -22649,8 +23017,8 @@ function noUserManager(result2) {
22649
23017
  }
22650
23018
  function stopByPidFile(options) {
22651
23019
  const { pidPath } = options;
22652
- if (pidPath === void 0 || !existsSync8(pidPath)) return;
22653
- const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
23020
+ if (pidPath === void 0 || !existsSync9(pidPath)) return;
23021
+ const record = parseDaemonPid(readFileSync10(pidPath, "utf8"));
22654
23022
  if (record === void 0) return;
22655
23023
  const sleep3 = options.sleep ?? blockingSleep;
22656
23024
  const processes = options.processes ?? {
@@ -22682,9 +23050,9 @@ function stopByPidFile(options) {
22682
23050
  );
22683
23051
  }
22684
23052
  function systemdManager(options) {
22685
- const dir = join14(options.home, ".config", "systemd", "user");
22686
- const unitPath = join14(dir, "tachod.service");
22687
- const wants = join14(dir, "default.target.wants");
23053
+ const dir = join15(options.home, ".config", "systemd", "user");
23054
+ const unitPath = join15(dir, "tachod.service");
23055
+ const wants = join15(dir, "default.target.wants");
22688
23056
  return {
22689
23057
  kind: "systemd",
22690
23058
  unitPath,
@@ -22694,8 +23062,8 @@ function systemdManager(options) {
22694
23062
  throw new Error(
22695
23063
  `no systemd user manager is available (${probe.stderr.trim().split("\n")[0] ?? ""})`
22696
23064
  );
22697
- const previous = existsSync8(unitPath) ? readFileSync9(unitPath, "utf8") : void 0;
22698
- const createsWants = previous === void 0 ? !existsSync8(wants) : previous.includes(CREATED_WANTS_MARK);
23065
+ const previous = existsSync9(unitPath) ? readFileSync10(unitPath, "utf8") : void 0;
23066
+ const createsWants = previous === void 0 ? !existsSync9(wants) : previous.includes(CREATED_WANTS_MARK);
22699
23067
  ensureDir(dir, 493);
22700
23068
  writeSensitiveFileAtomic(
22701
23069
  unitPath,
@@ -22735,7 +23103,7 @@ function systemdManager(options) {
22735
23103
  ]);
22736
23104
  if (noUserManager(disabled)) {
22737
23105
  stopByPidFile(options);
22738
- if (existsSync8(unitPath)) unlinkSync6(unitPath);
23106
+ if (existsSync9(unitPath)) unlinkSync7(unitPath);
22739
23107
  return;
22740
23108
  }
22741
23109
  const active2 = options.exec("systemctl", [
@@ -22744,13 +23112,13 @@ function systemdManager(options) {
22744
23112
  "tachod.service"
22745
23113
  ]);
22746
23114
  const stopped2 = active2.status === 3 && ["inactive", "failed"].includes(active2.stdout.trim()) || active2.status === 4 && active2.stdout.trim() === "unknown";
22747
- if (!stopped2 || disabled.status !== 0 && existsSync8(unitPath)) {
23115
+ if (!stopped2 || disabled.status !== 0 && existsSync9(unitPath)) {
22748
23116
  throw new Error(
22749
23117
  `systemctl could not remove tachod.service: ${disabled.stderr.trim() || active2.stderr.trim() || active2.stdout.trim() || "service state is unknown"}`
22750
23118
  );
22751
23119
  }
22752
- const unit = existsSync8(unitPath) ? readFileSync9(unitPath, "utf8") : void 0;
22753
- if (unit !== void 0) unlinkSync6(unitPath);
23120
+ const unit = existsSync9(unitPath) ? readFileSync10(unitPath, "utf8") : void 0;
23121
+ if (unit !== void 0) unlinkSync7(unitPath);
22754
23122
  try {
22755
23123
  const reload = options.exec("systemctl", ["--user", "daemon-reload"]);
22756
23124
  if (reload.status !== 0)
@@ -22763,13 +23131,13 @@ function systemdManager(options) {
22763
23131
  }
22764
23132
  if (unit?.includes(CREATED_WANTS_MARK) === true) {
22765
23133
  try {
22766
- if (readdirSync5(wants).length === 0) rmdirSync3(wants);
23134
+ if (readdirSync6(wants).length === 0) rmdirSync5(wants);
22767
23135
  } catch {
22768
23136
  }
22769
23137
  }
22770
23138
  },
22771
23139
  status: () => {
22772
- const installed = existsSync8(unitPath);
23140
+ const installed = existsSync9(unitPath);
22773
23141
  const result2 = options.exec("systemctl", [
22774
23142
  "--user",
22775
23143
  "is-active",
@@ -22821,11 +23189,11 @@ function tasklistImage(listing, pid) {
22821
23189
  return void 0;
22822
23190
  }
22823
23191
  function schtasksManager(options) {
22824
- const launcher = options.launcherPath ?? join14(options.home, "tachod.cmd");
22825
- const pidPath = options.pidPath ?? join14(launcher, "..", "tachod.pid");
23192
+ const launcher = options.launcherPath ?? join15(options.home, "tachod.cmd");
23193
+ const pidPath = options.pidPath ?? join15(launcher, "..", "tachod.pid");
22826
23194
  const livePid = () => {
22827
- if (!existsSync8(pidPath)) return void 0;
22828
- const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
23195
+ if (!existsSync9(pidPath)) return void 0;
23196
+ const record = parseDaemonPid(readFileSync10(pidPath, "utf8"));
22829
23197
  if (record === void 0) return void 0;
22830
23198
  const { pid } = record;
22831
23199
  const query = options.exec("tasklist", [
@@ -22864,7 +23232,7 @@ function schtasksManager(options) {
22864
23232
  unitPath: launcher,
22865
23233
  install: (spec) => {
22866
23234
  stopDaemon();
22867
- ensureDir(join14(launcher, ".."), 493);
23235
+ ensureDir(join15(launcher, ".."), 493);
22868
23236
  writeSensitiveFileAtomic(
22869
23237
  launcher,
22870
23238
  renderWindowsLauncher(
@@ -22876,7 +23244,7 @@ function schtasksManager(options) {
22876
23244
  // an empty directory stayed behind (#4317). The daemon uses
22877
23245
  // absolute paths only.
22878
23246
  { ...spec, workingDirectory: options.home },
22879
- randomBytes5(8).toString("hex")
23247
+ randomBytes6(8).toString("hex")
22880
23248
  ),
22881
23249
  384
22882
23250
  );
@@ -22918,7 +23286,7 @@ function schtasksManager(options) {
22918
23286
  `schtasks /Delete failed: ${deleted.stderr.trim() || "task removal was not confirmed"}`
22919
23287
  );
22920
23288
  }
22921
- if (existsSync8(launcher)) unlinkSync6(launcher);
23289
+ if (existsSync9(launcher)) unlinkSync7(launcher);
22922
23290
  },
22923
23291
  status: () => {
22924
23292
  const result2 = options.exec("schtasks", [
@@ -22991,15 +23359,15 @@ var init_service = __esm({
22991
23359
  });
22992
23360
 
22993
23361
  // ../../packages/tacho/src/version.ts
22994
- import { readFileSync as readFileSync10 } from "node:fs";
22995
- import { dirname as dirname7, resolve as resolve2 } from "node:path";
23362
+ import { readFileSync as readFileSync11 } from "node:fs";
23363
+ import { dirname as dirname8, resolve as resolve2 } from "node:path";
22996
23364
  import { fileURLToPath } from "node:url";
22997
23365
  function readVersion() {
22998
- if (true) return "2.1.4-443";
23366
+ if (true) return "2.1.4-453";
22999
23367
  try {
23000
- const here = dirname7(fileURLToPath(import.meta.url));
23368
+ const here = dirname8(fileURLToPath(import.meta.url));
23001
23369
  const pkg = JSON.parse(
23002
- readFileSync10(resolve2(here, "..", "package.json"), "utf8")
23370
+ readFileSync11(resolve2(here, "..", "package.json"), "utf8")
23003
23371
  );
23004
23372
  return pkg.version ?? "0.0.0";
23005
23373
  } catch {
@@ -23016,8 +23384,8 @@ var init_version = __esm({
23016
23384
 
23017
23385
  // ../../packages/tacho/src/cli/deps.ts
23018
23386
  import { spawnSync as spawnSync4 } from "node:child_process";
23019
- import { randomBytes as randomBytes6 } from "node:crypto";
23020
- import { existsSync as existsSync9 } from "node:fs";
23387
+ import { randomBytes as randomBytes7 } from "node:crypto";
23388
+ import { existsSync as existsSync10 } from "node:fs";
23021
23389
  import { get as httpGet, request as httpRequest } from "node:http";
23022
23390
  import { createServer } from "node:net";
23023
23391
  import {
@@ -23028,7 +23396,7 @@ import {
23028
23396
  userInfo
23029
23397
  } from "node:os";
23030
23398
  import { createRequire } from "node:module";
23031
- import { dirname as dirname8, posix as posix2, resolve as resolve3, win32 as win323 } from "node:path";
23399
+ import { dirname as dirname9, posix as posix2, resolve as resolve3, win32 as win323 } from "node:path";
23032
23400
  import { fileURLToPath as fileURLToPath2 } from "node:url";
23033
23401
  function configPicker(env, home) {
23034
23402
  let config = {};
@@ -23094,7 +23462,7 @@ function isNativeBuild() {
23094
23462
  function exeName(name, platform) {
23095
23463
  return platform === "win32" ? `${name}.exe` : name;
23096
23464
  }
23097
- function stableExecutablePath(executable, platform, exists = existsSync9) {
23465
+ function stableExecutablePath(executable, platform, exists = existsSync10) {
23098
23466
  const stable = platform === "win32" ? executable.replace(
23099
23467
  /^(.*\\apps\\[^\\]+\\)(?!current\\)[^\\]+(\\.*)$/i,
23100
23468
  "$1current$2"
@@ -23104,7 +23472,7 @@ function stableExecutablePath(executable, platform, exists = existsSync9) {
23104
23472
  );
23105
23473
  return stable !== executable && exists(stable) ? stable : executable;
23106
23474
  }
23107
- function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath = process.execPath, platform = process.platform, native = isNativeBuild(), exists = existsSync9) {
23475
+ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath = process.execPath, platform = process.platform, native = isNativeBuild(), exists = existsSync10) {
23108
23476
  const P = platform === "win32" ? win323 : posix2;
23109
23477
  const named = env["TACHO_BIN_DIR"];
23110
23478
  let binDir = named;
@@ -23112,7 +23480,7 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
23112
23480
  if (native) {
23113
23481
  binDir = P.dirname(nodePath);
23114
23482
  } else {
23115
- const here = dirname8(fileURLToPath2(import.meta.url));
23483
+ const here = dirname9(fileURLToPath2(import.meta.url));
23116
23484
  const entryDir = entry2 !== void 0 ? P.dirname(P.resolve(entry2)) : void 0;
23117
23485
  if (entryDir !== void 0 && exists(P.join(entryDir, "tachod.mjs")))
23118
23486
  binDir = entryDir;
@@ -23153,7 +23521,7 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
23153
23521
  ...flagged
23154
23522
  };
23155
23523
  }
23156
- function oxagenRuntimeCommands(entry2 = process.argv[1], env = process.env, nodePath = process.execPath, platform = process.platform, native = isNativeBuild(), exists = existsSync9) {
23524
+ function oxagenRuntimeCommands(entry2 = process.argv[1], env = process.env, nodePath = process.execPath, platform = process.platform, native = isNativeBuild(), exists = existsSync10) {
23157
23525
  const P = platform === "win32" ? win323 : posix2;
23158
23526
  const named = env["TACHO_BIN_DIR"];
23159
23527
  let prefix;
@@ -23240,7 +23608,7 @@ function whereExecutable(result2) {
23240
23608
  function shellExecutable(result2) {
23241
23609
  return outputLines(result2).reverse().find((line) => line.startsWith("/"));
23242
23610
  }
23243
- function harnessFacts(exec4, name, platform = process.platform, env = process.env, home = env["HOME"] ?? env["USERPROFILE"] ?? "", exists = existsSync9) {
23611
+ function harnessFacts(exec4, name, platform = process.platform, env = process.env, home = env["HOME"] ?? env["USERPROFILE"] ?? "", exists = existsSync10) {
23244
23612
  let path2;
23245
23613
  if (platform === "win32") {
23246
23614
  path2 = whereExecutable(exec4("where", [name]));
@@ -23268,7 +23636,7 @@ function harnessFacts(exec4, name, platform = process.platform, env = process.en
23268
23636
  const match = /(\d+\.\d+\.\d+)/.exec(version2.stdout);
23269
23637
  return { path: path2, ...match?.[1] !== void 0 ? { version: match[1] } : {} };
23270
23638
  }
23271
- function claudeDesktopFacts(platform = process.platform, home = homedir3(), env = process.env, exists = existsSync9) {
23639
+ function claudeDesktopFacts(platform = process.platform, home = homedir3(), env = process.env, exists = existsSync10) {
23272
23640
  const candidates = platform === "darwin" ? ["/Applications/Claude.app", `${home}/Applications/Claude.app`] : platform === "win32" ? [
23273
23641
  `${env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`}\\AnthropicClaude`,
23274
23642
  `${env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`}\\Programs\\Claude`
@@ -23278,7 +23646,7 @@ function claudeDesktopFacts(platform = process.platform, home = homedir3(), env
23278
23646
  }
23279
23647
  return { installed: false };
23280
23648
  }
23281
- function cursorAppFacts(platform = process.platform, home = homedir3(), env = process.env, exists = existsSync9) {
23649
+ function cursorAppFacts(platform = process.platform, home = homedir3(), env = process.env, exists = existsSync10) {
23282
23650
  const local = env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`;
23283
23651
  const programs = env["PROGRAMFILES"] ?? "C:\\Program Files";
23284
23652
  const candidates = platform === "darwin" ? ["/Applications/Cursor.app", `${home}/Applications/Cursor.app`] : platform === "win32" ? [`${local}\\Programs\\cursor`, `${programs}\\Cursor`] : [];
@@ -23322,8 +23690,8 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23322
23690
  const paths = overrides.paths ?? defaultAgentPaths(tachoHome(env, home, platform));
23323
23691
  const harnessFiles = new HarnessFiles(paths.dir);
23324
23692
  const harnessDirs = {
23325
- claudeConfigDir: dirname8(paths.claudeSettings),
23326
- codexHome: dirname8(paths.codexHooks)
23693
+ claudeConfigDir: dirname9(paths.claudeSettings),
23694
+ codexHome: dirname9(paths.codexHooks)
23327
23695
  };
23328
23696
  const daemonGet = async (path2) => {
23329
23697
  const host = readHostFile(paths.hostFile);
@@ -23431,6 +23799,21 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23431
23799
  `
23432
23800
  );
23433
23801
  },
23802
+ // Claude Code rewrites this file all the time, so the read is taken
23803
+ // inside its lock, not before it. proper-lockfile resolves a file's real
23804
+ // path before it locks it, so Claude Code holds no lock on a file that
23805
+ // does not exist yet, and neither does this.
23806
+ editClaudeUserConfig: (edit) => {
23807
+ const file = paths.claudeUserConfig;
23808
+ const apply = () => {
23809
+ const next = edit(harnessFiles.readJson(file));
23810
+ if (next !== void 0)
23811
+ harnessFiles.write(file, `${JSON.stringify(next, null, 2)}
23812
+ `);
23813
+ };
23814
+ if (existsSync10(file)) withClaudeConfigLock(file, apply);
23815
+ else apply();
23816
+ },
23434
23817
  harnessWriteProblem: (path2) => harnessFiles.writeProblem(path2),
23435
23818
  settleHarnessFiles: () => settleOrThrow(harnessFiles),
23436
23819
  // The base URL and the vendor key live in the files the hooks went into,
@@ -23541,7 +23924,7 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23541
23924
  server.close(() => resolvePromise(port));
23542
23925
  });
23543
23926
  }),
23544
- randomToken: () => randomBytes6(24).toString("hex"),
23927
+ randomToken: () => randomBytes7(24).toString("hex"),
23545
23928
  sleep: (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms)),
23546
23929
  wrapperVersion: TACHO_VERSION,
23547
23930
  atAgent: (agentPaths2) => defaultCliDeps({ ...overrides, paths: agentPaths2 }, managed),
@@ -23555,6 +23938,7 @@ var init_deps = __esm({
23555
23938
  init_hook_client();
23556
23939
  init_codex_app_server();
23557
23940
  init_credential_store();
23941
+ init_claude_config_lock();
23558
23942
  init_fs();
23559
23943
  init_harness_file();
23560
23944
  init_host_file();
@@ -24073,7 +24457,7 @@ import {
24073
24457
  sign as sign2,
24074
24458
  verify as verify2
24075
24459
  } from "node:crypto";
24076
- import { readFileSync as readFileSync11 } from "node:fs";
24460
+ import { readFileSync as readFileSync12 } from "node:fs";
24077
24461
  function encodePublicKey(privateKey) {
24078
24462
  const spki = createPublicKey2(privateKey).export({
24079
24463
  type: "spki",
@@ -24111,7 +24495,7 @@ function deviceKeyPem(key) {
24111
24495
  function loadOrCreateDeviceKey(path2) {
24112
24496
  try {
24113
24497
  return {
24114
- key: deviceKeyFromPem(readFileSync11(path2, "utf8")),
24498
+ key: deviceKeyFromPem(readFileSync12(path2, "utf8")),
24115
24499
  created: false
24116
24500
  };
24117
24501
  } catch (error) {
@@ -24149,110 +24533,6 @@ var init_device_key = __esm({
24149
24533
  }
24150
24534
  });
24151
24535
 
24152
- // ../../packages/tacho/src/host/install-lock.ts
24153
- import { randomBytes as randomBytes7 } from "node:crypto";
24154
- import {
24155
- closeSync as closeSync4,
24156
- existsSync as existsSync10,
24157
- linkSync,
24158
- mkdirSync as mkdirSync6,
24159
- openSync as openSync4,
24160
- readdirSync as readdirSync6,
24161
- readFileSync as readFileSync12,
24162
- renameSync as renameSync3,
24163
- rmdirSync as rmdirSync4,
24164
- unlinkSync as unlinkSync7,
24165
- writeSync as writeSync3
24166
- } from "node:fs";
24167
- import { dirname as dirname9, join as join15 } from "node:path";
24168
- function readLock(path2) {
24169
- try {
24170
- return readFileSync12(path2, "utf8");
24171
- } catch {
24172
- return void 0;
24173
- }
24174
- }
24175
- function holderOf(text4) {
24176
- try {
24177
- return JSON.parse(text4 ?? "");
24178
- } catch {
24179
- return {};
24180
- }
24181
- }
24182
- function removeQuietly(path2) {
24183
- try {
24184
- unlinkSync7(path2);
24185
- } catch {
24186
- }
24187
- }
24188
- function pidAlive(pid) {
24189
- try {
24190
- process.kill(pid, 0);
24191
- return true;
24192
- } catch (error) {
24193
- return error.code === "EPERM";
24194
- }
24195
- }
24196
- function acquireInstallLock(root, now = Date.now, alive = pidAlive) {
24197
- const path2 = join15(root, "install.lock");
24198
- const created = [];
24199
- let dir = root;
24200
- while (!existsSync10(dir) && dirname9(dir) !== dir) {
24201
- created.unshift(dir);
24202
- dir = dirname9(dir);
24203
- }
24204
- mkdirSync6(root, { recursive: true, mode: 448 });
24205
- const token = randomBytes7(12).toString("hex");
24206
- const liveHolder = (holder) => typeof holder.pid === "number" && typeof holder.at === "number" && now() - holder.at < STALE_AFTER_MS && alive(holder.pid) ? holder.pid : void 0;
24207
- for (let attempt = 0; attempt < 2; attempt += 1) {
24208
- try {
24209
- const fd2 = openSync4(path2, "wx", 384);
24210
- writeSync3(fd2, JSON.stringify({ pid: process.pid, at: now(), token }));
24211
- closeSync4(fd2);
24212
- return {
24213
- release: () => {
24214
- if (holderOf(readLock(path2)).token === token) removeQuietly(path2);
24215
- for (const made of [...created].reverse()) {
24216
- try {
24217
- if (readdirSync6(made).length === 0) rmdirSync4(made);
24218
- } catch {
24219
- }
24220
- }
24221
- }
24222
- };
24223
- } catch (error) {
24224
- if (error.code !== "EEXIST") throw error;
24225
- const live = liveHolder(holderOf(readLock(path2)));
24226
- if (live !== void 0) return { heldBy: live };
24227
- const aside = `${path2}.${process.pid}.${token}.stale`;
24228
- try {
24229
- renameSync3(path2, aside);
24230
- } catch (renameError) {
24231
- if (renameError.code === "ENOENT") continue;
24232
- throw renameError;
24233
- }
24234
- const moved = liveHolder(holderOf(readLock(aside)));
24235
- if (moved !== void 0) {
24236
- try {
24237
- linkSync(aside, path2);
24238
- } catch {
24239
- }
24240
- removeQuietly(aside);
24241
- return { heldBy: moved };
24242
- }
24243
- removeQuietly(aside);
24244
- }
24245
- }
24246
- return { heldBy: -1 };
24247
- }
24248
- var STALE_AFTER_MS;
24249
- var init_install_lock = __esm({
24250
- "../../packages/tacho/src/host/install-lock.ts"() {
24251
- "use strict";
24252
- STALE_AFTER_MS = 10 * 6e4;
24253
- }
24254
- });
24255
-
24256
24536
  // ../../packages/tacho/src/host/wal-index.ts
24257
24537
  import {
24258
24538
  appendFileSync,
@@ -24260,7 +24540,7 @@ import {
24260
24540
  existsSync as existsSync11,
24261
24541
  openSync as openSync5,
24262
24542
  readSync as readSync2,
24263
- statSync as statSync6,
24543
+ statSync as statSync7,
24264
24544
  unlinkSync as unlinkSync8
24265
24545
  } from "node:fs";
24266
24546
  import { open as open2 } from "node:fs/promises";
@@ -24278,7 +24558,7 @@ function parseStoredBody(text4) {
24278
24558
  return void 0;
24279
24559
  return stored;
24280
24560
  }
24281
- function readTailLine(path2, size = statSync6(path2).size, window2 = SCAN_CHUNK_BYTES) {
24561
+ function readTailLine(path2, size = statSync7(path2).size, window2 = SCAN_CHUNK_BYTES) {
24282
24562
  if (size === 0) return void 0;
24283
24563
  const fd2 = openSync5(path2, "r");
24284
24564
  try {
@@ -24495,7 +24775,7 @@ var init_wal_index = __esm({
24495
24775
  }
24496
24776
  /** The index to extend, and the byte the scan starts from. */
24497
24777
  base(sessionUuid2, bodiesPath) {
24498
- const size = statSync6(bodiesPath).size;
24778
+ const size = statSync7(bodiesPath).size;
24499
24779
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24500
24780
  if (held !== void 0 && held.covered <= size) return held;
24501
24781
  this.discard(sessionUuid2);
@@ -24569,7 +24849,7 @@ ${lines.join("\n")}
24569
24849
  * answers undefined.
24570
24850
  */
24571
24851
  covering(sessionUuid2, bodiesPath) {
24572
- const size = statSync6(bodiesPath).size;
24852
+ const size = statSync7(bodiesPath).size;
24573
24853
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24574
24854
  if (held === void 0 || held.covered !== size) return void 0;
24575
24855
  this.remember(sessionUuid2, held);
@@ -24583,7 +24863,7 @@ ${lines.join("\n")}
24583
24863
  * file.
24584
24864
  */
24585
24865
  held(sessionUuid2, bodiesPath) {
24586
- const size = statSync6(bodiesPath).size;
24866
+ const size = statSync7(bodiesPath).size;
24587
24867
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24588
24868
  if (held === void 0 || held.covered > size) return void 0;
24589
24869
  this.remember(sessionUuid2, held);
@@ -24592,7 +24872,7 @@ ${lines.join("\n")}
24592
24872
  /** The index of a body file, built or extended to cover the whole file. */
24593
24873
  ensure(sessionUuid2, bodiesPath, onInvalidRecord, onWriteFailure) {
24594
24874
  const index = this.base(sessionUuid2, bodiesPath);
24595
- const size = statSync6(bodiesPath).size;
24875
+ const size = statSync7(bodiesPath).size;
24596
24876
  if (index.covered < size) {
24597
24877
  const result2 = {
24598
24878
  added: [],
@@ -24646,7 +24926,7 @@ ${lines.join("\n")}
24646
24926
  for (let attempt = 0; ; attempt += 1) {
24647
24927
  const generation = this.generationOf(sessionUuid2);
24648
24928
  const index = this.base(sessionUuid2, bodiesPath);
24649
- const size = statSync6(bodiesPath).size;
24929
+ const size = statSync7(bodiesPath).size;
24650
24930
  if (index.covered >= size) {
24651
24931
  this.remember(sessionUuid2, index);
24652
24932
  return index;
@@ -24686,7 +24966,7 @@ import {
24686
24966
  readSync as readSync3,
24687
24967
  truncateSync,
24688
24968
  renameSync as renameSync4,
24689
- statSync as statSync7,
24969
+ statSync as statSync8,
24690
24970
  unlinkSync as unlinkSync9,
24691
24971
  writeSync as writeSync4
24692
24972
  } from "node:fs";
@@ -24697,7 +24977,7 @@ function activityAt(event) {
24697
24977
  return Number.isFinite(at) ? at : Number.NEGATIVE_INFINITY;
24698
24978
  }
24699
24979
  function sizeOf(path2) {
24700
- return statSync7(path2, { throwIfNoEntry: false })?.size ?? 0;
24980
+ return statSync8(path2, { throwIfNoEntry: false })?.size ?? 0;
24701
24981
  }
24702
24982
  var BODY_READ_SLICE, BODY_DROP_SLICE_BYTES, BODY_DROP_SCANS, NEWLINE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
24703
24983
  var init_wal = __esm({
@@ -25057,7 +25337,7 @@ ${lines.join("\n")}
25057
25337
  for (const session of sessions) {
25058
25338
  if (sizes.has(session)) continue;
25059
25339
  const path2 = pathOf2(session);
25060
- sizes.set(session, existsSync12(path2) ? statSync7(path2).size : void 0);
25340
+ sizes.set(session, existsSync12(path2) ? statSync8(path2).size : void 0);
25061
25341
  }
25062
25342
  return sizes;
25063
25343
  }
@@ -25073,7 +25353,7 @@ ${lines.join("\n")}
25073
25353
  try {
25074
25354
  if (!existsSync12(path2)) continue;
25075
25355
  if (size === void 0) unlinkSync9(path2);
25076
- else if (statSync7(path2).size !== size) truncateSync(path2, size);
25356
+ else if (statSync8(path2).size !== size) truncateSync(path2, size);
25077
25357
  } catch (error) {
25078
25358
  onFailure(session, error);
25079
25359
  }
@@ -25736,7 +26016,7 @@ ${lines.join("\n")}
25736
26016
  const lastSeq = this.lastWrittenSeq(sessionUuid2);
25737
26017
  if (lastSeq === void 0) return 0;
25738
26018
  const path2 = this.bodyFileFor(sessionUuid2);
25739
- const size = statSync7(path2).size;
26019
+ const size = statSync8(path2).size;
25740
26020
  let keep = size;
25741
26021
  let cut2 = 0;
25742
26022
  while (keep > 0) {
@@ -25769,7 +26049,7 @@ ${lines.join("\n")}
25769
26049
  */
25770
26050
  lastWrittenSeq(sessionUuid2) {
25771
26051
  const path2 = this.fileFor(sessionUuid2);
25772
- if (!existsSync12(path2) || statSync7(path2).size === 0) return -1;
26052
+ if (!existsSync12(path2) || statSync8(path2).size === 0) return -1;
25773
26053
  return this.lastEvent(sessionUuid2)?.seq;
25774
26054
  }
25775
26055
  /**
@@ -25800,7 +26080,7 @@ ${lines.join("\n")}
25800
26080
  *eventsBackward(sessionUuid2) {
25801
26081
  const path2 = this.fileFor(sessionUuid2);
25802
26082
  if (!existsSync12(path2)) return;
25803
- let end = statSync7(path2).size;
26083
+ let end = statSync8(path2).size;
25804
26084
  while (end > 0) {
25805
26085
  const line = readTailLine(path2, end, EVENT_TAIL_WINDOW);
25806
26086
  if (line === void 0) return;
@@ -25831,7 +26111,7 @@ ${lines.join("\n")}
25831
26111
  this.unverifiedResume.delete(sessionUuid2);
25832
26112
  let holds2 = false;
25833
26113
  try {
25834
- if (mark.offset <= statSync7(path2).size) {
26114
+ if (mark.offset <= statSync8(path2).size) {
25835
26115
  const line = readTailLine(path2, mark.offset, EVENT_TAIL_WINDOW);
25836
26116
  holds2 = line !== void 0 && line.terminated && line.end === mark.offset && JSON.parse(line.text).seq === mark.afterSeq;
25837
26117
  }
@@ -26227,7 +26507,7 @@ ${lines.join("\n")}
26227
26507
  if (sealedAt === void 0) continue;
26228
26508
  const path2 = this.fileFor(session);
26229
26509
  try {
26230
- const age = now - Math.max(Date.parse(sealedAt), statSync7(path2).mtimeMs);
26510
+ const age = now - Math.max(Date.parse(sealedAt), statSync8(path2).mtimeMs);
26231
26511
  if (age < retainMs) continue;
26232
26512
  const last = this.lastSeqOf(session);
26233
26513
  if (last < 0 || last > this.shippedThrough(session)) continue;
@@ -26249,7 +26529,7 @@ ${lines.join("\n")}
26249
26529
  if (removed.includes(session) || existsSync12(this.fileFor(session)))
26250
26530
  continue;
26251
26531
  try {
26252
- if (now - statSync7(this.bodyFileFor(session)).mtimeMs < retainMs)
26532
+ if (now - statSync8(this.bodyFileFor(session)).mtimeMs < retainMs)
26253
26533
  continue;
26254
26534
  } catch (error) {
26255
26535
  this.bodyFailure(session, "cleanup", error);
@@ -27303,7 +27583,7 @@ import {
27303
27583
  existsSync as existsSync15,
27304
27584
  lstatSync as lstatSync3,
27305
27585
  readdirSync as readdirSync8,
27306
- rmdirSync as rmdirSync5,
27586
+ rmdirSync as rmdirSync6,
27307
27587
  rmSync as rmSync2,
27308
27588
  unlinkSync as unlinkSync10
27309
27589
  } from "node:fs";
@@ -27540,6 +27820,24 @@ async function stripEnrollmentHooks(host, deps, settle3 = false) {
27540
27820
  }
27541
27821
  });
27542
27822
  }
27823
+ let claudeCodeMcpChanged;
27824
+ const editUserConfig = deps.editClaudeUserConfig;
27825
+ if (editUserConfig !== void 0 && (host?.harnesses === void 0 || host.harnesses.includes("claude-code"))) {
27826
+ const userConfig = deps.paths.claudeUserConfig;
27827
+ attempt(userConfig, () => {
27828
+ editUserConfig((current) => {
27829
+ if (current === void 0) return void 0;
27830
+ const stripped = stripClaudeCodeMcpConfig(
27831
+ current,
27832
+ host?.host_enrollment_id,
27833
+ host?.displaced_mcp_servers?.["claude-code"] ?? {}
27834
+ );
27835
+ if (!stripped.changed) return void 0;
27836
+ claudeCodeMcpChanged = userConfig;
27837
+ return stripped.config;
27838
+ });
27839
+ });
27840
+ }
27543
27841
  if (settle3 && failed3.length === 0) {
27544
27842
  try {
27545
27843
  deps.settleHarnessFiles?.();
@@ -27557,6 +27855,7 @@ async function stripEnrollmentHooks(host, deps, settle3 = false) {
27557
27855
  cursorChanged,
27558
27856
  stellaChanged,
27559
27857
  claudeDesktopChanged,
27858
+ ...claudeCodeMcpChanged !== void 0 ? { claudeCodeMcpChanged } : {},
27560
27859
  failed: failed3
27561
27860
  };
27562
27861
  }
@@ -27669,7 +27968,7 @@ function restartForRemaining(installed, deps) {
27669
27968
  }
27670
27969
  function removeIfEmpty(dir) {
27671
27970
  try {
27672
- if (readdirSync8(dir).length === 0) rmdirSync5(dir);
27971
+ if (readdirSync8(dir).length === 0) rmdirSync6(dir);
27673
27972
  } catch {
27674
27973
  }
27675
27974
  }
@@ -27759,6 +28058,11 @@ async function unenrollLocked(options, deps) {
27759
28058
  " Quit Claude Desktop and open it again for the change to take effect"
27760
28059
  );
27761
28060
  }
28061
+ if (stripped.claudeCodeMcpChanged !== void 0) {
28062
+ deps.out(
28063
+ ` Oxagen's MCP server removed from ${stripped.claudeCodeMcpChanged}`
28064
+ );
28065
+ }
27762
28066
  for (const failure2 of stripped.failed) {
27763
28067
  incomplete = true;
27764
28068
  warnings.push(
@@ -27890,6 +28194,7 @@ var init_unenroll = __esm({
27890
28194
  "../../packages/tacho/src/cli/unenroll.ts"() {
27891
28195
  "use strict";
27892
28196
  init_install_lock();
28197
+ init_claude_code_mcp_writer();
27893
28198
  init_claude_desktop_writer();
27894
28199
  init_codex_hook_trust();
27895
28200
  init_codex_writer();
@@ -28638,6 +28943,32 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
28638
28943
  deps.out(" already present; nothing to change");
28639
28944
  }
28640
28945
  });
28946
+ if (harnesses.includes("claude-code") && !unhooked.includes("claude-code") && deps.editClaudeUserConfig !== void 0) {
28947
+ deps.out(` Claude Code MCP server: ${deps.paths.claudeUserConfig}`);
28948
+ try {
28949
+ const registered = registerClaudeCodeMcp(host, deps);
28950
+ host = registered.host;
28951
+ if (registered.result === "skipped")
28952
+ warnings.push(
28953
+ `Claude Code was not given Oxagen's tools: ${registered.reason ?? "unknown reason"}. Until it has them, the Stop hook asks Claude Code for no reflection.`
28954
+ );
28955
+ else if (registered.result === "written")
28956
+ deps.out(
28957
+ " `oxagen` server written; a new Claude Code session lists Oxagen's tools"
28958
+ );
28959
+ else deps.out(" already present; nothing to change");
28960
+ if (registered.displaced)
28961
+ warnings.push(
28962
+ "an MCP server already used the name `oxagen` in Claude Code; it was moved aside and unenroll restores it"
28963
+ );
28964
+ } catch (error) {
28965
+ host = readHostFile(deps.paths.hostFile) ?? host;
28966
+ warnings.push(
28967
+ `Claude Code was not given Oxagen's tools: ${error instanceof Error ? error.message : String(error)}. The hooks are written. Run \`oxagen agent enroll\` again to add the tools; until then the Stop hook asks Claude Code for no reflection.`
28968
+ );
28969
+ deps.out(" not written (see the warning below)");
28970
+ }
28971
+ }
28641
28972
  hook("codex", () => {
28642
28973
  deps.out(` Codex: ${deps.paths.codexHooks}`);
28643
28974
  const merged = mergeCodexHooks(deps.readCodexHooks(), hookConfig);
@@ -29012,6 +29343,7 @@ var init_enroll = __esm({
29012
29343
  init_credential();
29013
29344
  init_github();
29014
29345
  init_agent_deps();
29346
+ init_claude_code_mcp();
29015
29347
  init_daemon_service();
29016
29348
  init_policy_mode();
29017
29349
  init_status();
@@ -29914,9 +30246,9 @@ import {
29914
30246
  readFileSync as readFileSync14,
29915
30247
  readlinkSync as readlinkSync3,
29916
30248
  realpathSync as realpathSync2,
29917
- rmdirSync as rmdirSync6,
30249
+ rmdirSync as rmdirSync7,
29918
30250
  rmSync as rmSync4,
29919
- statSync as statSync8,
30251
+ statSync as statSync9,
29920
30252
  unlinkSync as unlinkSync11
29921
30253
  } from "node:fs";
29922
30254
  import { dirname as dirname13, isAbsolute, join as join19, relative, resolve as resolve6, sep } from "node:path";
@@ -30072,7 +30404,7 @@ function removeFile(path2, tally2, note = "") {
30072
30404
  function removeDirIfEmpty(dir) {
30073
30405
  try {
30074
30406
  if (readdirSync9(dir).length > 0) return false;
30075
- rmdirSync6(dir);
30407
+ rmdirSync7(dir);
30076
30408
  return true;
30077
30409
  } catch {
30078
30410
  return false;
@@ -30091,7 +30423,7 @@ function removeProfileBlock(path2, tally2) {
30091
30423
  if (updated === text4) return;
30092
30424
  try {
30093
30425
  const real = realpathSync2(path2);
30094
- writeFileAtomic(real, updated, { mode: statSync8(real).mode & 4095 });
30426
+ writeFileAtomic(real, updated, { mode: statSync9(real).mode & 4095 });
30095
30427
  tally2.removed.push(`the Oxagen PATH lines in ${path2}`);
30096
30428
  } catch (error) {
30097
30429
  tally2.left.push(`cannot rewrite ${path2}: ${reasonOf3(error)}`);
@@ -31162,10 +31494,12 @@ __export(cli_exports, {
31162
31494
  MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
31163
31495
  SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
31164
31496
  STATIC_TOKEN_RENEW_WINDOW_MS: () => STATIC_TOKEN_RENEW_WINDOW_MS,
31497
+ addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
31165
31498
  agentNameFromCommand: () => agentNameFromCommand,
31166
31499
  backfillCommand: () => backfillCommand,
31167
31500
  brokerCredentials: () => brokerCredentials,
31168
31501
  brokerEnvVar: () => brokerEnvVar,
31502
+ canServeOxagenTools: () => canServeOxagenTools,
31169
31503
  claudeDesktopFacts: () => claudeDesktopFacts,
31170
31504
  claudeFacts: () => claudeFacts,
31171
31505
  credentialIssue: () => credentialIssue,
@@ -31191,6 +31525,7 @@ __export(cli_exports, {
31191
31525
  isNativeBuild: () => isNativeBuild,
31192
31526
  moveOffTachoNames: () => moveOffTachoNames,
31193
31527
  namesTachoExecutable: () => namesTachoExecutable,
31528
+ needsClaudeCodeMcp: () => needsClaudeCodeMcp,
31194
31529
  observedTiers: () => observedTiers,
31195
31530
  oxagenRuntimeCommands: () => oxagenRuntimeCommands,
31196
31531
  parseCredentialMode: () => parseCredentialMode,
@@ -31199,6 +31534,7 @@ __export(cli_exports, {
31199
31534
  readDesktopRecord: () => readDesktopRecord,
31200
31535
  reassign: () => reassign,
31201
31536
  reassignTarget: () => reassignTarget,
31537
+ registerClaudeCodeMcp: () => registerClaudeCodeMcp,
31202
31538
  removePathBlocks: () => removePathBlocks,
31203
31539
  repointCommands: () => repointCommands,
31204
31540
  resolveApiUrl: () => resolveApiUrl,
@@ -31241,6 +31577,7 @@ var init_cli = __esm({
31241
31577
  "../../packages/tacho/src/cli/index.ts"() {
31242
31578
  "use strict";
31243
31579
  init_backfill3();
31580
+ init_claude_code_mcp();
31244
31581
  init_credential();
31245
31582
  init_deps();
31246
31583
  init_detect();
@@ -37377,6 +37714,7 @@ function reflectionAsk(record, options) {
37377
37714
  const state = states.get(record);
37378
37715
  if (state === void 0 || state.asked || state.reflected || state.signals.size === 0)
37379
37716
  return void 0;
37717
+ if (!options.toolRegistered()) return void 0;
37380
37718
  state.asked = true;
37381
37719
  const full = askText(state, true);
37382
37720
  return full.length <= REFLECTION_ASK_MAX_CHARS ? full : askText(state, false);
@@ -38682,7 +39020,10 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
38682
39020
  const ask = input.agent_id !== void 0 ? void 0 : reflectionAsk(record, {
38683
39021
  harness: record.customAgent !== void 0 ? void 0 : record.harness ?? "claude-code",
38684
39022
  stopHookActive: input["stop_hook_active"] === true,
38685
- replayed: replay !== void 0
39023
+ replayed: replay !== void 0,
39024
+ toolRegistered: () => deps.reflectionToolRegistered?.({
39025
+ startedAt: record.startedAt
39026
+ }) ?? false
38686
39027
  });
38687
39028
  const texts = [
38688
39029
  ...drainMidTurn(input, record, deps, events, replay, ask?.length ?? 0),
@@ -45687,7 +46028,7 @@ var init_model_proxy_listener = __esm({
45687
46028
  import {
45688
46029
  createServer as createServer4
45689
46030
  } from "node:http";
45690
- import { chmodSync as chmodSync7, existsSync as existsSync18, statSync as statSync9, unlinkSync as unlinkSync12 } from "node:fs";
46031
+ import { chmodSync as chmodSync7, existsSync as existsSync18, statSync as statSync10, unlinkSync as unlinkSync12 } from "node:fs";
45691
46032
  import { randomBytes as randomBytes10 } from "node:crypto";
45692
46033
  import { createConnection } from "node:net";
45693
46034
  function readBody(req) {
@@ -45959,7 +46300,7 @@ function createRequestHandler(api, log, options = {}) {
45959
46300
  }
45960
46301
  async function removeStaleSocket(socketPath) {
45961
46302
  if (!existsSync18(socketPath)) return;
45962
- if (!statSync9(socketPath).isSocket()) {
46303
+ if (!statSync10(socketPath).isSocket()) {
45963
46304
  throw new Error(`collector socket path is not a socket: ${socketPath}`);
45964
46305
  }
45965
46306
  const stale = await new Promise((resolve20, reject) => {
@@ -46747,7 +47088,7 @@ import {
46747
47088
  readdirSync as readdirSync12,
46748
47089
  readFileSync as readFileSync18,
46749
47090
  renameSync as renameSync5,
46750
- statSync as statSync10,
47091
+ statSync as statSync11,
46751
47092
  unlinkSync as unlinkSync13,
46752
47093
  writeFileSync as writeFileSync3
46753
47094
  } from "node:fs";
@@ -46922,6 +47263,22 @@ async function initializeDaemon(options, server, port, ready) {
46922
47263
  ))
46923
47264
  memoryUses.note({ ...file, ...run });
46924
47265
  };
47266
+ const reflectionToolRegistered = (session) => {
47267
+ try {
47268
+ const current = readHostFile(paths.hostFile) ?? host;
47269
+ return claudeCodeSessionHasOxagenTools({
47270
+ enrollmentId: current.host_enrollment_id,
47271
+ hasGatewayKey: current.gateway_api_key !== void 0,
47272
+ registeredAt: current.mcp_registered_at?.["claude-code"],
47273
+ sessionStartedAt: session.startedAt,
47274
+ readUserConfig: () => readJsonFileIfExists(
47275
+ withRecordedHarnessFiles(paths, current).claudeUserConfig
47276
+ )
47277
+ });
47278
+ } catch {
47279
+ return false;
47280
+ }
47281
+ };
46925
47282
  const localServers = options.localServers === true ? createMachineLoop({
46926
47283
  host: () => host,
46927
47284
  fetch: options.fetch ?? globalThis.fetch,
@@ -46987,7 +47344,7 @@ async function initializeDaemon(options, server, port, ready) {
46987
47344
  processStarts: hookProcessStarts,
46988
47345
  adoptedChain: (sessionUuid2) => backfillLedger.chainHead(sessionUuid2)
46989
47346
  });
46990
- const priorStateWrittenAt = existsSync20(paths.daemonState) ? statSync10(paths.daemonState).mtimeMs : void 0;
47347
+ const priorStateWrittenAt = existsSync20(paths.daemonState) ? statSync11(paths.daemonState).mtimeMs : void 0;
46991
47348
  const sealedState = parseSealedState(
46992
47349
  readJsonStateFile(
46993
47350
  paths.daemonSealedState,
@@ -48063,7 +48420,8 @@ async function initializeDaemon(options, server, port, ready) {
48063
48420
  cedar: loadCedarRuntime,
48064
48421
  skills,
48065
48422
  ...recallMemories !== void 0 ? { recallMemories } : {},
48066
- ...noteMemoryReads !== void 0 ? { noteMemoryReads } : {}
48423
+ ...noteMemoryReads !== void 0 ? { noteMemoryReads } : {},
48424
+ reflectionToolRegistered
48067
48425
  },
48068
48426
  envelope2.replay,
48069
48427
  envelope2.harness,
@@ -49012,7 +49370,7 @@ async function initializeDaemon(options, server, port, ready) {
49012
49370
  if (!name.endsWith(".json")) continue;
49013
49371
  const file = join34(paths.quarantine, name);
49014
49372
  try {
49015
- if (at - statSync10(file).mtimeMs < retainMs) continue;
49373
+ if (at - statSync11(file).mtimeMs < retainMs) continue;
49016
49374
  unlinkSync13(file);
49017
49375
  removed += 1;
49018
49376
  } catch {
@@ -49442,6 +49800,7 @@ var init_daemon = __esm({
49442
49800
  init_device_key();
49443
49801
  init_fs();
49444
49802
  init_host_file();
49803
+ init_claude_code_mcp_writer();
49445
49804
  init_model_base_url();
49446
49805
  init_model_credential();
49447
49806
  init_run_token();
@@ -49877,7 +50236,7 @@ import {
49877
50236
  constants as constants3,
49878
50237
  fstatSync as fstatSync2,
49879
50238
  lstatSync as lstatSync7,
49880
- mkdirSync as mkdirSync7,
50239
+ mkdirSync as mkdirSync8,
49881
50240
  openSync as openSync7,
49882
50241
  readSync as readSync4,
49883
50242
  realpathSync as realpathSync5,
@@ -49886,7 +50245,7 @@ import {
49886
50245
  import { dirname as dirname20, join as join35, resolve as resolve13 } from "node:path";
49887
50246
  function bundlePath(root, relative10, create2 = false) {
49888
50247
  const absolute = resolve13(root);
49889
- if (create2) mkdirSync7(absolute, { recursive: true, mode: 448 });
50248
+ if (create2) mkdirSync8(absolute, { recursive: true, mode: 448 });
49890
50249
  if (lstatSync7(absolute).isSymbolicLink() || !lstatSync7(absolute).isDirectory()) {
49891
50250
  throw new Error("ARP bundle root must be a directory, not a symlink");
49892
50251
  }
@@ -49902,7 +50261,7 @@ function bundlePath(root, relative10, create2 = false) {
49902
50261
  }
49903
50262
  } catch (error) {
49904
50263
  if (error.code !== "ENOENT") throw error;
49905
- if (create2 && i2 < parts.length - 1) mkdirSync7(current, { mode: 448 });
50264
+ if (create2 && i2 < parts.length - 1) mkdirSync8(current, { mode: 448 });
49906
50265
  else if (i2 < parts.length - 1) throw error;
49907
50266
  }
49908
50267
  }
@@ -50766,7 +51125,7 @@ import {
50766
51125
  constants as constants4,
50767
51126
  fstatSync as fstatSync3,
50768
51127
  lstatSync as lstatSync8,
50769
- mkdirSync as mkdirSync8,
51128
+ mkdirSync as mkdirSync9,
50770
51129
  openSync as openSync8,
50771
51130
  readSync as readSync5,
50772
51131
  realpathSync as realpathSync6,
@@ -50896,7 +51255,7 @@ function captureCheckpoint(options) {
50896
51255
  executable: (file.mode & 73) !== 0
50897
51256
  });
50898
51257
  }
50899
- mkdirSync8(out, { mode: 448 });
51258
+ mkdirSync9(out, { mode: 448 });
50900
51259
  try {
50901
51260
  const blob = (value) => storeBlob(out, Buffer.from(jcs(value)), "application/json");
50902
51261
  const entries2 = [];
@@ -51083,16 +51442,16 @@ function prepareCheckpoint(options) {
51083
51442
  }
51084
51443
  return { entry: entry2, bytes: null };
51085
51444
  });
51086
- mkdirSync8(destination, { mode: 448 });
51445
+ mkdirSync9(destination, { mode: 448 });
51087
51446
  try {
51088
51447
  const restored = join36(destination, "workspace");
51089
- mkdirSync8(restored, { mode: 448 });
51448
+ mkdirSync9(restored, { mode: 448 });
51090
51449
  for (const { entry: entry2, bytes } of files2) {
51091
51450
  const path2 = join36(restored, entry2.path);
51092
51451
  if (entry2.kind === "directory")
51093
- mkdirSync8(path2, { recursive: true, mode: 448 });
51452
+ mkdirSync9(path2, { recursive: true, mode: 448 });
51094
51453
  else {
51095
- mkdirSync8(dirname21(path2), { recursive: true, mode: 448 });
51454
+ mkdirSync9(dirname21(path2), { recursive: true, mode: 448 });
51096
51455
  if (bytes === null) fail2("Workspace file has no bytes.");
51097
51456
  writeFileSync6(path2, bytes, {
51098
51457
  flag: "wx",
@@ -51101,7 +51460,7 @@ function prepareCheckpoint(options) {
51101
51460
  }
51102
51461
  }
51103
51462
  const promptDir = join36(restored, ".arp");
51104
- mkdirSync8(promptDir, { mode: 448 });
51463
+ mkdirSync9(promptDir, { mode: 448 });
51105
51464
  const promptFile = join36(promptDir, "handoff.json");
51106
51465
  const prompt = {
51107
51466
  checkpoint_digest: checkpointDigest,
@@ -51676,7 +52035,7 @@ var init_package = __esm({
51676
52035
  "package.json"() {
51677
52036
  package_default = {
51678
52037
  name: "@oxagen/cli",
51679
- version: "2.1.4-443",
52038
+ version: "2.1.4-453",
51680
52039
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
51681
52040
  type: "module",
51682
52041
  main: "./dist/index.js",
@@ -51787,7 +52146,7 @@ var init_atomic_write = __esm({
51787
52146
  // src/lib/config.ts
51788
52147
  import { homedir as homedir8 } from "os";
51789
52148
  import { join as join38 } from "path";
51790
- import { readFileSync as readFileSync21, mkdirSync as mkdirSync9, existsSync as existsSync22 } from "fs";
52149
+ import { readFileSync as readFileSync21, mkdirSync as mkdirSync10, existsSync as existsSync22 } from "fs";
51791
52150
  function getConfigDir() {
51792
52151
  return CONFIG_DIR;
51793
52152
  }
@@ -51808,7 +52167,7 @@ Run \`oxagen login\` to re-authenticate.
51808
52167
  function writeConfig(patch) {
51809
52168
  const current = readConfig();
51810
52169
  const next = { ...current, ...patch };
51811
- if (!existsSync22(CONFIG_DIR)) mkdirSync9(CONFIG_DIR, { recursive: true });
52170
+ if (!existsSync22(CONFIG_DIR)) mkdirSync10(CONFIG_DIR, { recursive: true });
51812
52171
  atomicWriteFileSync(CONFIG_FILE, JSON.stringify(next, null, 2));
51813
52172
  }
51814
52173
  function clearConfig() {
@@ -52363,6 +52722,34 @@ var init_api = __esm({
52363
52722
  }
52364
52723
  });
52365
52724
 
52725
+ // src/commands/add-claude-code-mcp.ts
52726
+ async function addClaudeCodeMcp(writer) {
52727
+ const { addMissingClaudeCodeMcp: addMissingClaudeCodeMcp2, defaultCliDeps: defaultCliDeps2, oxagenRuntimeCommands: oxagenRuntimeCommands2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
52728
+ let added;
52729
+ try {
52730
+ added = addMissingClaudeCodeMcp2(
52731
+ defaultCliDeps2({
52732
+ out: () => void 0,
52733
+ err: (line) => writer.writeErr(line),
52734
+ runtime: oxagenRuntimeCommands2()
52735
+ })
52736
+ );
52737
+ } catch (error) {
52738
+ writer.writeErr(
52739
+ `Could not check Claude Code for Oxagen's MCP server: ${error instanceof Error ? error.message : String(error)}. Run \`oxagen agent enroll\` to add it.`
52740
+ );
52741
+ return;
52742
+ }
52743
+ for (const agent of added)
52744
+ writer.writeErr(
52745
+ agent.ok ? `Added Oxagen's MCP server to Claude Code for ${agent.agentKey} in ${agent.path}. New Claude Code sessions list Oxagen's tools.` : `Could not add Oxagen's MCP server to Claude Code for ${agent.agentKey}: ${agent.problem ?? "unknown reason"}. The hooks keep working. Run \`oxagen agent enroll\` to try again.`
52746
+ );
52747
+ }
52748
+ var init_add_claude_code_mcp = __esm({
52749
+ "src/commands/add-claude-code-mcp.ts"() {
52750
+ }
52751
+ });
52752
+
52366
52753
  // src/commands/move-off-tacho.ts
52367
52754
  async function moveOffTacho(writer) {
52368
52755
  const { defaultCliDeps: defaultCliDeps2, moveOffTachoNames: moveOffTachoNames2, oxagenRuntimeCommands: oxagenRuntimeCommands2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
@@ -52462,6 +52849,7 @@ async function handleTachoEnroll(opts, writer = stdoutWriter) {
52462
52849
  async function handleTachoStatus(opts, writer = stdoutWriter) {
52463
52850
  const { status: status2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
52464
52851
  await moveOffTacho(writer);
52852
+ await addClaudeCodeMcp(writer);
52465
52853
  const report = await status2(opts, await tachoDeps(writer));
52466
52854
  const reports = [report, ...report.enrollments ?? []];
52467
52855
  return report.enrolled && !reports.some((entry2) => entry2.shipping?.healthy === false);
@@ -52678,6 +53066,7 @@ var init_tacho = __esm({
52678
53066
  init_config();
52679
53067
  init_capture_writer();
52680
53068
  init_api();
53069
+ init_add_claude_code_mcp();
52681
53070
  init_move_off_tacho();
52682
53071
  }
52683
53072
  });
@@ -54094,6 +54483,12 @@ async function findingsList(opts, writer = stdoutWriter) {
54094
54483
  ]),
54095
54484
  writer
54096
54485
  );
54486
+ if (result2.truncated === true) {
54487
+ writer.write("");
54488
+ writer.write(
54489
+ `The list shows the first ${result2.findings.length} of ${result2.counts.findings} ${status2} findings.`
54490
+ );
54491
+ }
54097
54492
  }
54098
54493
  var STATUSES, FRAMES_SHOWN;
54099
54494
  var init_findings = __esm({
@@ -56802,7 +57197,7 @@ var init_src2 = __esm({
56802
57197
  // src/commands/workspace-link.ts
56803
57198
  import {
56804
57199
  existsSync as existsSync23,
56805
- mkdirSync as mkdirSync10,
57200
+ mkdirSync as mkdirSync11,
56806
57201
  readFileSync as readFileSync22,
56807
57202
  rmSync as rmSync9,
56808
57203
  writeFileSync as writeFileSync8
@@ -56827,7 +57222,7 @@ function readWorkspaceLink(cwd) {
56827
57222
  }
56828
57223
  function writeWorkspaceLink(cwd, link) {
56829
57224
  const dir = join44(cwd, ".oxagen");
56830
- mkdirSync10(dir, { recursive: true });
57225
+ mkdirSync11(dir, { recursive: true });
56831
57226
  writeFileSync8(workspaceLinkPath(cwd), JSON.stringify(link, null, 2), "utf8");
56832
57227
  }
56833
57228
  var init_workspace_link = __esm({
@@ -58277,7 +58672,7 @@ var init_runtime_shared = __esm({
58277
58672
  function spanDays(from, to) {
58278
58673
  return (Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / DAY_MS + 1;
58279
58674
  }
58280
- var costBasisSchema, microsSchema, moneySchema, costSchema, daySchema, monthSchema, SPEND_RANGE_DAYS_MAX, DAY_MS, dayRangeSchema, principalPublicIdSchema, tokenCountsSchema, spendGroupKindSchema, ratioSchema, spendFigureSchema, spendDaySchema, unmeteredRunsSchema;
58675
+ var costBasisSchema, microsSchema, moneySchema, costSchema, daySchema, monthSchema, SPEND_RANGE_DAYS_MAX, DAY_MS, dayRangeSchema, principalPublicIdSchema, tokenCountsSchema, standingTokensSchema, spendGroupKindSchema, ratioSchema, spendFigureSchema, spendDaySchema, unmeteredRunsSchema;
58281
58676
  var init_spend_shared = __esm({
58282
58677
  "../../packages/oxagen/src/contracts/spend.shared.ts"() {
58283
58678
  "use strict";
@@ -58326,6 +58721,11 @@ var init_spend_shared = __esm({
58326
58721
  */
58327
58722
  server_tool_request: external_exports.number().int().nonnegative()
58328
58723
  }).strict();
58724
+ standingTokensSchema = external_exports.object({
58725
+ toolDefinitionTokens: external_exports.number().int().nonnegative().nullable(),
58726
+ contextFrameTokens: external_exports.number().int().nonnegative().nullable(),
58727
+ steeringTokens: external_exports.number().int().nonnegative().nullable()
58728
+ }).strict();
58329
58729
  spendGroupKindSchema = external_exports.enum([
58330
58730
  "operator",
58331
58731
  "agent",
@@ -74214,7 +74614,7 @@ __export(verify_exports, {
74214
74614
  readBundle: () => readBundle,
74215
74615
  verifyBundle: () => verifyBundle3
74216
74616
  });
74217
- import { existsSync as existsSync25, readFileSync as readFileSync23, statSync as statSync11 } from "node:fs";
74617
+ import { existsSync as existsSync25, readFileSync as readFileSync23, statSync as statSync12 } from "node:fs";
74218
74618
  import { join as join47 } from "node:path";
74219
74619
  function readDirectory(dir) {
74220
74620
  const read2 = (name) => {
@@ -74260,7 +74660,7 @@ function readBundle(path2) {
74260
74660
  if (!existsSync25(path2)) {
74261
74661
  throw new BundleReadError(`${path2} does not exist.`);
74262
74662
  }
74263
- return statSync11(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
74663
+ return statSync12(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
74264
74664
  }
74265
74665
  function digestLabel(state) {
74266
74666
  return state === "not_carried" ? "not carried" : state;
@@ -75154,7 +75554,7 @@ import {
75154
75554
  appendFileSync as appendFileSync4,
75155
75555
  existsSync as existsSync26,
75156
75556
  lstatSync as lstatSync9,
75157
- mkdirSync as mkdirSync11,
75557
+ mkdirSync as mkdirSync12,
75158
75558
  readFileSync as readFileSync24,
75159
75559
  readdirSync as readdirSync13,
75160
75560
  realpathSync as realpathSync8,
@@ -75251,7 +75651,7 @@ function machineId(configDir = getConfigDir()) {
75251
75651
  }
75252
75652
  if (secret === null) {
75253
75653
  secret = randomBytes11(32).toString("hex");
75254
- mkdirSync11(configDir, { recursive: true });
75654
+ mkdirSync12(configDir, { recursive: true });
75255
75655
  writeFileSync10(file, `${secret}
75256
75656
  `, { encoding: "utf8", mode: 384 });
75257
75657
  }
@@ -75803,12 +76203,12 @@ __export(pull_exports, {
75803
76203
  import { createHash as createHash19 } from "node:crypto";
75804
76204
  import {
75805
76205
  lstatSync as lstatSync10,
75806
- mkdirSync as mkdirSync12,
76206
+ mkdirSync as mkdirSync13,
75807
76207
  readFileSync as readFileSync25,
75808
76208
  readdirSync as readdirSync14,
75809
76209
  realpathSync as realpathSync9,
75810
76210
  rmSync as rmSync10,
75811
- rmdirSync as rmdirSync7
76211
+ rmdirSync as rmdirSync8
75812
76212
  } from "node:fs";
75813
76213
  import {
75814
76214
  dirname as dirname27,
@@ -75979,7 +76379,7 @@ function pruneEmptyDirs(root, path2) {
75979
76379
  while (dir.startsWith(stop + sep7)) {
75980
76380
  try {
75981
76381
  if (readdirSync14(dir).length > 0) return;
75982
- rmdirSync7(dir);
76382
+ rmdirSync8(dir);
75983
76383
  } catch {
75984
76384
  return;
75985
76385
  }
@@ -75991,7 +76391,7 @@ function applyPlan(root, plan) {
75991
76391
  for (const d of plan.deletes) assertInsideOxagen(root, d);
75992
76392
  for (const w of plan.writes) {
75993
76393
  const abs = join49(root, ...w.path.split("/"));
75994
- mkdirSync12(dirname27(abs), { recursive: true });
76394
+ mkdirSync13(dirname27(abs), { recursive: true });
75995
76395
  atomicWriteFileSync(abs, w.content);
75996
76396
  }
75997
76397
  for (const d of plan.deletes) {
@@ -113377,10 +113777,10 @@ import { execFileSync as execFileSync2 } from "node:child_process";
113377
113777
  import { createHash as createHash20 } from "node:crypto";
113378
113778
  import {
113379
113779
  lstatSync as lstatSync11,
113380
- mkdirSync as mkdirSync13,
113780
+ mkdirSync as mkdirSync14,
113381
113781
  readFileSync as readFileSync26,
113382
113782
  readlinkSync as readlinkSync4,
113383
- statSync as statSync12
113783
+ statSync as statSync13
113384
113784
  } from "node:fs";
113385
113785
  import { dirname as dirname28, isAbsolute as isAbsolute14, join as join50, relative as relative9, resolve as resolve19, sep as sep8 } from "node:path";
113386
113786
  function git5(cwd, args, input) {
@@ -113488,14 +113888,14 @@ function chooseBase(root, ref2) {
113488
113888
  }
113489
113889
  function isFile(path2) {
113490
113890
  try {
113491
- return statSync12(path2).isFile();
113891
+ return statSync13(path2).isFile();
113492
113892
  } catch {
113493
113893
  return false;
113494
113894
  }
113495
113895
  }
113496
113896
  function isDir(path2) {
113497
113897
  try {
113498
- return statSync12(path2).isDirectory();
113898
+ return statSync13(path2).isDirectory();
113499
113899
  } catch {
113500
113900
  return false;
113501
113901
  }
@@ -113611,7 +114011,7 @@ async function publishedInputs(root, deps, refresh, out) {
113611
114011
  }
113612
114012
  if (file !== null) {
113613
114013
  try {
113614
- mkdirSync13(dirname28(file), { recursive: true });
114014
+ mkdirSync14(dirname28(file), { recursive: true });
113615
114015
  atomicWriteFileSync(
113616
114016
  file,
113617
114017
  JSON.stringify({