@oxagen/cli 2.1.4-447 → 2.1.4-460

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 +713 -309
  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,
@@ -20367,6 +20394,304 @@ var init_backfill3 = __esm({
20367
20394
  }
20368
20395
  });
20369
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
+
20370
20695
  // ../../packages/tacho/src/host/model-base-url-toml.ts
20371
20696
  function splitLines(text4) {
20372
20697
  const lines = [];
@@ -20480,13 +20805,13 @@ var init_model_base_url_toml = __esm({
20480
20805
  import { createHash as createHash5 } from "node:crypto";
20481
20806
  import {
20482
20807
  chmodSync as chmodSync3,
20483
- existsSync as existsSync4,
20484
- mkdirSync as mkdirSync4,
20485
- readFileSync as readFileSync5,
20808
+ existsSync as existsSync5,
20809
+ mkdirSync as mkdirSync5,
20810
+ readFileSync as readFileSync6,
20486
20811
  statSync as statSync3,
20487
- unlinkSync as unlinkSync3
20812
+ unlinkSync as unlinkSync4
20488
20813
  } from "node:fs";
20489
- 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";
20490
20815
  function claudeToolSearchEnabled(value) {
20491
20816
  return typeof value === "string" && /^(true|auto(:\d+)?)$/i.test(value);
20492
20817
  }
@@ -20500,19 +20825,19 @@ function isModelProxyBaseUrl(harness, value) {
20500
20825
  }
20501
20826
  function modelBaseUrlFile(harness, homes) {
20502
20827
  if (harness === "claude-code")
20503
- return join12(
20828
+ return join13(
20504
20829
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
20505
20830
  "settings.json"
20506
20831
  );
20507
20832
  if (harness === "codex")
20508
- return join12(
20833
+ return join13(
20509
20834
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
20510
20835
  "config.toml"
20511
20836
  );
20512
- const stellaHome = homes.stellaHome ?? join12(homes.home, ".stella");
20513
- const toml = join12(stellaHome, "stella.toml");
20514
- const json2 = join12(stellaHome, "settings.json");
20515
- 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;
20516
20841
  }
20517
20842
  function keyLabel(harness) {
20518
20843
  if (harness === "claude-code") return `env.${CLAUDE_KEY}`;
@@ -20522,7 +20847,7 @@ function isJsonFile(file) {
20522
20847
  return file.endsWith(".json");
20523
20848
  }
20524
20849
  function sidecarFor(file) {
20525
- return join12(dirname4(file), `.${basename3(file)}.oxagen-model-base-url.json`);
20850
+ return join13(dirname5(file), `.${basename3(file)}.oxagen-model-base-url.json`);
20526
20851
  }
20527
20852
  function modelBaseUrlBackupPath(harness, home, stellaHome, dirs = {}) {
20528
20853
  return sidecarFor(fileFor(harness, homesOf(home, stellaHome, dirs)));
@@ -20539,9 +20864,9 @@ function sha256(text4) {
20539
20864
  return createHash5("sha256").update(text4, "utf8").digest("hex");
20540
20865
  }
20541
20866
  function readSidecar(path2) {
20542
- if (!existsSync4(path2)) return void 0;
20867
+ if (!existsSync5(path2)) return void 0;
20543
20868
  try {
20544
- const parsed = JSON.parse(readFileSync5(path2, "utf8"));
20869
+ const parsed = JSON.parse(readFileSync6(path2, "utf8"));
20545
20870
  if (parsed.schema !== SIDECAR_SCHEMA) return void 0;
20546
20871
  if (typeof parsed.written_sha256 !== "string") return void 0;
20547
20872
  return {
@@ -20566,7 +20891,7 @@ function readSidecar(path2) {
20566
20891
  }
20567
20892
  }
20568
20893
  function writeAtomicPreserving(path2, data) {
20569
- mkdirSync4(dirname4(path2), { recursive: true });
20894
+ mkdirSync5(dirname5(path2), { recursive: true });
20570
20895
  let mode = 384;
20571
20896
  let owner;
20572
20897
  try {
@@ -20582,7 +20907,7 @@ function writeAtomicPreserving(path2, data) {
20582
20907
  }
20583
20908
  function readTextIfExists(path2) {
20584
20909
  try {
20585
- return readFileSync5(path2, "utf8");
20910
+ return readFileSync6(path2, "utf8");
20586
20911
  } catch (error) {
20587
20912
  if (error.code === "ENOENT") return void 0;
20588
20913
  throw error;
@@ -20673,9 +20998,9 @@ function stellaManagedSettingsPath(platform = process.platform, env = process.en
20673
20998
  const named = env["STELLA_MANAGED_SETTINGS"];
20674
20999
  if (named !== void 0 && named.length > 0) return named;
20675
21000
  const dir = platform === "darwin" ? "/Library/Application Support/stella" : "/etc/stella";
20676
- const toml = join12(dir, "stella.toml");
20677
- const json2 = join12(dir, "settings.json");
20678
- 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;
20679
21004
  }
20680
21005
  function managedShadow(harness, managedFile, expected) {
20681
21006
  try {
@@ -20934,7 +21259,7 @@ function applyOne(harness, options) {
20934
21259
  original_base64: orphan ? "" : (
20935
21260
  // The raw bytes, not the decoded text: restore is byte-exact even
20936
21261
  // for a file that is not clean UTF-8.
20937
- (text4 === void 0 ? Buffer.alloc(0) : readFileSync5(file)).toString("base64")
21262
+ (text4 === void 0 ? Buffer.alloc(0) : readFileSync6(file)).toString("base64")
20938
21263
  ),
20939
21264
  written_sha256: orphan ? "" : sha256(next),
20940
21265
  previous,
@@ -20955,7 +21280,7 @@ function restoreOne(harness, options) {
20955
21280
  const sidecar = readSidecar(backup);
20956
21281
  const text4 = readTextIfExists(file);
20957
21282
  const dropSidecar = () => {
20958
- if (existsSync4(backup)) unlinkSync3(backup);
21283
+ if (existsSync5(backup)) unlinkSync4(backup);
20959
21284
  };
20960
21285
  if (text4 === void 0) {
20961
21286
  dropSidecar();
@@ -20968,7 +21293,7 @@ function restoreOne(harness, options) {
20968
21293
  Buffer.from(sidecar.original_base64, "base64")
20969
21294
  );
20970
21295
  } else {
20971
- unlinkSync3(file);
21296
+ unlinkSync4(file);
20972
21297
  }
20973
21298
  dropSidecar();
20974
21299
  return true;
@@ -21070,8 +21395,8 @@ var init_model_base_url = __esm({
21070
21395
  });
21071
21396
 
21072
21397
  // ../../packages/tacho/src/host/run-token.ts
21073
- import { createHmac, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
21074
- 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";
21075
21400
  function keyFromBytes(bytes) {
21076
21401
  if (bytes.length !== KEY_BYTES)
21077
21402
  throw new Error(
@@ -21081,7 +21406,7 @@ function keyFromBytes(bytes) {
21081
21406
  return { bytes, id };
21082
21407
  }
21083
21408
  function generateRunTokenKey() {
21084
- return keyFromBytes(randomBytes3(KEY_BYTES));
21409
+ return keyFromBytes(randomBytes4(KEY_BYTES));
21085
21410
  }
21086
21411
  function runTokenKeyFromHex(hex) {
21087
21412
  const trimmed = hex.trim();
@@ -21092,7 +21417,7 @@ function runTokenKeyFromHex(hex) {
21092
21417
  function loadOrCreateRunTokenKey(path2) {
21093
21418
  try {
21094
21419
  return {
21095
- key: runTokenKeyFromHex(readFileSync6(path2, "utf8")),
21420
+ key: runTokenKeyFromHex(readFileSync7(path2, "utf8")),
21096
21421
  created: false
21097
21422
  };
21098
21423
  } catch (error) {
@@ -21105,7 +21430,7 @@ function loadOrCreateRunTokenKey(path2) {
21105
21430
  }
21106
21431
  function readRunTokenKey(path2) {
21107
21432
  try {
21108
- return runTokenKeyFromHex(readFileSync6(path2, "utf8"));
21433
+ return runTokenKeyFromHex(readFileSync7(path2, "utf8"));
21109
21434
  } catch (error) {
21110
21435
  if (error.code === "ENOENT") return void 0;
21111
21436
  throw error;
@@ -21128,7 +21453,7 @@ function mintRunToken(options) {
21128
21453
  throw new Error("run token would already be expired when minted");
21129
21454
  const claims = {
21130
21455
  v: 1,
21131
- tid: `rt_${randomBytes3(10).toString("hex")}`,
21456
+ tid: `rt_${randomBytes4(10).toString("hex")}`,
21132
21457
  host: options.host,
21133
21458
  harness: options.harness,
21134
21459
  provider: options.provider,
@@ -21239,14 +21564,14 @@ var init_run_token = __esm({
21239
21564
  import { createHash as createHash6 } from "node:crypto";
21240
21565
  import {
21241
21566
  chmodSync as chmodSync4,
21242
- existsSync as existsSync5,
21567
+ existsSync as existsSync6,
21243
21568
  lstatSync as lstatSync2,
21244
- mkdirSync as mkdirSync5,
21245
- readFileSync as readFileSync7,
21569
+ mkdirSync as mkdirSync6,
21570
+ readFileSync as readFileSync8,
21246
21571
  statSync as statSync4,
21247
- unlinkSync as unlinkSync4
21572
+ unlinkSync as unlinkSync5
21248
21573
  } from "node:fs";
21249
- 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";
21250
21575
  function isTachoHelper(value) {
21251
21576
  return typeof value === "string" && HELPER_MARK.test(value);
21252
21577
  }
@@ -21254,16 +21579,16 @@ function helperCommandFor(tachoCommand) {
21254
21579
  return `${tachoCommand} credential issue --harness claude-code`;
21255
21580
  }
21256
21581
  function fileFor2(harness, homes) {
21257
- return harness === "claude-code" ? join13(
21582
+ return harness === "claude-code" ? join14(
21258
21583
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
21259
21584
  "settings.json"
21260
- ) : join13(
21585
+ ) : join14(
21261
21586
  homes.codexHome ?? harnessConfigDirs(homes.home).codexHome,
21262
21587
  "auth.json"
21263
21588
  );
21264
21589
  }
21265
21590
  function sidecarFor2(file) {
21266
- return join13(dirname5(file), `.${basename4(file)}.oxagen-model-credential.json`);
21591
+ return join14(dirname6(file), `.${basename4(file)}.oxagen-model-credential.json`);
21267
21592
  }
21268
21593
  function modelCredentialBackupPath(harness, home, dirs = {}) {
21269
21594
  return sidecarFor2(fileFor2(harness, { ...dirs, home }));
@@ -21272,9 +21597,9 @@ function sha2562(text4) {
21272
21597
  return createHash6("sha256").update(text4, "utf8").digest("hex");
21273
21598
  }
21274
21599
  function readSidecar2(path2) {
21275
- if (!existsSync5(path2)) return void 0;
21600
+ if (!existsSync6(path2)) return void 0;
21276
21601
  try {
21277
- const parsed = JSON.parse(readFileSync7(path2, "utf8"));
21602
+ const parsed = JSON.parse(readFileSync8(path2, "utf8"));
21278
21603
  if (parsed.schema !== SIDECAR_SCHEMA2) return void 0;
21279
21604
  if (typeof parsed.written_sha256 !== "string") return void 0;
21280
21605
  return {
@@ -21294,7 +21619,7 @@ function readSidecar2(path2) {
21294
21619
  }
21295
21620
  }
21296
21621
  function writeAtomicPreserving2(path2, data) {
21297
- mkdirSync5(dirname5(path2), { recursive: true });
21622
+ mkdirSync6(dirname6(path2), { recursive: true });
21298
21623
  let mode = 384;
21299
21624
  let owner;
21300
21625
  try {
@@ -21310,7 +21635,7 @@ function writeAtomicPreserving2(path2, data) {
21310
21635
  }
21311
21636
  function readTextIfExists2(path2) {
21312
21637
  try {
21313
- return readFileSync7(path2, "utf8");
21638
+ return readFileSync8(path2, "utf8");
21314
21639
  } catch (error) {
21315
21640
  if (error.code === "ENOENT") return void 0;
21316
21641
  throw error;
@@ -21543,7 +21868,7 @@ function restoreClaude(options, released) {
21543
21868
  const sidecar = readSidecar2(backup);
21544
21869
  const text4 = readTextIfExists2(file);
21545
21870
  const dropSidecar = () => {
21546
- if (existsSync5(backup)) unlinkSync4(backup);
21871
+ if (existsSync6(backup)) unlinkSync5(backup);
21547
21872
  };
21548
21873
  if (text4 === void 0) {
21549
21874
  if (released !== void 0) {
@@ -21600,7 +21925,7 @@ function restoreCodex(options, released) {
21600
21925
  const sidecar = readSidecar2(backup);
21601
21926
  const text4 = readTextIfExists2(file);
21602
21927
  const dropSidecar = () => {
21603
- if (existsSync5(backup)) unlinkSync4(backup);
21928
+ if (existsSync6(backup)) unlinkSync5(backup);
21604
21929
  };
21605
21930
  if (text4 === void 0) {
21606
21931
  if (released !== void 0) {
@@ -21627,7 +21952,7 @@ function restoreCodex(options, released) {
21627
21952
  if (released !== void 0) auth[CODEX_KEY2] = released.secret;
21628
21953
  else delete auth[CODEX_KEY2];
21629
21954
  if (sidecar !== void 0 && !sidecar.existed && Object.keys(auth).length === 0) {
21630
- unlinkSync4(file);
21955
+ unlinkSync5(file);
21631
21956
  dropSidecar();
21632
21957
  return { changed: true, secretRestored: released !== void 0 };
21633
21958
  }
@@ -21772,42 +22097,13 @@ var init_model_credential = __esm({
21772
22097
  }
21773
22098
  });
21774
22099
 
21775
- // ../../packages/tacho/src/cli/agent-deps.ts
21776
- function agentDeps(deps, paths) {
21777
- if (JSON.stringify(paths) === JSON.stringify(deps.paths)) return deps;
21778
- const rebound = deps.atAgent?.(paths) ?? { ...deps, paths };
21779
- return {
21780
- ...deps,
21781
- ...rebound,
21782
- paths,
21783
- serviceManager: deps.serviceManager,
21784
- out: deps.out,
21785
- err: deps.err
21786
- };
21787
- }
21788
- function depsForHarness(deps, harness) {
21789
- const agent = agentHolding(deps.paths, harness);
21790
- return agent === void 0 ? deps : depsForAgent(deps, agent);
21791
- }
21792
- function depsForAgent(deps, agent) {
21793
- const host = agent.host ?? readHostFileLenient(agent.paths.hostFile).salvaged;
21794
- return agentDeps(deps, withRecordedHarnessFiles(agent.paths, host));
21795
- }
21796
- var init_agent_deps = __esm({
21797
- "../../packages/tacho/src/cli/agent-deps.ts"() {
21798
- "use strict";
21799
- init_agents();
21800
- init_host_file();
21801
- }
21802
- });
21803
-
21804
22100
  // ../../packages/tacho/src/cli/credential.ts
21805
- import { existsSync as existsSync6 } from "node:fs";
21806
- import { dirname as dirname6 } from "node:path";
22101
+ import { existsSync as existsSync7 } from "node:fs";
22102
+ import { dirname as dirname7 } from "node:path";
21807
22103
  function harnessDirsOf(deps) {
21808
22104
  return {
21809
- claudeConfigDir: dirname6(deps.paths.claudeSettings),
21810
- codexHome: dirname6(deps.paths.codexHooks)
22105
+ claudeConfigDir: dirname7(deps.paths.claudeSettings),
22106
+ codexHome: dirname7(deps.paths.codexHooks)
21811
22107
  };
21812
22108
  }
21813
22109
  function parseCredentialMode(value) {
@@ -22058,7 +22354,7 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
22058
22354
  try {
22059
22355
  if (host !== void 0 && !host.harnesses.includes(harness)) {
22060
22356
  const dirs = harnessDirsOf(deps);
22061
- const hasReceipt = existsSync6(
22357
+ const hasReceipt = existsSync7(
22062
22358
  modelCredentialBackupPath(harness, deps.home, dirs)
22063
22359
  );
22064
22360
  if (!hasReceipt && !hasOrphanedModelCredential(harness, deps.home, dirs))
@@ -22289,20 +22585,20 @@ var init_codex_app_server = __esm({
22289
22585
  });
22290
22586
 
22291
22587
  // ../../packages/tacho/src/host/credential-store.ts
22292
- import { createCipheriv, createDecipheriv, randomBytes as randomBytes4 } from "node:crypto";
22588
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes5 } from "node:crypto";
22293
22589
  import {
22294
- closeSync as closeSync3,
22295
- existsSync as existsSync7,
22590
+ closeSync as closeSync4,
22591
+ existsSync as existsSync8,
22296
22592
  fsyncSync as fsyncSync2,
22297
- openSync as openSync3,
22298
- readFileSync as readFileSync8,
22593
+ openSync as openSync4,
22594
+ readFileSync as readFileSync9,
22299
22595
  statSync as statSync5,
22300
- unlinkSync as unlinkSync5,
22301
- writeSync as writeSync2
22596
+ unlinkSync as unlinkSync6,
22597
+ writeSync as writeSync3
22302
22598
  } from "node:fs";
22303
22599
  function readKey(path2) {
22304
22600
  try {
22305
- const hex = readFileSync8(path2, "utf8").trim();
22601
+ const hex = readFileSync9(path2, "utf8").trim();
22306
22602
  if (!/^[0-9a-f]{64}$/i.test(hex))
22307
22603
  throw new Error(`${path2} does not hold a 32-byte key`);
22308
22604
  return Buffer.from(hex, "hex");
@@ -22314,7 +22610,7 @@ function readKey(path2) {
22314
22610
  function loadOrCreateKey(path2) {
22315
22611
  const existing = readKey(path2);
22316
22612
  if (existing !== void 0) return existing;
22317
- const key = randomBytes4(KEY_BYTES2);
22613
+ const key = randomBytes5(KEY_BYTES2);
22318
22614
  writeSensitiveFileAtomic(path2, `${key.toString("hex")}
22319
22615
  `);
22320
22616
  return key;
@@ -22331,16 +22627,16 @@ function overwriteInPlace(path2) {
22331
22627
  let fd2;
22332
22628
  try {
22333
22629
  const size = statSync5(path2).size;
22334
- fd2 = openSync3(path2, "r+");
22335
- 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);
22336
22632
  fsyncSync2(fd2);
22337
22633
  } catch {
22338
22634
  } finally {
22339
- if (fd2 !== void 0) closeSync3(fd2);
22635
+ if (fd2 !== void 0) closeSync4(fd2);
22340
22636
  }
22341
22637
  }
22342
22638
  function seal(key, secret, aad) {
22343
- const nonce = randomBytes4(12);
22639
+ const nonce = randomBytes5(12);
22344
22640
  const cipher = createCipheriv("aes-256-gcm", key, nonce);
22345
22641
  cipher.setAAD(Buffer.from(aad, "utf8"));
22346
22642
  const ciphertext = Buffer.concat([
@@ -22385,7 +22681,7 @@ function openCredentialStore(paths) {
22385
22681
  function readAll() {
22386
22682
  let text4;
22387
22683
  try {
22388
- text4 = readFileSync8(paths.file, "utf8");
22684
+ text4 = readFileSync9(paths.file, "utf8");
22389
22685
  } catch (error) {
22390
22686
  if (error.code === "ENOENT") return [];
22391
22687
  throw error;
@@ -22399,7 +22695,7 @@ function openCredentialStore(paths) {
22399
22695
  }
22400
22696
  function writeAll2(entries2) {
22401
22697
  if (entries2.length === 0) {
22402
- if (existsSync7(paths.file)) unlinkSync5(paths.file);
22698
+ if (existsSync8(paths.file)) unlinkSync6(paths.file);
22403
22699
  return;
22404
22700
  }
22405
22701
  writeSensitiveFileAtomic(
@@ -22454,9 +22750,9 @@ function openCredentialStore(paths) {
22454
22750
  },
22455
22751
  status: () => readAll().map(custodyOf),
22456
22752
  shred: () => {
22457
- if (existsSync7(paths.key)) overwriteInPlace(paths.key);
22753
+ if (existsSync8(paths.key)) overwriteInPlace(paths.key);
22458
22754
  for (const path2 of [paths.file, paths.key])
22459
- if (existsSync7(path2)) unlinkSync5(path2);
22755
+ if (existsSync8(path2)) unlinkSync6(path2);
22460
22756
  }
22461
22757
  };
22462
22758
  }
@@ -22500,16 +22796,87 @@ var init_credential_store = __esm({
22500
22796
  }
22501
22797
  });
22502
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
+
22503
22870
  // ../../packages/tacho/src/host/service.ts
22504
- import { join as join14 } from "node:path";
22871
+ import { join as join15 } from "node:path";
22505
22872
  import {
22506
- existsSync as existsSync8,
22507
- readdirSync as readdirSync5,
22508
- readFileSync as readFileSync9,
22509
- rmdirSync as rmdirSync3,
22510
- unlinkSync as unlinkSync6
22873
+ existsSync as existsSync9,
22874
+ readdirSync as readdirSync6,
22875
+ readFileSync as readFileSync10,
22876
+ rmdirSync as rmdirSync5,
22877
+ unlinkSync as unlinkSync7
22511
22878
  } from "node:fs";
22512
- import { randomBytes as randomBytes5 } from "node:crypto";
22879
+ import { randomBytes as randomBytes6 } from "node:crypto";
22513
22880
  function xmlEscape(value) {
22514
22881
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22515
22882
  }
@@ -22581,8 +22948,8 @@ function blockingSleep(ms) {
22581
22948
  }
22582
22949
  function launchdManager(options) {
22583
22950
  const sleep3 = options.sleep ?? blockingSleep;
22584
- const dir = join14(options.home, "Library", "LaunchAgents");
22585
- const unitPath = join14(dir, `${SERVICE_LABEL}.plist`);
22951
+ const dir = join15(options.home, "Library", "LaunchAgents");
22952
+ const unitPath = join15(dir, `${SERVICE_LABEL}.plist`);
22586
22953
  const domain = `gui/${options.uid ?? process.getuid?.() ?? 501}`;
22587
22954
  const target2 = `${domain}/${SERVICE_LABEL}`;
22588
22955
  const loaded = () => options.exec("launchctl", ["print", target2]).status === 0;
@@ -22601,7 +22968,7 @@ function launchdManager(options) {
22601
22968
  unitPath,
22602
22969
  install: (spec) => {
22603
22970
  const plist = renderLaunchdPlist(spec);
22604
- const unchanged = existsSync8(unitPath) && readFileSync9(unitPath, "utf8") === plist;
22971
+ const unchanged = existsSync9(unitPath) && readFileSync10(unitPath, "utf8") === plist;
22605
22972
  if (unchanged && loaded()) {
22606
22973
  const kicked = options.exec("launchctl", ["kickstart", "-k", target2]);
22607
22974
  if (kicked.status === 0 && loaded()) return;
@@ -22630,10 +22997,10 @@ function launchdManager(options) {
22630
22997
  uninstall: () => {
22631
22998
  options.exec("launchctl", ["bootout", target2]);
22632
22999
  if (!waitUntilGone()) throw stillLoaded("unenroll");
22633
- if (existsSync8(unitPath)) unlinkSync6(unitPath);
23000
+ if (existsSync9(unitPath)) unlinkSync7(unitPath);
22634
23001
  },
22635
23002
  status: () => {
22636
- const installed = existsSync8(unitPath);
23003
+ const installed = existsSync9(unitPath);
22637
23004
  const result2 = options.exec("launchctl", [
22638
23005
  "print",
22639
23006
  `${domain}/${SERVICE_LABEL}`
@@ -22650,8 +23017,8 @@ function noUserManager(result2) {
22650
23017
  }
22651
23018
  function stopByPidFile(options) {
22652
23019
  const { pidPath } = options;
22653
- if (pidPath === void 0 || !existsSync8(pidPath)) return;
22654
- const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
23020
+ if (pidPath === void 0 || !existsSync9(pidPath)) return;
23021
+ const record = parseDaemonPid(readFileSync10(pidPath, "utf8"));
22655
23022
  if (record === void 0) return;
22656
23023
  const sleep3 = options.sleep ?? blockingSleep;
22657
23024
  const processes = options.processes ?? {
@@ -22683,9 +23050,9 @@ function stopByPidFile(options) {
22683
23050
  );
22684
23051
  }
22685
23052
  function systemdManager(options) {
22686
- const dir = join14(options.home, ".config", "systemd", "user");
22687
- const unitPath = join14(dir, "tachod.service");
22688
- 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");
22689
23056
  return {
22690
23057
  kind: "systemd",
22691
23058
  unitPath,
@@ -22695,8 +23062,8 @@ function systemdManager(options) {
22695
23062
  throw new Error(
22696
23063
  `no systemd user manager is available (${probe.stderr.trim().split("\n")[0] ?? ""})`
22697
23064
  );
22698
- const previous = existsSync8(unitPath) ? readFileSync9(unitPath, "utf8") : void 0;
22699
- 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);
22700
23067
  ensureDir(dir, 493);
22701
23068
  writeSensitiveFileAtomic(
22702
23069
  unitPath,
@@ -22736,7 +23103,7 @@ function systemdManager(options) {
22736
23103
  ]);
22737
23104
  if (noUserManager(disabled)) {
22738
23105
  stopByPidFile(options);
22739
- if (existsSync8(unitPath)) unlinkSync6(unitPath);
23106
+ if (existsSync9(unitPath)) unlinkSync7(unitPath);
22740
23107
  return;
22741
23108
  }
22742
23109
  const active2 = options.exec("systemctl", [
@@ -22745,13 +23112,13 @@ function systemdManager(options) {
22745
23112
  "tachod.service"
22746
23113
  ]);
22747
23114
  const stopped2 = active2.status === 3 && ["inactive", "failed"].includes(active2.stdout.trim()) || active2.status === 4 && active2.stdout.trim() === "unknown";
22748
- if (!stopped2 || disabled.status !== 0 && existsSync8(unitPath)) {
23115
+ if (!stopped2 || disabled.status !== 0 && existsSync9(unitPath)) {
22749
23116
  throw new Error(
22750
23117
  `systemctl could not remove tachod.service: ${disabled.stderr.trim() || active2.stderr.trim() || active2.stdout.trim() || "service state is unknown"}`
22751
23118
  );
22752
23119
  }
22753
- const unit = existsSync8(unitPath) ? readFileSync9(unitPath, "utf8") : void 0;
22754
- if (unit !== void 0) unlinkSync6(unitPath);
23120
+ const unit = existsSync9(unitPath) ? readFileSync10(unitPath, "utf8") : void 0;
23121
+ if (unit !== void 0) unlinkSync7(unitPath);
22755
23122
  try {
22756
23123
  const reload = options.exec("systemctl", ["--user", "daemon-reload"]);
22757
23124
  if (reload.status !== 0)
@@ -22764,13 +23131,13 @@ function systemdManager(options) {
22764
23131
  }
22765
23132
  if (unit?.includes(CREATED_WANTS_MARK) === true) {
22766
23133
  try {
22767
- if (readdirSync5(wants).length === 0) rmdirSync3(wants);
23134
+ if (readdirSync6(wants).length === 0) rmdirSync5(wants);
22768
23135
  } catch {
22769
23136
  }
22770
23137
  }
22771
23138
  },
22772
23139
  status: () => {
22773
- const installed = existsSync8(unitPath);
23140
+ const installed = existsSync9(unitPath);
22774
23141
  const result2 = options.exec("systemctl", [
22775
23142
  "--user",
22776
23143
  "is-active",
@@ -22822,11 +23189,11 @@ function tasklistImage(listing, pid) {
22822
23189
  return void 0;
22823
23190
  }
22824
23191
  function schtasksManager(options) {
22825
- const launcher = options.launcherPath ?? join14(options.home, "tachod.cmd");
22826
- 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");
22827
23194
  const livePid = () => {
22828
- if (!existsSync8(pidPath)) return void 0;
22829
- const record = parseDaemonPid(readFileSync9(pidPath, "utf8"));
23195
+ if (!existsSync9(pidPath)) return void 0;
23196
+ const record = parseDaemonPid(readFileSync10(pidPath, "utf8"));
22830
23197
  if (record === void 0) return void 0;
22831
23198
  const { pid } = record;
22832
23199
  const query = options.exec("tasklist", [
@@ -22865,7 +23232,7 @@ function schtasksManager(options) {
22865
23232
  unitPath: launcher,
22866
23233
  install: (spec) => {
22867
23234
  stopDaemon();
22868
- ensureDir(join14(launcher, ".."), 493);
23235
+ ensureDir(join15(launcher, ".."), 493);
22869
23236
  writeSensitiveFileAtomic(
22870
23237
  launcher,
22871
23238
  renderWindowsLauncher(
@@ -22877,7 +23244,7 @@ function schtasksManager(options) {
22877
23244
  // an empty directory stayed behind (#4317). The daemon uses
22878
23245
  // absolute paths only.
22879
23246
  { ...spec, workingDirectory: options.home },
22880
- randomBytes5(8).toString("hex")
23247
+ randomBytes6(8).toString("hex")
22881
23248
  ),
22882
23249
  384
22883
23250
  );
@@ -22919,7 +23286,7 @@ function schtasksManager(options) {
22919
23286
  `schtasks /Delete failed: ${deleted.stderr.trim() || "task removal was not confirmed"}`
22920
23287
  );
22921
23288
  }
22922
- if (existsSync8(launcher)) unlinkSync6(launcher);
23289
+ if (existsSync9(launcher)) unlinkSync7(launcher);
22923
23290
  },
22924
23291
  status: () => {
22925
23292
  const result2 = options.exec("schtasks", [
@@ -22992,15 +23359,15 @@ var init_service = __esm({
22992
23359
  });
22993
23360
 
22994
23361
  // ../../packages/tacho/src/version.ts
22995
- import { readFileSync as readFileSync10 } from "node:fs";
22996
- 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";
22997
23364
  import { fileURLToPath } from "node:url";
22998
23365
  function readVersion() {
22999
- if (true) return "2.1.4-447";
23366
+ if (true) return "2.1.4-460";
23000
23367
  try {
23001
- const here = dirname7(fileURLToPath(import.meta.url));
23368
+ const here = dirname8(fileURLToPath(import.meta.url));
23002
23369
  const pkg = JSON.parse(
23003
- readFileSync10(resolve2(here, "..", "package.json"), "utf8")
23370
+ readFileSync11(resolve2(here, "..", "package.json"), "utf8")
23004
23371
  );
23005
23372
  return pkg.version ?? "0.0.0";
23006
23373
  } catch {
@@ -23017,8 +23384,8 @@ var init_version = __esm({
23017
23384
 
23018
23385
  // ../../packages/tacho/src/cli/deps.ts
23019
23386
  import { spawnSync as spawnSync4 } from "node:child_process";
23020
- import { randomBytes as randomBytes6 } from "node:crypto";
23021
- import { existsSync as existsSync9 } from "node:fs";
23387
+ import { randomBytes as randomBytes7 } from "node:crypto";
23388
+ import { existsSync as existsSync10 } from "node:fs";
23022
23389
  import { get as httpGet, request as httpRequest } from "node:http";
23023
23390
  import { createServer } from "node:net";
23024
23391
  import {
@@ -23029,7 +23396,7 @@ import {
23029
23396
  userInfo
23030
23397
  } from "node:os";
23031
23398
  import { createRequire } from "node:module";
23032
- 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";
23033
23400
  import { fileURLToPath as fileURLToPath2 } from "node:url";
23034
23401
  function configPicker(env, home) {
23035
23402
  let config = {};
@@ -23095,7 +23462,7 @@ function isNativeBuild() {
23095
23462
  function exeName(name, platform) {
23096
23463
  return platform === "win32" ? `${name}.exe` : name;
23097
23464
  }
23098
- function stableExecutablePath(executable, platform, exists = existsSync9) {
23465
+ function stableExecutablePath(executable, platform, exists = existsSync10) {
23099
23466
  const stable = platform === "win32" ? executable.replace(
23100
23467
  /^(.*\\apps\\[^\\]+\\)(?!current\\)[^\\]+(\\.*)$/i,
23101
23468
  "$1current$2"
@@ -23105,7 +23472,7 @@ function stableExecutablePath(executable, platform, exists = existsSync9) {
23105
23472
  );
23106
23473
  return stable !== executable && exists(stable) ? stable : executable;
23107
23474
  }
23108
- 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) {
23109
23476
  const P = platform === "win32" ? win323 : posix2;
23110
23477
  const named = env["TACHO_BIN_DIR"];
23111
23478
  let binDir = named;
@@ -23113,7 +23480,7 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
23113
23480
  if (native) {
23114
23481
  binDir = P.dirname(nodePath);
23115
23482
  } else {
23116
- const here = dirname8(fileURLToPath2(import.meta.url));
23483
+ const here = dirname9(fileURLToPath2(import.meta.url));
23117
23484
  const entryDir = entry2 !== void 0 ? P.dirname(P.resolve(entry2)) : void 0;
23118
23485
  if (entryDir !== void 0 && exists(P.join(entryDir, "tachod.mjs")))
23119
23486
  binDir = entryDir;
@@ -23154,7 +23521,7 @@ function runtimeCommands(entry2 = process.argv[1], env = process.env, nodePath =
23154
23521
  ...flagged
23155
23522
  };
23156
23523
  }
23157
- 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) {
23158
23525
  const P = platform === "win32" ? win323 : posix2;
23159
23526
  const named = env["TACHO_BIN_DIR"];
23160
23527
  let prefix;
@@ -23241,7 +23608,7 @@ function whereExecutable(result2) {
23241
23608
  function shellExecutable(result2) {
23242
23609
  return outputLines(result2).reverse().find((line) => line.startsWith("/"));
23243
23610
  }
23244
- 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) {
23245
23612
  let path2;
23246
23613
  if (platform === "win32") {
23247
23614
  path2 = whereExecutable(exec4("where", [name]));
@@ -23269,7 +23636,7 @@ function harnessFacts(exec4, name, platform = process.platform, env = process.en
23269
23636
  const match = /(\d+\.\d+\.\d+)/.exec(version2.stdout);
23270
23637
  return { path: path2, ...match?.[1] !== void 0 ? { version: match[1] } : {} };
23271
23638
  }
23272
- 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) {
23273
23640
  const candidates = platform === "darwin" ? ["/Applications/Claude.app", `${home}/Applications/Claude.app`] : platform === "win32" ? [
23274
23641
  `${env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`}\\AnthropicClaude`,
23275
23642
  `${env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`}\\Programs\\Claude`
@@ -23279,7 +23646,7 @@ function claudeDesktopFacts(platform = process.platform, home = homedir3(), env
23279
23646
  }
23280
23647
  return { installed: false };
23281
23648
  }
23282
- 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) {
23283
23650
  const local = env["LOCALAPPDATA"] ?? `${home}\\AppData\\Local`;
23284
23651
  const programs = env["PROGRAMFILES"] ?? "C:\\Program Files";
23285
23652
  const candidates = platform === "darwin" ? ["/Applications/Cursor.app", `${home}/Applications/Cursor.app`] : platform === "win32" ? [`${local}\\Programs\\cursor`, `${programs}\\Cursor`] : [];
@@ -23323,8 +23690,8 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23323
23690
  const paths = overrides.paths ?? defaultAgentPaths(tachoHome(env, home, platform));
23324
23691
  const harnessFiles = new HarnessFiles(paths.dir);
23325
23692
  const harnessDirs = {
23326
- claudeConfigDir: dirname8(paths.claudeSettings),
23327
- codexHome: dirname8(paths.codexHooks)
23693
+ claudeConfigDir: dirname9(paths.claudeSettings),
23694
+ codexHome: dirname9(paths.codexHooks)
23328
23695
  };
23329
23696
  const daemonGet = async (path2) => {
23330
23697
  const host = readHostFile(paths.hostFile);
@@ -23432,6 +23799,21 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23432
23799
  `
23433
23800
  );
23434
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
+ },
23435
23817
  harnessWriteProblem: (path2) => harnessFiles.writeProblem(path2),
23436
23818
  settleHarnessFiles: () => settleOrThrow(harnessFiles),
23437
23819
  // The base URL and the vendor key live in the files the hooks went into,
@@ -23542,7 +23924,7 @@ function defaultCliDeps(overrides = {}, managed = {}) {
23542
23924
  server.close(() => resolvePromise(port));
23543
23925
  });
23544
23926
  }),
23545
- randomToken: () => randomBytes6(24).toString("hex"),
23927
+ randomToken: () => randomBytes7(24).toString("hex"),
23546
23928
  sleep: (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms)),
23547
23929
  wrapperVersion: TACHO_VERSION,
23548
23930
  atAgent: (agentPaths2) => defaultCliDeps({ ...overrides, paths: agentPaths2 }, managed),
@@ -23556,6 +23938,7 @@ var init_deps = __esm({
23556
23938
  init_hook_client();
23557
23939
  init_codex_app_server();
23558
23940
  init_credential_store();
23941
+ init_claude_config_lock();
23559
23942
  init_fs();
23560
23943
  init_harness_file();
23561
23944
  init_host_file();
@@ -24074,7 +24457,7 @@ import {
24074
24457
  sign as sign2,
24075
24458
  verify as verify2
24076
24459
  } from "node:crypto";
24077
- import { readFileSync as readFileSync11 } from "node:fs";
24460
+ import { readFileSync as readFileSync12 } from "node:fs";
24078
24461
  function encodePublicKey(privateKey) {
24079
24462
  const spki = createPublicKey2(privateKey).export({
24080
24463
  type: "spki",
@@ -24112,7 +24495,7 @@ function deviceKeyPem(key) {
24112
24495
  function loadOrCreateDeviceKey(path2) {
24113
24496
  try {
24114
24497
  return {
24115
- key: deviceKeyFromPem(readFileSync11(path2, "utf8")),
24498
+ key: deviceKeyFromPem(readFileSync12(path2, "utf8")),
24116
24499
  created: false
24117
24500
  };
24118
24501
  } catch (error) {
@@ -24150,110 +24533,6 @@ var init_device_key = __esm({
24150
24533
  }
24151
24534
  });
24152
24535
 
24153
- // ../../packages/tacho/src/host/install-lock.ts
24154
- import { randomBytes as randomBytes7 } from "node:crypto";
24155
- import {
24156
- closeSync as closeSync4,
24157
- existsSync as existsSync10,
24158
- linkSync,
24159
- mkdirSync as mkdirSync6,
24160
- openSync as openSync4,
24161
- readdirSync as readdirSync6,
24162
- readFileSync as readFileSync12,
24163
- renameSync as renameSync3,
24164
- rmdirSync as rmdirSync4,
24165
- unlinkSync as unlinkSync7,
24166
- writeSync as writeSync3
24167
- } from "node:fs";
24168
- import { dirname as dirname9, join as join15 } from "node:path";
24169
- function readLock(path2) {
24170
- try {
24171
- return readFileSync12(path2, "utf8");
24172
- } catch {
24173
- return void 0;
24174
- }
24175
- }
24176
- function holderOf(text4) {
24177
- try {
24178
- return JSON.parse(text4 ?? "");
24179
- } catch {
24180
- return {};
24181
- }
24182
- }
24183
- function removeQuietly(path2) {
24184
- try {
24185
- unlinkSync7(path2);
24186
- } catch {
24187
- }
24188
- }
24189
- function pidAlive(pid) {
24190
- try {
24191
- process.kill(pid, 0);
24192
- return true;
24193
- } catch (error) {
24194
- return error.code === "EPERM";
24195
- }
24196
- }
24197
- function acquireInstallLock(root, now = Date.now, alive = pidAlive) {
24198
- const path2 = join15(root, "install.lock");
24199
- const created = [];
24200
- let dir = root;
24201
- while (!existsSync10(dir) && dirname9(dir) !== dir) {
24202
- created.unshift(dir);
24203
- dir = dirname9(dir);
24204
- }
24205
- mkdirSync6(root, { recursive: true, mode: 448 });
24206
- const token = randomBytes7(12).toString("hex");
24207
- const liveHolder = (holder) => typeof holder.pid === "number" && typeof holder.at === "number" && now() - holder.at < STALE_AFTER_MS && alive(holder.pid) ? holder.pid : void 0;
24208
- for (let attempt = 0; attempt < 2; attempt += 1) {
24209
- try {
24210
- const fd2 = openSync4(path2, "wx", 384);
24211
- writeSync3(fd2, JSON.stringify({ pid: process.pid, at: now(), token }));
24212
- closeSync4(fd2);
24213
- return {
24214
- release: () => {
24215
- if (holderOf(readLock(path2)).token === token) removeQuietly(path2);
24216
- for (const made of [...created].reverse()) {
24217
- try {
24218
- if (readdirSync6(made).length === 0) rmdirSync4(made);
24219
- } catch {
24220
- }
24221
- }
24222
- }
24223
- };
24224
- } catch (error) {
24225
- if (error.code !== "EEXIST") throw error;
24226
- const live = liveHolder(holderOf(readLock(path2)));
24227
- if (live !== void 0) return { heldBy: live };
24228
- const aside = `${path2}.${process.pid}.${token}.stale`;
24229
- try {
24230
- renameSync3(path2, aside);
24231
- } catch (renameError) {
24232
- if (renameError.code === "ENOENT") continue;
24233
- throw renameError;
24234
- }
24235
- const moved = liveHolder(holderOf(readLock(aside)));
24236
- if (moved !== void 0) {
24237
- try {
24238
- linkSync(aside, path2);
24239
- } catch {
24240
- }
24241
- removeQuietly(aside);
24242
- return { heldBy: moved };
24243
- }
24244
- removeQuietly(aside);
24245
- }
24246
- }
24247
- return { heldBy: -1 };
24248
- }
24249
- var STALE_AFTER_MS;
24250
- var init_install_lock = __esm({
24251
- "../../packages/tacho/src/host/install-lock.ts"() {
24252
- "use strict";
24253
- STALE_AFTER_MS = 10 * 6e4;
24254
- }
24255
- });
24256
-
24257
24536
  // ../../packages/tacho/src/host/wal-index.ts
24258
24537
  import {
24259
24538
  appendFileSync,
@@ -24261,7 +24540,7 @@ import {
24261
24540
  existsSync as existsSync11,
24262
24541
  openSync as openSync5,
24263
24542
  readSync as readSync2,
24264
- statSync as statSync6,
24543
+ statSync as statSync7,
24265
24544
  unlinkSync as unlinkSync8
24266
24545
  } from "node:fs";
24267
24546
  import { open as open2 } from "node:fs/promises";
@@ -24279,7 +24558,7 @@ function parseStoredBody(text4) {
24279
24558
  return void 0;
24280
24559
  return stored;
24281
24560
  }
24282
- function readTailLine(path2, size = statSync6(path2).size, window2 = SCAN_CHUNK_BYTES) {
24561
+ function readTailLine(path2, size = statSync7(path2).size, window2 = SCAN_CHUNK_BYTES) {
24283
24562
  if (size === 0) return void 0;
24284
24563
  const fd2 = openSync5(path2, "r");
24285
24564
  try {
@@ -24496,7 +24775,7 @@ var init_wal_index = __esm({
24496
24775
  }
24497
24776
  /** The index to extend, and the byte the scan starts from. */
24498
24777
  base(sessionUuid2, bodiesPath) {
24499
- const size = statSync6(bodiesPath).size;
24778
+ const size = statSync7(bodiesPath).size;
24500
24779
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24501
24780
  if (held !== void 0 && held.covered <= size) return held;
24502
24781
  this.discard(sessionUuid2);
@@ -24570,7 +24849,7 @@ ${lines.join("\n")}
24570
24849
  * answers undefined.
24571
24850
  */
24572
24851
  covering(sessionUuid2, bodiesPath) {
24573
- const size = statSync6(bodiesPath).size;
24852
+ const size = statSync7(bodiesPath).size;
24574
24853
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24575
24854
  if (held === void 0 || held.covered !== size) return void 0;
24576
24855
  this.remember(sessionUuid2, held);
@@ -24584,7 +24863,7 @@ ${lines.join("\n")}
24584
24863
  * file.
24585
24864
  */
24586
24865
  held(sessionUuid2, bodiesPath) {
24587
- const size = statSync6(bodiesPath).size;
24866
+ const size = statSync7(bodiesPath).size;
24588
24867
  const held = this.cached.get(sessionUuid2) ?? this.load(sessionUuid2);
24589
24868
  if (held === void 0 || held.covered > size) return void 0;
24590
24869
  this.remember(sessionUuid2, held);
@@ -24593,7 +24872,7 @@ ${lines.join("\n")}
24593
24872
  /** The index of a body file, built or extended to cover the whole file. */
24594
24873
  ensure(sessionUuid2, bodiesPath, onInvalidRecord, onWriteFailure) {
24595
24874
  const index = this.base(sessionUuid2, bodiesPath);
24596
- const size = statSync6(bodiesPath).size;
24875
+ const size = statSync7(bodiesPath).size;
24597
24876
  if (index.covered < size) {
24598
24877
  const result2 = {
24599
24878
  added: [],
@@ -24647,7 +24926,7 @@ ${lines.join("\n")}
24647
24926
  for (let attempt = 0; ; attempt += 1) {
24648
24927
  const generation = this.generationOf(sessionUuid2);
24649
24928
  const index = this.base(sessionUuid2, bodiesPath);
24650
- const size = statSync6(bodiesPath).size;
24929
+ const size = statSync7(bodiesPath).size;
24651
24930
  if (index.covered >= size) {
24652
24931
  this.remember(sessionUuid2, index);
24653
24932
  return index;
@@ -24687,7 +24966,7 @@ import {
24687
24966
  readSync as readSync3,
24688
24967
  truncateSync,
24689
24968
  renameSync as renameSync4,
24690
- statSync as statSync7,
24969
+ statSync as statSync8,
24691
24970
  unlinkSync as unlinkSync9,
24692
24971
  writeSync as writeSync4
24693
24972
  } from "node:fs";
@@ -24698,7 +24977,7 @@ function activityAt(event) {
24698
24977
  return Number.isFinite(at) ? at : Number.NEGATIVE_INFINITY;
24699
24978
  }
24700
24979
  function sizeOf(path2) {
24701
- return statSync7(path2, { throwIfNoEntry: false })?.size ?? 0;
24980
+ return statSync8(path2, { throwIfNoEntry: false })?.size ?? 0;
24702
24981
  }
24703
24982
  var BODY_READ_SLICE, BODY_DROP_SLICE_BYTES, BODY_DROP_SCANS, NEWLINE, EVENT_TAIL_WINDOW, WalRecoveryConflict, Wal;
24704
24983
  var init_wal = __esm({
@@ -25058,7 +25337,7 @@ ${lines.join("\n")}
25058
25337
  for (const session of sessions) {
25059
25338
  if (sizes.has(session)) continue;
25060
25339
  const path2 = pathOf2(session);
25061
- sizes.set(session, existsSync12(path2) ? statSync7(path2).size : void 0);
25340
+ sizes.set(session, existsSync12(path2) ? statSync8(path2).size : void 0);
25062
25341
  }
25063
25342
  return sizes;
25064
25343
  }
@@ -25074,7 +25353,7 @@ ${lines.join("\n")}
25074
25353
  try {
25075
25354
  if (!existsSync12(path2)) continue;
25076
25355
  if (size === void 0) unlinkSync9(path2);
25077
- else if (statSync7(path2).size !== size) truncateSync(path2, size);
25356
+ else if (statSync8(path2).size !== size) truncateSync(path2, size);
25078
25357
  } catch (error) {
25079
25358
  onFailure(session, error);
25080
25359
  }
@@ -25737,7 +26016,7 @@ ${lines.join("\n")}
25737
26016
  const lastSeq = this.lastWrittenSeq(sessionUuid2);
25738
26017
  if (lastSeq === void 0) return 0;
25739
26018
  const path2 = this.bodyFileFor(sessionUuid2);
25740
- const size = statSync7(path2).size;
26019
+ const size = statSync8(path2).size;
25741
26020
  let keep = size;
25742
26021
  let cut2 = 0;
25743
26022
  while (keep > 0) {
@@ -25770,7 +26049,7 @@ ${lines.join("\n")}
25770
26049
  */
25771
26050
  lastWrittenSeq(sessionUuid2) {
25772
26051
  const path2 = this.fileFor(sessionUuid2);
25773
- if (!existsSync12(path2) || statSync7(path2).size === 0) return -1;
26052
+ if (!existsSync12(path2) || statSync8(path2).size === 0) return -1;
25774
26053
  return this.lastEvent(sessionUuid2)?.seq;
25775
26054
  }
25776
26055
  /**
@@ -25801,7 +26080,7 @@ ${lines.join("\n")}
25801
26080
  *eventsBackward(sessionUuid2) {
25802
26081
  const path2 = this.fileFor(sessionUuid2);
25803
26082
  if (!existsSync12(path2)) return;
25804
- let end = statSync7(path2).size;
26083
+ let end = statSync8(path2).size;
25805
26084
  while (end > 0) {
25806
26085
  const line = readTailLine(path2, end, EVENT_TAIL_WINDOW);
25807
26086
  if (line === void 0) return;
@@ -25832,7 +26111,7 @@ ${lines.join("\n")}
25832
26111
  this.unverifiedResume.delete(sessionUuid2);
25833
26112
  let holds2 = false;
25834
26113
  try {
25835
- if (mark.offset <= statSync7(path2).size) {
26114
+ if (mark.offset <= statSync8(path2).size) {
25836
26115
  const line = readTailLine(path2, mark.offset, EVENT_TAIL_WINDOW);
25837
26116
  holds2 = line !== void 0 && line.terminated && line.end === mark.offset && JSON.parse(line.text).seq === mark.afterSeq;
25838
26117
  }
@@ -26228,7 +26507,7 @@ ${lines.join("\n")}
26228
26507
  if (sealedAt === void 0) continue;
26229
26508
  const path2 = this.fileFor(session);
26230
26509
  try {
26231
- const age = now - Math.max(Date.parse(sealedAt), statSync7(path2).mtimeMs);
26510
+ const age = now - Math.max(Date.parse(sealedAt), statSync8(path2).mtimeMs);
26232
26511
  if (age < retainMs) continue;
26233
26512
  const last = this.lastSeqOf(session);
26234
26513
  if (last < 0 || last > this.shippedThrough(session)) continue;
@@ -26250,7 +26529,7 @@ ${lines.join("\n")}
26250
26529
  if (removed.includes(session) || existsSync12(this.fileFor(session)))
26251
26530
  continue;
26252
26531
  try {
26253
- if (now - statSync7(this.bodyFileFor(session)).mtimeMs < retainMs)
26532
+ if (now - statSync8(this.bodyFileFor(session)).mtimeMs < retainMs)
26254
26533
  continue;
26255
26534
  } catch (error) {
26256
26535
  this.bodyFailure(session, "cleanup", error);
@@ -27304,7 +27583,7 @@ import {
27304
27583
  existsSync as existsSync15,
27305
27584
  lstatSync as lstatSync3,
27306
27585
  readdirSync as readdirSync8,
27307
- rmdirSync as rmdirSync5,
27586
+ rmdirSync as rmdirSync6,
27308
27587
  rmSync as rmSync2,
27309
27588
  unlinkSync as unlinkSync10
27310
27589
  } from "node:fs";
@@ -27541,6 +27820,24 @@ async function stripEnrollmentHooks(host, deps, settle3 = false) {
27541
27820
  }
27542
27821
  });
27543
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
+ }
27544
27841
  if (settle3 && failed3.length === 0) {
27545
27842
  try {
27546
27843
  deps.settleHarnessFiles?.();
@@ -27558,6 +27855,7 @@ async function stripEnrollmentHooks(host, deps, settle3 = false) {
27558
27855
  cursorChanged,
27559
27856
  stellaChanged,
27560
27857
  claudeDesktopChanged,
27858
+ ...claudeCodeMcpChanged !== void 0 ? { claudeCodeMcpChanged } : {},
27561
27859
  failed: failed3
27562
27860
  };
27563
27861
  }
@@ -27670,7 +27968,7 @@ function restartForRemaining(installed, deps) {
27670
27968
  }
27671
27969
  function removeIfEmpty(dir) {
27672
27970
  try {
27673
- if (readdirSync8(dir).length === 0) rmdirSync5(dir);
27971
+ if (readdirSync8(dir).length === 0) rmdirSync6(dir);
27674
27972
  } catch {
27675
27973
  }
27676
27974
  }
@@ -27760,6 +28058,11 @@ async function unenrollLocked(options, deps) {
27760
28058
  " Quit Claude Desktop and open it again for the change to take effect"
27761
28059
  );
27762
28060
  }
28061
+ if (stripped.claudeCodeMcpChanged !== void 0) {
28062
+ deps.out(
28063
+ ` Oxagen's MCP server removed from ${stripped.claudeCodeMcpChanged}`
28064
+ );
28065
+ }
27763
28066
  for (const failure2 of stripped.failed) {
27764
28067
  incomplete = true;
27765
28068
  warnings.push(
@@ -27891,6 +28194,7 @@ var init_unenroll = __esm({
27891
28194
  "../../packages/tacho/src/cli/unenroll.ts"() {
27892
28195
  "use strict";
27893
28196
  init_install_lock();
28197
+ init_claude_code_mcp_writer();
27894
28198
  init_claude_desktop_writer();
27895
28199
  init_codex_hook_trust();
27896
28200
  init_codex_writer();
@@ -28639,6 +28943,32 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
28639
28943
  deps.out(" already present; nothing to change");
28640
28944
  }
28641
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
+ }
28642
28972
  hook("codex", () => {
28643
28973
  deps.out(` Codex: ${deps.paths.codexHooks}`);
28644
28974
  const merged = mergeCodexHooks(deps.readCodexHooks(), hookConfig);
@@ -29013,6 +29343,7 @@ var init_enroll = __esm({
29013
29343
  init_credential();
29014
29344
  init_github();
29015
29345
  init_agent_deps();
29346
+ init_claude_code_mcp();
29016
29347
  init_daemon_service();
29017
29348
  init_policy_mode();
29018
29349
  init_status();
@@ -29915,9 +30246,9 @@ import {
29915
30246
  readFileSync as readFileSync14,
29916
30247
  readlinkSync as readlinkSync3,
29917
30248
  realpathSync as realpathSync2,
29918
- rmdirSync as rmdirSync6,
30249
+ rmdirSync as rmdirSync7,
29919
30250
  rmSync as rmSync4,
29920
- statSync as statSync8,
30251
+ statSync as statSync9,
29921
30252
  unlinkSync as unlinkSync11
29922
30253
  } from "node:fs";
29923
30254
  import { dirname as dirname13, isAbsolute, join as join19, relative, resolve as resolve6, sep } from "node:path";
@@ -30073,7 +30404,7 @@ function removeFile(path2, tally2, note = "") {
30073
30404
  function removeDirIfEmpty(dir) {
30074
30405
  try {
30075
30406
  if (readdirSync9(dir).length > 0) return false;
30076
- rmdirSync6(dir);
30407
+ rmdirSync7(dir);
30077
30408
  return true;
30078
30409
  } catch {
30079
30410
  return false;
@@ -30092,7 +30423,7 @@ function removeProfileBlock(path2, tally2) {
30092
30423
  if (updated === text4) return;
30093
30424
  try {
30094
30425
  const real = realpathSync2(path2);
30095
- writeFileAtomic(real, updated, { mode: statSync8(real).mode & 4095 });
30426
+ writeFileAtomic(real, updated, { mode: statSync9(real).mode & 4095 });
30096
30427
  tally2.removed.push(`the Oxagen PATH lines in ${path2}`);
30097
30428
  } catch (error) {
30098
30429
  tally2.left.push(`cannot rewrite ${path2}: ${reasonOf3(error)}`);
@@ -31163,10 +31494,12 @@ __export(cli_exports, {
31163
31494
  MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
31164
31495
  SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
31165
31496
  STATIC_TOKEN_RENEW_WINDOW_MS: () => STATIC_TOKEN_RENEW_WINDOW_MS,
31497
+ addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
31166
31498
  agentNameFromCommand: () => agentNameFromCommand,
31167
31499
  backfillCommand: () => backfillCommand,
31168
31500
  brokerCredentials: () => brokerCredentials,
31169
31501
  brokerEnvVar: () => brokerEnvVar,
31502
+ canServeOxagenTools: () => canServeOxagenTools,
31170
31503
  claudeDesktopFacts: () => claudeDesktopFacts,
31171
31504
  claudeFacts: () => claudeFacts,
31172
31505
  credentialIssue: () => credentialIssue,
@@ -31192,6 +31525,7 @@ __export(cli_exports, {
31192
31525
  isNativeBuild: () => isNativeBuild,
31193
31526
  moveOffTachoNames: () => moveOffTachoNames,
31194
31527
  namesTachoExecutable: () => namesTachoExecutable,
31528
+ needsClaudeCodeMcp: () => needsClaudeCodeMcp,
31195
31529
  observedTiers: () => observedTiers,
31196
31530
  oxagenRuntimeCommands: () => oxagenRuntimeCommands,
31197
31531
  parseCredentialMode: () => parseCredentialMode,
@@ -31200,6 +31534,7 @@ __export(cli_exports, {
31200
31534
  readDesktopRecord: () => readDesktopRecord,
31201
31535
  reassign: () => reassign,
31202
31536
  reassignTarget: () => reassignTarget,
31537
+ registerClaudeCodeMcp: () => registerClaudeCodeMcp,
31203
31538
  removePathBlocks: () => removePathBlocks,
31204
31539
  repointCommands: () => repointCommands,
31205
31540
  resolveApiUrl: () => resolveApiUrl,
@@ -31242,6 +31577,7 @@ var init_cli = __esm({
31242
31577
  "../../packages/tacho/src/cli/index.ts"() {
31243
31578
  "use strict";
31244
31579
  init_backfill3();
31580
+ init_claude_code_mcp();
31245
31581
  init_credential();
31246
31582
  init_deps();
31247
31583
  init_detect();
@@ -37378,6 +37714,7 @@ function reflectionAsk(record, options) {
37378
37714
  const state = states.get(record);
37379
37715
  if (state === void 0 || state.asked || state.reflected || state.signals.size === 0)
37380
37716
  return void 0;
37717
+ if (!options.toolRegistered()) return void 0;
37381
37718
  state.asked = true;
37382
37719
  const full = askText(state, true);
37383
37720
  return full.length <= REFLECTION_ASK_MAX_CHARS ? full : askText(state, false);
@@ -38683,7 +39020,10 @@ async function routeHook(raw, env, deps, replay, harness, agent, hookId) {
38683
39020
  const ask = input.agent_id !== void 0 ? void 0 : reflectionAsk(record, {
38684
39021
  harness: record.customAgent !== void 0 ? void 0 : record.harness ?? "claude-code",
38685
39022
  stopHookActive: input["stop_hook_active"] === true,
38686
- replayed: replay !== void 0
39023
+ replayed: replay !== void 0,
39024
+ toolRegistered: () => deps.reflectionToolRegistered?.({
39025
+ startedAt: record.startedAt
39026
+ }) ?? false
38687
39027
  });
38688
39028
  const texts = [
38689
39029
  ...drainMidTurn(input, record, deps, events, replay, ask?.length ?? 0),
@@ -45688,7 +46028,7 @@ var init_model_proxy_listener = __esm({
45688
46028
  import {
45689
46029
  createServer as createServer4
45690
46030
  } from "node:http";
45691
- 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";
45692
46032
  import { randomBytes as randomBytes10 } from "node:crypto";
45693
46033
  import { createConnection } from "node:net";
45694
46034
  function readBody(req) {
@@ -45960,7 +46300,7 @@ function createRequestHandler(api, log, options = {}) {
45960
46300
  }
45961
46301
  async function removeStaleSocket(socketPath) {
45962
46302
  if (!existsSync18(socketPath)) return;
45963
- if (!statSync9(socketPath).isSocket()) {
46303
+ if (!statSync10(socketPath).isSocket()) {
45964
46304
  throw new Error(`collector socket path is not a socket: ${socketPath}`);
45965
46305
  }
45966
46306
  const stale = await new Promise((resolve20, reject) => {
@@ -46748,7 +47088,7 @@ import {
46748
47088
  readdirSync as readdirSync12,
46749
47089
  readFileSync as readFileSync18,
46750
47090
  renameSync as renameSync5,
46751
- statSync as statSync10,
47091
+ statSync as statSync11,
46752
47092
  unlinkSync as unlinkSync13,
46753
47093
  writeFileSync as writeFileSync3
46754
47094
  } from "node:fs";
@@ -46923,6 +47263,22 @@ async function initializeDaemon(options, server, port, ready) {
46923
47263
  ))
46924
47264
  memoryUses.note({ ...file, ...run });
46925
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
+ };
46926
47282
  const localServers = options.localServers === true ? createMachineLoop({
46927
47283
  host: () => host,
46928
47284
  fetch: options.fetch ?? globalThis.fetch,
@@ -46988,7 +47344,7 @@ async function initializeDaemon(options, server, port, ready) {
46988
47344
  processStarts: hookProcessStarts,
46989
47345
  adoptedChain: (sessionUuid2) => backfillLedger.chainHead(sessionUuid2)
46990
47346
  });
46991
- const priorStateWrittenAt = existsSync20(paths.daemonState) ? statSync10(paths.daemonState).mtimeMs : void 0;
47347
+ const priorStateWrittenAt = existsSync20(paths.daemonState) ? statSync11(paths.daemonState).mtimeMs : void 0;
46992
47348
  const sealedState = parseSealedState(
46993
47349
  readJsonStateFile(
46994
47350
  paths.daemonSealedState,
@@ -48064,7 +48420,8 @@ async function initializeDaemon(options, server, port, ready) {
48064
48420
  cedar: loadCedarRuntime,
48065
48421
  skills,
48066
48422
  ...recallMemories !== void 0 ? { recallMemories } : {},
48067
- ...noteMemoryReads !== void 0 ? { noteMemoryReads } : {}
48423
+ ...noteMemoryReads !== void 0 ? { noteMemoryReads } : {},
48424
+ reflectionToolRegistered
48068
48425
  },
48069
48426
  envelope2.replay,
48070
48427
  envelope2.harness,
@@ -49013,7 +49370,7 @@ async function initializeDaemon(options, server, port, ready) {
49013
49370
  if (!name.endsWith(".json")) continue;
49014
49371
  const file = join34(paths.quarantine, name);
49015
49372
  try {
49016
- if (at - statSync10(file).mtimeMs < retainMs) continue;
49373
+ if (at - statSync11(file).mtimeMs < retainMs) continue;
49017
49374
  unlinkSync13(file);
49018
49375
  removed += 1;
49019
49376
  } catch {
@@ -49443,6 +49800,7 @@ var init_daemon = __esm({
49443
49800
  init_device_key();
49444
49801
  init_fs();
49445
49802
  init_host_file();
49803
+ init_claude_code_mcp_writer();
49446
49804
  init_model_base_url();
49447
49805
  init_model_credential();
49448
49806
  init_run_token();
@@ -49878,7 +50236,7 @@ import {
49878
50236
  constants as constants3,
49879
50237
  fstatSync as fstatSync2,
49880
50238
  lstatSync as lstatSync7,
49881
- mkdirSync as mkdirSync7,
50239
+ mkdirSync as mkdirSync8,
49882
50240
  openSync as openSync7,
49883
50241
  readSync as readSync4,
49884
50242
  realpathSync as realpathSync5,
@@ -49887,7 +50245,7 @@ import {
49887
50245
  import { dirname as dirname20, join as join35, resolve as resolve13 } from "node:path";
49888
50246
  function bundlePath(root, relative10, create2 = false) {
49889
50247
  const absolute = resolve13(root);
49890
- if (create2) mkdirSync7(absolute, { recursive: true, mode: 448 });
50248
+ if (create2) mkdirSync8(absolute, { recursive: true, mode: 448 });
49891
50249
  if (lstatSync7(absolute).isSymbolicLink() || !lstatSync7(absolute).isDirectory()) {
49892
50250
  throw new Error("ARP bundle root must be a directory, not a symlink");
49893
50251
  }
@@ -49903,7 +50261,7 @@ function bundlePath(root, relative10, create2 = false) {
49903
50261
  }
49904
50262
  } catch (error) {
49905
50263
  if (error.code !== "ENOENT") throw error;
49906
- if (create2 && i2 < parts.length - 1) mkdirSync7(current, { mode: 448 });
50264
+ if (create2 && i2 < parts.length - 1) mkdirSync8(current, { mode: 448 });
49907
50265
  else if (i2 < parts.length - 1) throw error;
49908
50266
  }
49909
50267
  }
@@ -50767,7 +51125,7 @@ import {
50767
51125
  constants as constants4,
50768
51126
  fstatSync as fstatSync3,
50769
51127
  lstatSync as lstatSync8,
50770
- mkdirSync as mkdirSync8,
51128
+ mkdirSync as mkdirSync9,
50771
51129
  openSync as openSync8,
50772
51130
  readSync as readSync5,
50773
51131
  realpathSync as realpathSync6,
@@ -50897,7 +51255,7 @@ function captureCheckpoint(options) {
50897
51255
  executable: (file.mode & 73) !== 0
50898
51256
  });
50899
51257
  }
50900
- mkdirSync8(out, { mode: 448 });
51258
+ mkdirSync9(out, { mode: 448 });
50901
51259
  try {
50902
51260
  const blob = (value) => storeBlob(out, Buffer.from(jcs(value)), "application/json");
50903
51261
  const entries2 = [];
@@ -51084,16 +51442,16 @@ function prepareCheckpoint(options) {
51084
51442
  }
51085
51443
  return { entry: entry2, bytes: null };
51086
51444
  });
51087
- mkdirSync8(destination, { mode: 448 });
51445
+ mkdirSync9(destination, { mode: 448 });
51088
51446
  try {
51089
51447
  const restored = join36(destination, "workspace");
51090
- mkdirSync8(restored, { mode: 448 });
51448
+ mkdirSync9(restored, { mode: 448 });
51091
51449
  for (const { entry: entry2, bytes } of files2) {
51092
51450
  const path2 = join36(restored, entry2.path);
51093
51451
  if (entry2.kind === "directory")
51094
- mkdirSync8(path2, { recursive: true, mode: 448 });
51452
+ mkdirSync9(path2, { recursive: true, mode: 448 });
51095
51453
  else {
51096
- mkdirSync8(dirname21(path2), { recursive: true, mode: 448 });
51454
+ mkdirSync9(dirname21(path2), { recursive: true, mode: 448 });
51097
51455
  if (bytes === null) fail2("Workspace file has no bytes.");
51098
51456
  writeFileSync6(path2, bytes, {
51099
51457
  flag: "wx",
@@ -51102,7 +51460,7 @@ function prepareCheckpoint(options) {
51102
51460
  }
51103
51461
  }
51104
51462
  const promptDir = join36(restored, ".arp");
51105
- mkdirSync8(promptDir, { mode: 448 });
51463
+ mkdirSync9(promptDir, { mode: 448 });
51106
51464
  const promptFile = join36(promptDir, "handoff.json");
51107
51465
  const prompt = {
51108
51466
  checkpoint_digest: checkpointDigest,
@@ -51677,7 +52035,7 @@ var init_package = __esm({
51677
52035
  "package.json"() {
51678
52036
  package_default = {
51679
52037
  name: "@oxagen/cli",
51680
- version: "2.1.4-447",
52038
+ version: "2.1.4-460",
51681
52039
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
51682
52040
  type: "module",
51683
52041
  main: "./dist/index.js",
@@ -51788,7 +52146,7 @@ var init_atomic_write = __esm({
51788
52146
  // src/lib/config.ts
51789
52147
  import { homedir as homedir8 } from "os";
51790
52148
  import { join as join38 } from "path";
51791
- 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";
51792
52150
  function getConfigDir() {
51793
52151
  return CONFIG_DIR;
51794
52152
  }
@@ -51809,7 +52167,7 @@ Run \`oxagen login\` to re-authenticate.
51809
52167
  function writeConfig(patch) {
51810
52168
  const current = readConfig();
51811
52169
  const next = { ...current, ...patch };
51812
- if (!existsSync22(CONFIG_DIR)) mkdirSync9(CONFIG_DIR, { recursive: true });
52170
+ if (!existsSync22(CONFIG_DIR)) mkdirSync10(CONFIG_DIR, { recursive: true });
51813
52171
  atomicWriteFileSync(CONFIG_FILE, JSON.stringify(next, null, 2));
51814
52172
  }
51815
52173
  function clearConfig() {
@@ -52364,6 +52722,34 @@ var init_api = __esm({
52364
52722
  }
52365
52723
  });
52366
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
+
52367
52753
  // src/commands/move-off-tacho.ts
52368
52754
  async function moveOffTacho(writer) {
52369
52755
  const { defaultCliDeps: defaultCliDeps2, moveOffTachoNames: moveOffTachoNames2, oxagenRuntimeCommands: oxagenRuntimeCommands2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
@@ -52463,6 +52849,7 @@ async function handleTachoEnroll(opts, writer = stdoutWriter) {
52463
52849
  async function handleTachoStatus(opts, writer = stdoutWriter) {
52464
52850
  const { status: status2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
52465
52851
  await moveOffTacho(writer);
52852
+ await addClaudeCodeMcp(writer);
52466
52853
  const report = await status2(opts, await tachoDeps(writer));
52467
52854
  const reports = [report, ...report.enrollments ?? []];
52468
52855
  return report.enrolled && !reports.some((entry2) => entry2.shipping?.healthy === false);
@@ -52679,6 +53066,7 @@ var init_tacho = __esm({
52679
53066
  init_config();
52680
53067
  init_capture_writer();
52681
53068
  init_api();
53069
+ init_add_claude_code_mcp();
52682
53070
  init_move_off_tacho();
52683
53071
  }
52684
53072
  });
@@ -56809,7 +57197,7 @@ var init_src2 = __esm({
56809
57197
  // src/commands/workspace-link.ts
56810
57198
  import {
56811
57199
  existsSync as existsSync23,
56812
- mkdirSync as mkdirSync10,
57200
+ mkdirSync as mkdirSync11,
56813
57201
  readFileSync as readFileSync22,
56814
57202
  rmSync as rmSync9,
56815
57203
  writeFileSync as writeFileSync8
@@ -56834,7 +57222,7 @@ function readWorkspaceLink(cwd) {
56834
57222
  }
56835
57223
  function writeWorkspaceLink(cwd, link) {
56836
57224
  const dir = join44(cwd, ".oxagen");
56837
- mkdirSync10(dir, { recursive: true });
57225
+ mkdirSync11(dir, { recursive: true });
56838
57226
  writeFileSync8(workspaceLinkPath(cwd), JSON.stringify(link, null, 2), "utf8");
56839
57227
  }
56840
57228
  var init_workspace_link = __esm({
@@ -58284,7 +58672,7 @@ var init_runtime_shared = __esm({
58284
58672
  function spanDays(from, to) {
58285
58673
  return (Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / DAY_MS + 1;
58286
58674
  }
58287
- 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;
58288
58676
  var init_spend_shared = __esm({
58289
58677
  "../../packages/oxagen/src/contracts/spend.shared.ts"() {
58290
58678
  "use strict";
@@ -58333,6 +58721,11 @@ var init_spend_shared = __esm({
58333
58721
  */
58334
58722
  server_tool_request: external_exports.number().int().nonnegative()
58335
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();
58336
58729
  spendGroupKindSchema = external_exports.enum([
58337
58730
  "operator",
58338
58731
  "agent",
@@ -73697,6 +74090,17 @@ async function runContext(runId, opts = {}, writer = stdoutWriter) {
73697
74090
  })
73698
74091
  ])
73699
74092
  ];
74093
+ const composition = result2.composition ?? null;
74094
+ if (composition !== null)
74095
+ rows.push([
74096
+ "All",
74097
+ `${count5(composition.requests)} request(s)`,
74098
+ count5(composition.promptTokens),
74099
+ ...BLOCKS.map((kind) => {
74100
+ const tokens = composition.blocks[kind];
74101
+ return tokens === null ? "-" : count5(tokens);
74102
+ })
74103
+ ]);
73700
74104
  const widths = (rows[0] ?? []).map(
73701
74105
  (_, col) => Math.max(...rows.map((r) => (r[col] ?? "").length))
73702
74106
  );
@@ -74221,7 +74625,7 @@ __export(verify_exports, {
74221
74625
  readBundle: () => readBundle,
74222
74626
  verifyBundle: () => verifyBundle3
74223
74627
  });
74224
- import { existsSync as existsSync25, readFileSync as readFileSync23, statSync as statSync11 } from "node:fs";
74628
+ import { existsSync as existsSync25, readFileSync as readFileSync23, statSync as statSync12 } from "node:fs";
74225
74629
  import { join as join47 } from "node:path";
74226
74630
  function readDirectory(dir) {
74227
74631
  const read2 = (name) => {
@@ -74267,7 +74671,7 @@ function readBundle(path2) {
74267
74671
  if (!existsSync25(path2)) {
74268
74672
  throw new BundleReadError(`${path2} does not exist.`);
74269
74673
  }
74270
- return statSync11(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
74674
+ return statSync12(path2).isDirectory() ? readDirectory(path2) : readZip(path2);
74271
74675
  }
74272
74676
  function digestLabel(state) {
74273
74677
  return state === "not_carried" ? "not carried" : state;
@@ -75161,7 +75565,7 @@ import {
75161
75565
  appendFileSync as appendFileSync4,
75162
75566
  existsSync as existsSync26,
75163
75567
  lstatSync as lstatSync9,
75164
- mkdirSync as mkdirSync11,
75568
+ mkdirSync as mkdirSync12,
75165
75569
  readFileSync as readFileSync24,
75166
75570
  readdirSync as readdirSync13,
75167
75571
  realpathSync as realpathSync8,
@@ -75258,7 +75662,7 @@ function machineId(configDir = getConfigDir()) {
75258
75662
  }
75259
75663
  if (secret === null) {
75260
75664
  secret = randomBytes11(32).toString("hex");
75261
- mkdirSync11(configDir, { recursive: true });
75665
+ mkdirSync12(configDir, { recursive: true });
75262
75666
  writeFileSync10(file, `${secret}
75263
75667
  `, { encoding: "utf8", mode: 384 });
75264
75668
  }
@@ -75810,12 +76214,12 @@ __export(pull_exports, {
75810
76214
  import { createHash as createHash19 } from "node:crypto";
75811
76215
  import {
75812
76216
  lstatSync as lstatSync10,
75813
- mkdirSync as mkdirSync12,
76217
+ mkdirSync as mkdirSync13,
75814
76218
  readFileSync as readFileSync25,
75815
76219
  readdirSync as readdirSync14,
75816
76220
  realpathSync as realpathSync9,
75817
76221
  rmSync as rmSync10,
75818
- rmdirSync as rmdirSync7
76222
+ rmdirSync as rmdirSync8
75819
76223
  } from "node:fs";
75820
76224
  import {
75821
76225
  dirname as dirname27,
@@ -75986,7 +76390,7 @@ function pruneEmptyDirs(root, path2) {
75986
76390
  while (dir.startsWith(stop + sep7)) {
75987
76391
  try {
75988
76392
  if (readdirSync14(dir).length > 0) return;
75989
- rmdirSync7(dir);
76393
+ rmdirSync8(dir);
75990
76394
  } catch {
75991
76395
  return;
75992
76396
  }
@@ -75998,7 +76402,7 @@ function applyPlan(root, plan) {
75998
76402
  for (const d of plan.deletes) assertInsideOxagen(root, d);
75999
76403
  for (const w of plan.writes) {
76000
76404
  const abs = join49(root, ...w.path.split("/"));
76001
- mkdirSync12(dirname27(abs), { recursive: true });
76405
+ mkdirSync13(dirname27(abs), { recursive: true });
76002
76406
  atomicWriteFileSync(abs, w.content);
76003
76407
  }
76004
76408
  for (const d of plan.deletes) {
@@ -113384,10 +113788,10 @@ import { execFileSync as execFileSync2 } from "node:child_process";
113384
113788
  import { createHash as createHash20 } from "node:crypto";
113385
113789
  import {
113386
113790
  lstatSync as lstatSync11,
113387
- mkdirSync as mkdirSync13,
113791
+ mkdirSync as mkdirSync14,
113388
113792
  readFileSync as readFileSync26,
113389
113793
  readlinkSync as readlinkSync4,
113390
- statSync as statSync12
113794
+ statSync as statSync13
113391
113795
  } from "node:fs";
113392
113796
  import { dirname as dirname28, isAbsolute as isAbsolute14, join as join50, relative as relative9, resolve as resolve19, sep as sep8 } from "node:path";
113393
113797
  function git5(cwd, args, input) {
@@ -113495,14 +113899,14 @@ function chooseBase(root, ref2) {
113495
113899
  }
113496
113900
  function isFile(path2) {
113497
113901
  try {
113498
- return statSync12(path2).isFile();
113902
+ return statSync13(path2).isFile();
113499
113903
  } catch {
113500
113904
  return false;
113501
113905
  }
113502
113906
  }
113503
113907
  function isDir(path2) {
113504
113908
  try {
113505
- return statSync12(path2).isDirectory();
113909
+ return statSync13(path2).isDirectory();
113506
113910
  } catch {
113507
113911
  return false;
113508
113912
  }
@@ -113618,7 +114022,7 @@ async function publishedInputs(root, deps, refresh, out) {
113618
114022
  }
113619
114023
  if (file !== null) {
113620
114024
  try {
113621
- mkdirSync13(dirname28(file), { recursive: true });
114025
+ mkdirSync14(dirname28(file), { recursive: true });
113622
114026
  atomicWriteFileSync(
113623
114027
  file,
113624
114028
  JSON.stringify({