switchroom 0.15.3 → 0.15.4

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.
@@ -13654,7 +13654,8 @@ var init_schema = __esm(() => {
13654
13654
  stream_mode: exports_external.enum(["pty", "checklist"]).optional().describe("How live progress is streamed to Telegram during a turn. " + "'pty' (default) surfaces text snapshots of Claude Code's TUI \u2014 " + "compatible but can flicker as Ink re-renders. 'checklist' drives " + "a structured progress card from session-tail events \u2014 stable " + "order, per-tool status emojis, fires only on semantic transitions."),
13655
13655
  stream_throttle_ms: exports_external.number().int().nonnegative().optional().describe("Throttle window in ms between successive stream edits (or " + "sendMessageDraft tics) during a turn. Lower = more responsive " + "stream, higher = fewer API calls. Floored at 250 by draft-stream " + "itself. Default 300 for draft transport (DMs) and 1000 for " + "message transport (groups/forums). Override per-agent if a " + "particular agent needs snappier or quieter streaming."),
13656
13656
  clear_status_on_completion: exports_external.boolean().optional().describe("When true, the live activity/status feed (the in-place 'what it's " + "doing' message \u2014 Reading X, Searching the web for Y, \u2026) is DELETED " + "when the turn's final answer lands, so only the reply remains. " + "Default false: the status message is left in the chat as a record " + "(its last step marked done) \u2014 no post-then-delete. Per-agent " + "override; cascades defaults \u2192 profile \u2192 agent (per-key)."),
13657
- hotReloadStable: exports_external.boolean().optional().describe("If true, the stable workspace prefix (AGENTS.md, SOUL.md, USER.md, " + "IDENTITY.md, TOOLS.md, HEARTBEAT.md) is re-injected on every turn via " + "the UserPromptSubmit hook instead of baked into --append-system-prompt " + "at session start. Lets workspace edits propagate without a restart. " + "Costs ~5-10% per-turn latency/spend since the stable prefix is no " + "longer prompt-cached."),
13657
+ hotReloadStable: exports_external.boolean().optional().describe("If true, the stable workspace prefix (AGENTS.md, SOUL.md, USER.md, " + "IDENTITY.md, TOOLS.md) is re-injected on every turn via " + "the UserPromptSubmit hook instead of baked into --append-system-prompt " + "at session start. Lets workspace edits propagate without a restart. " + "Costs ~5-10% per-turn latency/spend since the stable prefix is no " + "longer prompt-cached."),
13658
+ inject_on_change: exports_external.boolean().optional().describe("Context-efficiency gate for per-turn hook injection (default true). " + "When true (the default), the turn-pacing directive and dynamic " + "workspace content are only re-emitted when their content changes or " + "the session_id changes \u2014 suppressing redundant injection that " + "otherwise triples compaction frequency. Set to false to revert to " + "the legacy always-emit behaviour (every turn injects the full " + "content regardless of whether it changed)."),
13658
13659
  orphan_promotion_ms: exports_external.number().int().nonnegative().optional().describe("How long (ms) a parent turn waits for a sub-agent JSONL watcher " + "to deliver sub_agent_started before the heartbeat promotes the spawn " + "to a synthesised 'running' row. Default 5000. Set to 0 to disable " + "orphan promotion entirely."),
13659
13660
  cold_sub_agent_threshold_ms: exports_external.number().int().nonnegative().optional().describe("JSONL-cold threshold (ms). When a running sub-agent emits no events " + "for this long, the heartbeat synthesises a turn_end for it so the " + "deferred-completion path can proceed. Default 30000. Set to 0 to " + "disable the synthetic close."),
13660
13661
  deferred_completion_timeout_ms: exports_external.number().int().nonnegative().optional().describe("Force-close timeout (ms) for deferred sub-agent completion. After " + "the parent turn_end arrives while sub-agents are still running, the " + "card is force-closed after this many ms even if sub-agents never " + "finish. Watcher-disconnect safety net. Default 180000 (3 min)."),
@@ -23264,6 +23265,14 @@ function normalizeBindMountPath(p) {
23264
23265
  out = out.slice(0, -1);
23265
23266
  return out;
23266
23267
  }
23268
+ function resolveConfigMountSource(switchroomConfigPath, homePrefix) {
23269
+ if (!switchroomConfigPath)
23270
+ return;
23271
+ if (switchroomConfigPath === CONTAINER_CONFIG_PATH || switchroomConfigPath.startsWith("/state/")) {
23272
+ return `${homePrefix}/.switchroom/switchroom.yaml`;
23273
+ }
23274
+ return switchroomConfigPath;
23275
+ }
23267
23276
  function resolveBindMount(agentName, entry) {
23268
23277
  const rawSource = entry.source;
23269
23278
  if (typeof rawSource !== "string" || rawSource.length === 0) {
@@ -23320,10 +23329,15 @@ function generateCompose(opts) {
23320
23329
  const buildMode = opts.buildMode ?? "pull";
23321
23330
  const buildContext = opts.buildContext;
23322
23331
  const homePrefix = opts.homeDir ?? "${HOME}";
23332
+ if (homePrefix === "/host-home" || homePrefix.startsWith("/host-home/")) {
23333
+ throw new Error(`compose: refusing to generate \u2014 the host-home prefix resolved to "${homePrefix}", ` + `which is the IN-CONTAINER mount point, not a host path. Emitting it as a bind-mount ` + `source would make docker create empty dirs on the host and crash the fleet (start.sh ` + `missing \u2192 exec 127; broker EISDIR).
23334
+
23335
+ ` + `Cause: \`apply\` ran inside a container without SWITCHROOM_HOST_HOME set to the real ` + `host home. Recovery: run \`switchroom apply\` once from the HOST shell (not via an ` + `agent / hostd), which regenerates the compose with correct host paths and re-bakes a ` + `correct SWITCHROOM_HOST_HOME into the fleet.`);
23336
+ }
23323
23337
  const containerNamePrefix = opts.containerNamePrefix ?? "switchroom";
23324
23338
  const hostControlEnabled = config.host_control?.enabled !== false;
23325
23339
  const hostHomeForChecks = opts.homeDir ?? process.env.HOME ?? "";
23326
- const switchroomConfigPath = opts.switchroomConfigPath;
23340
+ const switchroomConfigPath = resolveConfigMountSource(opts.switchroomConfigPath, homePrefix);
23327
23341
  const bundledSkillsPoolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
23328
23342
  let resolvedAnalyticsId = null;
23329
23343
  if (hostHomeForChecks !== "") {
@@ -23608,6 +23622,9 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
23608
23622
  SWITCHROOM_TIMEZONE: a.timezone,
23609
23623
  TZ: a.timezone
23610
23624
  };
23625
+ if (hostHomeForChecks) {
23626
+ env2.SWITCHROOM_HOST_HOME = hostHomeForChecks;
23627
+ }
23611
23628
  if (posthog.analyticsId != null) {
23612
23629
  env2.SWITCHROOM_ANALYTICS_ID = posthog.analyticsId;
23613
23630
  }
@@ -23717,7 +23734,7 @@ function emitAgentService(lines, a, imageTag, buildMode, buildContext, homePrefi
23717
23734
  lines.push(` - ${homePrefix}/.switchroom/logs/${a.name}:${homePrefix}/.switchroom/logs/${a.name}`);
23718
23735
  lines.push(``);
23719
23736
  }
23720
- var AGENT_UID_MIN = 10001, AGENT_UID_MAX = 10999, RESOURCE_BY_PROFILE, CRON_SESSION_MEM_BUMP_MIB = 512, CRON_SESSION_PIDS_BUMP = 128, BIND_MOUNT_SOURCE_DENYLIST, BIND_MOUNT_TARGET_DENYLIST, BIND_MOUNT_EXACT_SOURCE_DENY;
23737
+ var AGENT_UID_MIN = 10001, AGENT_UID_MAX = 10999, RESOURCE_BY_PROFILE, CRON_SESSION_MEM_BUMP_MIB = 512, CRON_SESSION_PIDS_BUMP = 128, BIND_MOUNT_SOURCE_DENYLIST, BIND_MOUNT_TARGET_DENYLIST, BIND_MOUNT_EXACT_SOURCE_DENY, CONTAINER_CONFIG_PATH = "/state/config/switchroom.yaml";
23721
23738
  var init_compose = __esm(() => {
23722
23739
  init_cron_routing();
23723
23740
  init_merge();
@@ -23969,16 +23986,16 @@ var init_singleton_reconcile = __esm(() => {
23969
23986
  // src/agents/lifecycle.ts
23970
23987
  import { execFileSync as execFileSync8, spawn, spawnSync } from "node:child_process";
23971
23988
  import { existsSync as existsSync17, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, renameSync as renameSync4, readFileSync as readFileSync15 } from "node:fs";
23972
- import { resolve as resolve13, join as join12 } from "node:path";
23989
+ import { resolve as resolve13, join as join13 } from "node:path";
23973
23990
  import { connect } from "node:net";
23974
23991
  function cleanShutdownMarkerPathForAgent(name) {
23975
23992
  const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? resolveStatePath("agents");
23976
- return join12(agentsDir, name, "telegram", "clean-shutdown.json");
23993
+ return join13(agentsDir, name, "telegram", "clean-shutdown.json");
23977
23994
  }
23978
23995
  function writeRestartReasonMarker(name, reason, opts = {}) {
23979
23996
  const path = cleanShutdownMarkerPathForAgent(name);
23980
23997
  try {
23981
- mkdirSync13(join12(path, ".."), { recursive: true });
23998
+ mkdirSync13(join13(path, ".."), { recursive: true });
23982
23999
  if (opts.preserveExisting && existsSync17(path)) {
23983
24000
  try {
23984
24001
  const prev = JSON.parse(readFileSync15(path, "utf-8"));
@@ -24082,7 +24099,7 @@ function gracefulRestartAgent(name) {
24082
24099
  return new Promise((resolvePromise, reject) => {
24083
24100
  const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? resolveStatePath("agents");
24084
24101
  const agentDir = resolve13(agentsDir, name);
24085
- const socketPath = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join12(agentDir, "telegram", "gateway.sock");
24102
+ const socketPath = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join13(agentDir, "telegram", "gateway.sock");
24086
24103
  if (!existsSync17(socketPath)) {
24087
24104
  reject(new Error("Gateway socket not found. Is the gateway running?"));
24088
24105
  return;
@@ -24478,30 +24495,30 @@ import {
24478
24495
  writeFileSync as writeFileSync9
24479
24496
  } from "node:fs";
24480
24497
  import { randomBytes as randomBytes2 } from "node:crypto";
24481
- import { join as join15 } from "node:path";
24498
+ import { join as join16 } from "node:path";
24482
24499
  function claudeDir(agentDir) {
24483
- return join15(agentDir, ".claude");
24500
+ return join16(agentDir, ".claude");
24484
24501
  }
24485
24502
  function accountsDir(agentDir) {
24486
- return join15(claudeDir(agentDir), "accounts");
24503
+ return join16(claudeDir(agentDir), "accounts");
24487
24504
  }
24488
24505
  function slotDir(agentDir, slot) {
24489
- return join15(accountsDir(agentDir), slot);
24506
+ return join16(accountsDir(agentDir), slot);
24490
24507
  }
24491
24508
  function slotTokenPath(agentDir, slot) {
24492
- return join15(slotDir(agentDir, slot), ".oauth-token");
24509
+ return join16(slotDir(agentDir, slot), ".oauth-token");
24493
24510
  }
24494
24511
  function slotMetaPath(agentDir, slot) {
24495
- return join15(slotDir(agentDir, slot), ".oauth-token.meta.json");
24512
+ return join16(slotDir(agentDir, slot), ".oauth-token.meta.json");
24496
24513
  }
24497
24514
  function activeMarkerPath(agentDir) {
24498
- return join15(claudeDir(agentDir), "active");
24515
+ return join16(claudeDir(agentDir), "active");
24499
24516
  }
24500
24517
  function legacyTokenPath(agentDir) {
24501
- return join15(claudeDir(agentDir), ".oauth-token");
24518
+ return join16(claudeDir(agentDir), ".oauth-token");
24502
24519
  }
24503
24520
  function legacyMetaPath(agentDir) {
24504
- return join15(claudeDir(agentDir), ".oauth-token.meta.json");
24521
+ return join16(claudeDir(agentDir), ".oauth-token.meta.json");
24505
24522
  }
24506
24523
  function validateSlotName(slot) {
24507
24524
  if (typeof slot !== "string" || slot.length === 0) {
@@ -24544,7 +24561,7 @@ function listSlots(agentDir) {
24544
24561
  try {
24545
24562
  return readdirSync10(dir).filter((name) => {
24546
24563
  try {
24547
- return statSync12(join15(dir, name)).isDirectory();
24564
+ return statSync12(join16(dir, name)).isDirectory();
24548
24565
  } catch {
24549
24566
  return false;
24550
24567
  }
@@ -24723,7 +24740,7 @@ import {
24723
24740
  chmodSync as chmodSync3,
24724
24741
  statSync as statSync13
24725
24742
  } from "node:fs";
24726
- import { join as join16, resolve as resolve15 } from "node:path";
24743
+ import { join as join17, resolve as resolve15 } from "node:path";
24727
24744
  function extractCodeChallenge(url) {
24728
24745
  const match = url.match(/[?&]code_challenge=([A-Za-z0-9_-]+)/);
24729
24746
  return match ? match[1] : null;
@@ -24738,22 +24755,22 @@ function stripAnsi(text) {
24738
24755
  return text.replace(/\x1B\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/\x1B[@-_]/g, "").replace(/\r/g, "");
24739
24756
  }
24740
24757
  function claudeDir2(agentDir) {
24741
- return join16(agentDir, ".claude");
24758
+ return join17(agentDir, ".claude");
24742
24759
  }
24743
24760
  function credentialsPath(agentDir) {
24744
- return join16(claudeDir2(agentDir), ".credentials.json");
24761
+ return join17(claudeDir2(agentDir), ".credentials.json");
24745
24762
  }
24746
24763
  function oauthTokenPath(agentDir) {
24747
- return join16(claudeDir2(agentDir), ".oauth-token");
24764
+ return join17(claudeDir2(agentDir), ".oauth-token");
24748
24765
  }
24749
24766
  function oauthTokenMetaPath(agentDir) {
24750
- return join16(claudeDir2(agentDir), ".oauth-token.meta.json");
24767
+ return join17(claudeDir2(agentDir), ".oauth-token.meta.json");
24751
24768
  }
24752
24769
  function authLogPath(agentDir) {
24753
- return join16(claudeDir2(agentDir), ".setup-token.log");
24770
+ return join17(claudeDir2(agentDir), ".setup-token.log");
24754
24771
  }
24755
24772
  function authSessionMetaPath(agentDir) {
24756
- return join16(claudeDir2(agentDir), ".setup-token.session.json");
24773
+ return join17(claudeDir2(agentDir), ".setup-token.session.json");
24757
24774
  }
24758
24775
  function authSessionName(name, slot) {
24759
24776
  const base = `switchroom-auth-${name.replace(/[^a-zA-Z0-9_.-]/g, "-")}`;
@@ -24873,7 +24890,7 @@ function cleanupAuthTempDirs(agentDir) {
24873
24890
  try {
24874
24891
  for (const entry of readdirSync11(dir)) {
24875
24892
  if (entry.startsWith(".setup-token-tmp-")) {
24876
- rmSync5(join16(dir, entry), { recursive: true, force: true });
24893
+ rmSync5(join17(dir, entry), { recursive: true, force: true });
24877
24894
  }
24878
24895
  }
24879
24896
  } catch {}
@@ -25034,14 +25051,14 @@ function startAuthSession(name, agentDir, opts = {}) {
25034
25051
  rmSync5(logPath, { force: true });
25035
25052
  let configDir;
25036
25053
  if (opts.force) {
25037
- configDir = mkdtempSync2(join16(claudeDir2(agentDir), ".setup-token-tmp-"));
25054
+ configDir = mkdtempSync2(join17(claudeDir2(agentDir), ".setup-token-tmp-"));
25038
25055
  try {
25039
25056
  chmodSync3(configDir, 448);
25040
25057
  } catch {}
25041
25058
  } else {
25042
25059
  configDir = claudeDir2(agentDir);
25043
25060
  }
25044
- const credentialsMtimeAtStart = fileMtimeMs(join16(configDir, ".credentials.json"));
25061
+ const credentialsMtimeAtStart = fileMtimeMs(join17(configDir, ".credentials.json"));
25045
25062
  const commandParts = [];
25046
25063
  commandParts.push(`CLAUDE_CONFIG_DIR=${shellQuote(configDir)}`);
25047
25064
  commandParts.push(`LOG_PATH=${shellQuote(logPath)}`);
@@ -25129,7 +25146,7 @@ function submitAuthCode(name, agentDir, code, slot, _opts = {}) {
25129
25146
  tmux(["send-keys", "-l", "-t", sessionName, code.trim()]);
25130
25147
  tmux(["send-keys", "-t", sessionName, "Enter"]);
25131
25148
  const meta = readJsonFile(authSessionMetaPath(agentDir));
25132
- const credFileToWatch = meta?.configDir ? join16(meta.configDir, ".credentials.json") : credentialsPath(agentDir);
25149
+ const credFileToWatch = meta?.configDir ? join17(meta.configDir, ".credentials.json") : credentialsPath(agentDir);
25133
25150
  const credsMtimeSnapshot = meta?.credentialsMtimeAtStart ?? 0;
25134
25151
  const logPath = authLogPath(agentDir);
25135
25152
  let token = null;
@@ -25235,9 +25252,9 @@ import {
25235
25252
  unlinkSync as unlinkSync6,
25236
25253
  writeFileSync as writeFileSync12
25237
25254
  } from "node:fs";
25238
- import { join as join18 } from "node:path";
25255
+ import { join as join19 } from "node:path";
25239
25256
  function quarantineMarkerPath(telegramStateDir) {
25240
- return join18(telegramStateDir, QUARANTINE_FILENAME);
25257
+ return join19(telegramStateDir, QUARANTINE_FILENAME);
25241
25258
  }
25242
25259
  function readQuarantineMarker(telegramStateDir) {
25243
25260
  const path = quarantineMarkerPath(telegramStateDir);
@@ -25277,7 +25294,7 @@ function clearQuarantineMarker(telegramStateDir) {
25277
25294
  }
25278
25295
  }
25279
25296
  function hostTelegramStateDir(agentsDir, name) {
25280
- return join18(agentsDir, name, "telegram");
25297
+ return join19(agentsDir, name, "telegram");
25281
25298
  }
25282
25299
  function readQuarantineMarkerForAgent(agentsDir, name) {
25283
25300
  return readQuarantineMarker(hostTelegramStateDir(agentsDir, name));
@@ -25855,7 +25872,7 @@ import * as net2 from "node:net";
25855
25872
  import { existsSync as existsSync29 } from "node:fs";
25856
25873
  import { homedir as homedir9 } from "node:os";
25857
25874
  import { randomUUID } from "node:crypto";
25858
- import { join as join20 } from "node:path";
25875
+ import { join as join21 } from "node:path";
25859
25876
  function reviveDate(v) {
25860
25877
  if (v == null)
25861
25878
  return null;
@@ -25865,7 +25882,7 @@ function reviveDate(v) {
25865
25882
  return Number.isNaN(d.getTime()) ? null : d;
25866
25883
  }
25867
25884
  function operatorSocketPath(home2 = homedir9()) {
25868
- return join20(home2, ".switchroom", "state", "auth-broker-operator", "sock");
25885
+ return join21(home2, ".switchroom", "state", "auth-broker-operator", "sock");
25869
25886
  }
25870
25887
  function resolveAuthBrokerSocketPath(opts) {
25871
25888
  if (opts?.socket)
@@ -26223,7 +26240,7 @@ import {
26223
26240
  writeFileSync as writeFileSync14
26224
26241
  } from "node:fs";
26225
26242
  import { homedir as homedir11 } from "node:os";
26226
- import { join as join22, resolve as resolve22 } from "node:path";
26243
+ import { join as join23, resolve as resolve22 } from "node:path";
26227
26244
  function accountsRootOverride() {
26228
26245
  const v = process.env.SWITCHROOM_ACCOUNTS_DIR;
26229
26246
  if (v && v.length > 0 && v.startsWith("/"))
@@ -26234,13 +26251,13 @@ function accountsRoot(home2 = homedir11()) {
26234
26251
  return accountsRootOverride() ?? resolve22(home2, ".switchroom", "accounts");
26235
26252
  }
26236
26253
  function accountDir(label, home2 = homedir11()) {
26237
- return join22(accountsRoot(home2), label);
26254
+ return join23(accountsRoot(home2), label);
26238
26255
  }
26239
26256
  function accountCredentialsPath(label, home2 = homedir11()) {
26240
- return join22(accountDir(label, home2), "credentials.json");
26257
+ return join23(accountDir(label, home2), "credentials.json");
26241
26258
  }
26242
26259
  function accountMetaPath(label, home2 = homedir11()) {
26243
- return join22(accountDir(label, home2), "meta.json");
26260
+ return join23(accountDir(label, home2), "meta.json");
26244
26261
  }
26245
26262
  function listAccounts(home2 = homedir11()) {
26246
26263
  const root = accountsRoot(home2);
@@ -26249,7 +26266,7 @@ function listAccounts(home2 = homedir11()) {
26249
26266
  try {
26250
26267
  return readdirSync15(root).filter((name) => {
26251
26268
  try {
26252
- return statSync16(join22(root, name)).isDirectory();
26269
+ return statSync16(join23(root, name)).isDirectory();
26253
26270
  } catch {
26254
26271
  return false;
26255
26272
  }
@@ -26965,9 +26982,9 @@ var init_disconnect = __esm(() => {
26965
26982
  // src/vault/approvals/client.ts
26966
26983
  import { existsSync as existsSync31 } from "node:fs";
26967
26984
  import { homedir as homedir12 } from "node:os";
26968
- import { join as join23 } from "node:path";
26985
+ import { join as join24 } from "node:path";
26969
26986
  function kernelOperatorSocketPath(home2 = homedir12()) {
26970
- return join23(home2, ".switchroom", "state", "kernel-operator", "sock");
26987
+ return join24(home2, ".switchroom", "state", "kernel-operator", "sock");
26971
26988
  }
26972
26989
  function resolveKernelOperatorSocket(home2 = homedir12()) {
26973
26990
  const p = kernelOperatorSocketPath(home2);
@@ -28051,7 +28068,7 @@ __export(exports_via_claude, {
28051
28068
  });
28052
28069
  import { execFileSync as execFileSync14, spawnSync as spawnSync2 } from "node:child_process";
28053
28070
  import { existsSync as existsSync32, mkdirSync as mkdirSync19, readFileSync as readFileSync27 } from "node:fs";
28054
- import { join as join24, resolve as resolve23 } from "node:path";
28071
+ import { join as join25, resolve as resolve23 } from "node:path";
28055
28072
  function tmuxHasSession(session) {
28056
28073
  const r = spawnSync2("tmux", ["has-session", "-t", session], {
28057
28074
  stdio: ["ignore", "ignore", "ignore"]
@@ -28097,7 +28114,7 @@ async function runViaClaude(opts) {
28097
28114
  const poll = opts.pollMs ?? VIA_CLAUDE_DEFAULTS.pollMs;
28098
28115
  const configDir = resolve23(opts.configDir);
28099
28116
  mkdirSync19(configDir, { recursive: true });
28100
- const credentialsPath2 = join24(configDir, ".credentials.json");
28117
+ const credentialsPath2 = join25(configDir, ".credentials.json");
28101
28118
  const capture = opts.capturePane ?? (() => tmuxCapturePane(SESSION));
28102
28119
  const send = opts.sendKeys ?? ((keys, literal) => tmuxSendKeys(SESSION, keys, literal === true));
28103
28120
  if (!opts.spawnClaude) {
@@ -28852,9 +28869,9 @@ var DOCUMENTS_PAGE_LIMIT = 500;
28852
28869
 
28853
28870
  // src/host-control/audit-reader.ts
28854
28871
  import { homedir as homedir21 } from "node:os";
28855
- import { join as join39 } from "node:path";
28872
+ import { join as join40 } from "node:path";
28856
28873
  function defaultAuditLogPath2(home2 = homedir21()) {
28857
- return join39(home2, ".switchroom", "host-control-audit.log");
28874
+ return join40(home2, ".switchroom", "host-control-audit.log");
28858
28875
  }
28859
28876
  function parseAuditLine2(line) {
28860
28877
  const trimmed = line.trim();
@@ -29117,12 +29134,12 @@ import {
29117
29134
  readFileSync as readFileSync47,
29118
29135
  readdirSync as readdirSync19
29119
29136
  } from "node:fs";
29120
- import { dirname as dirname12, join as join46 } from "node:path";
29137
+ import { dirname as dirname12, join as join47 } from "node:path";
29121
29138
  import { execSync as execSync2 } from "node:child_process";
29122
29139
  function locateManifestPath() {
29123
29140
  let dir = import.meta.dirname;
29124
29141
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
29125
- const candidate = join46(dir, "dependencies.json");
29142
+ const candidate = join47(dir, "dependencies.json");
29126
29143
  if (existsSync52(candidate))
29127
29144
  return candidate;
29128
29145
  dir = dirname12(dir);
@@ -29192,13 +29209,13 @@ function probeClaudeVersion() {
29192
29209
  }
29193
29210
  function probePlaywrightMcpVersion() {
29194
29211
  const home2 = process.env.HOME ?? "";
29195
- const npxCache = join46(home2, ".npm/_npx");
29212
+ const npxCache = join47(home2, ".npm/_npx");
29196
29213
  if (!existsSync52(npxCache))
29197
29214
  return null;
29198
29215
  try {
29199
29216
  const entries = readdirSync19(npxCache);
29200
29217
  for (const entry of entries) {
29201
- const pkgPath = join46(npxCache, entry, "node_modules/@playwright/mcp/package.json");
29218
+ const pkgPath = join47(npxCache, entry, "node_modules/@playwright/mcp/package.json");
29202
29219
  if (existsSync52(pkgPath)) {
29203
29220
  try {
29204
29221
  const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
@@ -29644,7 +29661,7 @@ import { existsSync as existsSync53, readFileSync as readFileSync49 } from "node
29644
29661
  import { createHash as createHash10 } from "node:crypto";
29645
29662
  import { spawnSync as spawnSync6 } from "node:child_process";
29646
29663
  import { homedir as homedir26 } from "node:os";
29647
- import { join as join47 } from "node:path";
29664
+ import { join as join48 } from "node:path";
29648
29665
  function defaultDockerInspect(container, format) {
29649
29666
  try {
29650
29667
  const r = spawnSync6("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
@@ -29744,7 +29761,7 @@ function checkAuthBrokerPerAgentSockets(config, deps = {}) {
29744
29761
  }
29745
29762
  function checkAuthBrokerDrift(deps = {}) {
29746
29763
  const stateDir = resolveStateDir(deps);
29747
- const indexPath = join47(stateDir, "sha-index.json");
29764
+ const indexPath = join48(stateDir, "sha-index.json");
29748
29765
  if (!existsSync53(indexPath)) {
29749
29766
  return {
29750
29767
  name: "auth-broker: drift",
@@ -29805,7 +29822,7 @@ function checkAuthBrokerDrift(deps = {}) {
29805
29822
  }
29806
29823
  function checkAuthBrokerThresholdViolations(deps = {}) {
29807
29824
  const stateDir = resolveStateDir(deps);
29808
- const path4 = join47(stateDir, "threshold-violations.json");
29825
+ const path4 = join48(stateDir, "threshold-violations.json");
29809
29826
  if (!existsSync53(path4)) {
29810
29827
  return {
29811
29828
  name: "auth-broker: threshold violations",
@@ -30002,7 +30019,7 @@ import {
30002
30019
  statSync as statSync23
30003
30020
  } from "node:fs";
30004
30021
  import { userInfo, homedir as homedir27 } from "node:os";
30005
- import { join as join48 } from "node:path";
30022
+ import { join as join49 } from "node:path";
30006
30023
  function resolveVaultPath2(config) {
30007
30024
  return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
30008
30025
  }
@@ -30140,7 +30157,7 @@ async function runSecretAccessChecks(config, deps = {}) {
30140
30157
  };
30141
30158
  const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
30142
30159
  if (!passphrase) {
30143
- const sock = deps.brokerOperatorSocket ?? join48(homedir27(), ".switchroom", "broker-operator", "sock");
30160
+ const sock = deps.brokerOperatorSocket ?? join49(homedir27(), ".switchroom", "broker-operator", "sock");
30144
30161
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
30145
30162
  for (const name of Object.keys(config.agents ?? {})) {
30146
30163
  const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
@@ -30225,7 +30242,7 @@ import {
30225
30242
  existsSync as realExistsSync,
30226
30243
  readFileSync as realReadFileSync
30227
30244
  } from "node:fs";
30228
- import { join as join49, resolve as resolve30 } from "node:path";
30245
+ import { join as join50, resolve as resolve30 } from "node:path";
30229
30246
  import { homedir as homedir28 } from "node:os";
30230
30247
  function resolveDeps(config, deps) {
30231
30248
  let agentsDir = deps.agentsDir;
@@ -30349,8 +30366,8 @@ function checkScaffoldWiring(config, driveAgents, d) {
30349
30366
  });
30350
30367
  continue;
30351
30368
  }
30352
- const mcpPath = join49(agentDir, ".mcp.json");
30353
- const claudeJsonPath = join49(agentDir, ".claude", ".claude.json");
30369
+ const mcpPath = join50(agentDir, ".mcp.json");
30370
+ const claudeJsonPath = join50(agentDir, ".claude", ".claude.json");
30354
30371
  const mcpRead = readJson(d, mcpPath);
30355
30372
  const trustRead = readJson(d, claudeJsonPath);
30356
30373
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -30463,7 +30480,7 @@ async function runDriveBrokerReachabilityChecks(config, deps = {}) {
30463
30480
  }
30464
30481
  ];
30465
30482
  }
30466
- const sock = deps.brokerOperatorSocket ?? join49(homedir28(), ".switchroom", "broker-operator", "sock");
30483
+ const sock = deps.brokerOperatorSocket ?? join50(homedir28(), ".switchroom", "broker-operator", "sock");
30467
30484
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
30468
30485
  const results = [];
30469
30486
  for (const agent of driveAgents) {
@@ -30517,7 +30534,7 @@ import {
30517
30534
  readSync as realReadSync,
30518
30535
  closeSync as realCloseSync
30519
30536
  } from "node:fs";
30520
- import { join as join50 } from "node:path";
30537
+ import { join as join51 } from "node:path";
30521
30538
  import { homedir as homedir29 } from "node:os";
30522
30539
  function defaultReadHead(p, n) {
30523
30540
  let fd;
@@ -30575,7 +30592,7 @@ function runWebkiteChecks(config, deps = {}) {
30575
30592
  return [];
30576
30593
  const d = resolveDeps2(config, deps);
30577
30594
  const results = [];
30578
- const binPath = join50(d.homeDir, ".switchroom", "bin", "webkite");
30595
+ const binPath = join51(d.homeDir, ".switchroom", "bin", "webkite");
30579
30596
  if (!d.existsSync(binPath)) {
30580
30597
  results.push({
30581
30598
  name: "webkite: binary",
@@ -30605,12 +30622,12 @@ function runWebkiteChecks(config, deps = {}) {
30605
30622
  });
30606
30623
  }
30607
30624
  }
30608
- const cloakDir = join50(d.homeDir, ".cloakbrowser");
30625
+ const cloakDir = join51(d.homeDir, ".cloakbrowser");
30609
30626
  let chromeFound = false;
30610
30627
  if (d.existsSync(cloakDir)) {
30611
30628
  try {
30612
30629
  for (const entry of d.readdirSync(cloakDir)) {
30613
- if (entry.startsWith("chromium-") && d.existsSync(join50(cloakDir, entry, "chrome"))) {
30630
+ if (entry.startsWith("chromium-") && d.existsSync(join51(cloakDir, entry, "chrome"))) {
30614
30631
  chromeFound = true;
30615
30632
  break;
30616
30633
  }
@@ -30636,9 +30653,9 @@ function runWebkiteChecks(config, deps = {}) {
30636
30653
  return results;
30637
30654
  }
30638
30655
  for (const agent of enabledAgents) {
30639
- const agentDir = join50(d.agentsDir, agent);
30640
- const settingsPath = join50(agentDir, ".claude", "settings.json");
30641
- const mcpPath = join50(agentDir, ".mcp.json");
30656
+ const agentDir = join51(d.agentsDir, agent);
30657
+ const settingsPath = join51(agentDir, ".claude", "settings.json");
30658
+ const mcpPath = join51(agentDir, ".mcp.json");
30642
30659
  if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
30643
30660
  continue;
30644
30661
  }
@@ -30702,7 +30719,7 @@ var init_doctor_webkite = __esm(() => {
30702
30719
  });
30703
30720
 
30704
30721
  // src/cli/doctor-scaffold-wiring.ts
30705
- import { join as join51, resolve as resolve31 } from "node:path";
30722
+ import { join as join52, resolve as resolve31 } from "node:path";
30706
30723
  function readJson2(d, path4) {
30707
30724
  if (!d.existsSync(path4))
30708
30725
  return { kind: "absent" };
@@ -30745,8 +30762,8 @@ function checkIntegrationScaffoldWiring(args) {
30745
30762
  });
30746
30763
  continue;
30747
30764
  }
30748
- const mcpPath = join51(agentDir, ".mcp.json");
30749
- const claudeJsonPath = join51(agentDir, ".claude", ".claude.json");
30765
+ const mcpPath = join52(agentDir, ".mcp.json");
30766
+ const claudeJsonPath = join52(agentDir, ".claude", ".claude.json");
30750
30767
  const mcpRead = readJson2(deps, mcpPath);
30751
30768
  const trustRead = readJson2(deps, claudeJsonPath);
30752
30769
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -30810,14 +30827,14 @@ import {
30810
30827
  existsSync as realExistsSync3,
30811
30828
  readFileSync as realReadFileSync3
30812
30829
  } from "node:fs";
30813
- import { join as join52 } from "node:path";
30830
+ import { join as join53 } from "node:path";
30814
30831
  import { homedir as homedir30 } from "node:os";
30815
30832
  function resolveDeps3(deps) {
30816
30833
  const home2 = deps.homeDir?.() ?? homedir30();
30817
30834
  return {
30818
30835
  existsSync: deps.existsSync ?? realExistsSync3,
30819
30836
  readFileSync: deps.readFileSync ?? realReadFileSync3,
30820
- agentsDir: join52(home2, ".switchroom", "agents"),
30837
+ agentsDir: join53(home2, ".switchroom", "agents"),
30821
30838
  now: deps.now ?? Date.now
30822
30839
  };
30823
30840
  }
@@ -30900,7 +30917,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
30900
30917
  ];
30901
30918
  }
30902
30919
  function readHeartbeat(d, agentName) {
30903
- const path4 = join52(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
30920
+ const path4 = join53(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
30904
30921
  if (!d.existsSync(path4)) {
30905
30922
  return { error: "heartbeat file missing \u2014 launcher has not yet started" };
30906
30923
  }
@@ -30997,7 +31014,7 @@ import {
30997
31014
  readFileSync as realReadFileSync4,
30998
31015
  statSync as realStatSync
30999
31016
  } from "node:fs";
31000
- import { join as join53 } from "node:path";
31017
+ import { join as join54 } from "node:path";
31001
31018
  import { homedir as homedir31 } from "node:os";
31002
31019
  function resolveDeps4(deps) {
31003
31020
  const home2 = deps.homeDir?.() ?? homedir31();
@@ -31005,7 +31022,7 @@ function resolveDeps4(deps) {
31005
31022
  existsSync: deps.existsSync ?? realExistsSync4,
31006
31023
  readFileSync: deps.readFileSync ?? realReadFileSync4,
31007
31024
  statSync: deps.statSync ?? realStatSync,
31008
- agentsDir: join53(home2, ".switchroom", "agents"),
31025
+ agentsDir: join54(home2, ".switchroom", "agents"),
31009
31026
  now: deps.now ?? Date.now,
31010
31027
  vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
31011
31028
  };
@@ -31130,7 +31147,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
31130
31147
  return [];
31131
31148
  const results = [];
31132
31149
  for (const name of notionAgents) {
31133
- const heartbeatPath = join53(d.agentsDir, name, "notion-launcher.heartbeat.json");
31150
+ const heartbeatPath = join54(d.agentsDir, name, "notion-launcher.heartbeat.json");
31134
31151
  if (!d.existsSync(heartbeatPath)) {
31135
31152
  results.push({
31136
31153
  name: `notion:launcher-heartbeat:${name}`,
@@ -31206,9 +31223,9 @@ import {
31206
31223
  statSync as realStatSync2
31207
31224
  } from "node:fs";
31208
31225
  import { homedir as homedir32 } from "node:os";
31209
- import { join as join54 } from "node:path";
31226
+ import { join as join55 } from "node:path";
31210
31227
  function runCredentialsMigrationChecks(config, deps = {}) {
31211
- const credDir = deps.credentialsDir ?? join54(homedir32(), ".switchroom", "credentials");
31228
+ const credDir = deps.credentialsDir ?? join55(homedir32(), ".switchroom", "credentials");
31212
31229
  const existsSync55 = deps.existsSync ?? ((p) => realExistsSync5(p));
31213
31230
  const readdirSync21 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
31214
31231
  const isDirectory = deps.isDirectory ?? ((p) => {
@@ -31236,7 +31253,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
31236
31253
  const flat = [];
31237
31254
  const perAgentDirs = [];
31238
31255
  for (const e of entries) {
31239
- const full = join54(credDir, e);
31256
+ const full = join55(credDir, e);
31240
31257
  if (isDirectory(full) && agentNames.has(e)) {
31241
31258
  perAgentDirs.push(e);
31242
31259
  } else {
@@ -31360,13 +31377,13 @@ var init_doctor_inlined_secrets = __esm(() => {
31360
31377
  // src/cli/doctor-audit-integrity.ts
31361
31378
  import { readFileSync as fsReadFileSync2 } from "node:fs";
31362
31379
  import { homedir as homedir33 } from "node:os";
31363
- import { join as join55 } from "node:path";
31380
+ import { join as join56 } from "node:path";
31364
31381
  function rootWrittenLogs(home2) {
31365
31382
  return [
31366
- { label: "vault-broker", path: join55(home2, ".switchroom", "vault-audit.log") },
31383
+ { label: "vault-broker", path: join56(home2, ".switchroom", "vault-audit.log") },
31367
31384
  {
31368
31385
  label: "hostd",
31369
- path: join55(home2, ".switchroom", "host-control-audit.log")
31386
+ path: join56(home2, ".switchroom", "host-control-audit.log")
31370
31387
  }
31371
31388
  ];
31372
31389
  }
@@ -31430,13 +31447,13 @@ var init_doctor_audit_integrity = __esm(() => {
31430
31447
  // src/cli/doctor-agent-smoke.ts
31431
31448
  import { existsSync as existsSync55 } from "node:fs";
31432
31449
  import { homedir as homedir34 } from "node:os";
31433
- import { join as join56 } from "node:path";
31450
+ import { join as join57 } from "node:path";
31434
31451
  import { randomUUID as randomUUID5 } from "node:crypto";
31435
31452
  async function runAgentSmokeChecks(config, deps = {}) {
31436
31453
  if (deps.fast)
31437
31454
  return [];
31438
31455
  const home2 = deps.homeDir ?? homedir34();
31439
- const sock = deps.operatorSockPath ?? join56(home2, ".switchroom", "hostd", "operator", "sock");
31456
+ const sock = deps.operatorSockPath ?? join57(home2, ".switchroom", "hostd", "operator", "sock");
31440
31457
  if (!deps.hostdRequestImpl && !existsSync55(sock)) {
31441
31458
  return [
31442
31459
  {
@@ -31517,7 +31534,7 @@ var init_doctor_agent_smoke = __esm(() => {
31517
31534
  import { execFileSync as execFileSync18 } from "node:child_process";
31518
31535
  import { existsSync as existsSync56, statSync as statSync24 } from "node:fs";
31519
31536
  import { homedir as homedir35 } from "node:os";
31520
- import { join as join57 } from "node:path";
31537
+ import { join as join58 } from "node:path";
31521
31538
  function probeBindMountInode(hostPath, brokerContainerPath, opts) {
31522
31539
  const statHost = opts?.statHost ?? defaultStatHost;
31523
31540
  const statBroker = opts?.statBroker ?? defaultStatBroker;
@@ -31666,16 +31683,16 @@ function runVaultBrokerDurabilityChecks(_config, opts) {
31666
31683
  probeBrokerUnlocked(opts?.statusProbe),
31667
31684
  probeAutoUnlockBlob(home2),
31668
31685
  probeMachineIdMount(),
31669
- formatBindMountResult("vault-broker: vault.enc bind mount", join57(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join57(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
31670
- formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join57(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join57(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
31671
- formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join57(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join57(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
31686
+ formatBindMountResult("vault-broker: vault.enc bind mount", join58(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join58(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
31687
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join58(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join58(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
31688
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join58(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join58(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
31672
31689
  probeKernelDbDurability(home2, {
31673
31690
  statBroker: opts?.kernelStatBroker
31674
31691
  })
31675
31692
  ];
31676
31693
  }
31677
31694
  function probeKernelDbDurability(home2, opts) {
31678
- const hostDir = join57(home2, ".switchroom", "approvals");
31695
+ const hostDir = join58(home2, ".switchroom", "approvals");
31679
31696
  const containerDir = "/state/approvals";
31680
31697
  const name = "approval-kernel: approvals bind mount (allow_always durability)";
31681
31698
  const kernelStat = opts?.statBroker ?? defaultKernelStatBroker;
@@ -31743,7 +31760,7 @@ function defaultKernelStatBroker(p) {
31743
31760
  return { kind: "ok-with-stat", ino: inoStr, size };
31744
31761
  }
31745
31762
  function probeAutoUnlockBlob(home2) {
31746
- const blobPath = join57(home2, ".switchroom", "vault-auto-unlock");
31763
+ const blobPath = join58(home2, ".switchroom", "vault-auto-unlock");
31747
31764
  if (!existsSync56(blobPath)) {
31748
31765
  return {
31749
31766
  name: "vault-broker: auto-unlock blob",
@@ -31855,16 +31872,16 @@ import {
31855
31872
  readdirSync as readdirSync21,
31856
31873
  statSync as statSync25
31857
31874
  } from "node:fs";
31858
- import { dirname as dirname13, join as join58, resolve as resolve32 } from "node:path";
31875
+ import { dirname as dirname13, join as join59, resolve as resolve32 } from "node:path";
31859
31876
  import { createPublicKey, createPrivateKey } from "node:crypto";
31860
31877
  function findInNvm(bin) {
31861
- const nvmRoot = join58(process.env.HOME ?? "", ".nvm", "versions", "node");
31878
+ const nvmRoot = join59(process.env.HOME ?? "", ".nvm", "versions", "node");
31862
31879
  if (!existsSync57(nvmRoot))
31863
31880
  return null;
31864
31881
  try {
31865
31882
  const versions = readdirSync21(nvmRoot).sort().reverse();
31866
31883
  for (const v of versions) {
31867
- const candidate = join58(nvmRoot, v, "bin", bin);
31884
+ const candidate = join59(nvmRoot, v, "bin", bin);
31868
31885
  try {
31869
31886
  const s = statSync25(candidate);
31870
31887
  if (s.isFile() || s.isSymbolicLink()) {
@@ -32029,7 +32046,7 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
32029
32046
  if (envBrowsersPath && envBrowsersPath.length > 0) {
32030
32047
  cacheLocations.push(envBrowsersPath);
32031
32048
  }
32032
- cacheLocations.push(join58(homeDir, ".cache", "ms-playwright"));
32049
+ cacheLocations.push(join59(homeDir, ".cache", "ms-playwright"));
32033
32050
  for (const cacheDir of cacheLocations) {
32034
32051
  if (!existsSync57(cacheDir))
32035
32052
  continue;
@@ -32037,10 +32054,10 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
32037
32054
  const entries = readdirSync21(cacheDir).filter((e) => e.startsWith("chromium"));
32038
32055
  for (const entry of entries) {
32039
32056
  const candidates2 = [
32040
- join58(cacheDir, entry, "chrome-linux64", "chrome"),
32041
- join58(cacheDir, entry, "chrome-linux", "chrome"),
32042
- join58(cacheDir, entry, "chrome-linux64", "headless_shell"),
32043
- join58(cacheDir, entry, "chrome-linux", "headless_shell")
32057
+ join59(cacheDir, entry, "chrome-linux64", "chrome"),
32058
+ join59(cacheDir, entry, "chrome-linux", "chrome"),
32059
+ join59(cacheDir, entry, "chrome-linux64", "headless_shell"),
32060
+ join59(cacheDir, entry, "chrome-linux", "headless_shell")
32044
32061
  ];
32045
32062
  for (const path4 of candidates2) {
32046
32063
  if (existsSync57(path4))
@@ -32163,7 +32180,7 @@ function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
32163
32180
  function checkLegacyState() {
32164
32181
  const results = [];
32165
32182
  const h = process.env.HOME ?? "/root";
32166
- const clerkDir = join58(h, LEGACY_STATE_DIR);
32183
+ const clerkDir = join59(h, LEGACY_STATE_DIR);
32167
32184
  const clerkPresent = existsSync57(clerkDir);
32168
32185
  results.push({
32169
32186
  name: "legacy ~/.clerk state",
@@ -32173,7 +32190,7 @@ function checkLegacyState() {
32173
32190
  fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
32174
32191
  } : {}
32175
32192
  });
32176
- const legacySock = join58(h, ".switchroom", "vault-broker.sock");
32193
+ const legacySock = join59(h, ".switchroom", "vault-broker.sock");
32177
32194
  let sockStat = null;
32178
32195
  try {
32179
32196
  sockStat = lstatSync6(legacySock);
@@ -32551,7 +32568,7 @@ async function checkHindsight(config) {
32551
32568
  }
32552
32569
  function checkPendingRetainsQueue(dir) {
32553
32570
  const home2 = process.env.HOME ?? "";
32554
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join58(home2, ".hindsight", "pending-retains");
32571
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join59(home2, ".hindsight", "pending-retains");
32555
32572
  if (!existsSync57(pendingDir)) {
32556
32573
  return {
32557
32574
  name: "pending-retains queue",
@@ -32682,7 +32699,7 @@ async function checkTelegram(config) {
32682
32699
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
32683
32700
  if (plugin !== "switchroom")
32684
32701
  continue;
32685
- const envPath = join58(agentsDir, name, "telegram", ".env");
32702
+ const envPath = join59(agentsDir, name, "telegram", ".env");
32686
32703
  const read = tryReadHostFile(envPath);
32687
32704
  if (read.kind === "eacces") {
32688
32705
  results.push({
@@ -32765,7 +32782,7 @@ function checkStartShStale(agentName, startShPath) {
32765
32782
  }
32766
32783
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
32767
32784
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
32768
- const path4 = join58(agentDir, "home", ".switchroom");
32785
+ const path4 = join59(agentDir, "home", ".switchroom");
32769
32786
  let stats;
32770
32787
  try {
32771
32788
  stats = lstatSync6(path4);
@@ -32802,7 +32819,7 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
32802
32819
  }
32803
32820
  function checkRepoHygiene(repoRoot) {
32804
32821
  const results = [];
32805
- const exportDir = join58(repoRoot, "clerk-export");
32822
+ const exportDir = join59(repoRoot, "clerk-export");
32806
32823
  if (existsSync57(exportDir)) {
32807
32824
  results.push({
32808
32825
  name: "repo hygiene: clerk-export/ on disk (#1072)",
@@ -32811,7 +32828,7 @@ function checkRepoHygiene(repoRoot) {
32811
32828
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
32812
32829
  });
32813
32830
  }
32814
- const knownTarball = join58(repoRoot, "clerk-export-with-secrets.tar.gz");
32831
+ const knownTarball = join59(repoRoot, "clerk-export-with-secrets.tar.gz");
32815
32832
  if (existsSync57(knownTarball)) {
32816
32833
  results.push({
32817
32834
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
@@ -32829,7 +32846,7 @@ function checkRepoHygiene(repoRoot) {
32829
32846
  results.push({
32830
32847
  name: `repo hygiene: ${name} on disk (#1072)`,
32831
32848
  status: "warn",
32832
- detail: `${join58(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
32849
+ detail: `${join59(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
32833
32850
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
32834
32851
  });
32835
32852
  }
@@ -32852,9 +32869,9 @@ function checkRepoHygiene(repoRoot) {
32852
32869
  }
32853
32870
  function isSwitchroomCheckout(dir) {
32854
32871
  try {
32855
- if (!existsSync57(join58(dir, ".git")))
32872
+ if (!existsSync57(join59(dir, ".git")))
32856
32873
  return false;
32857
- const pkgPath = join58(dir, "package.json");
32874
+ const pkgPath = join59(dir, "package.json");
32858
32875
  if (!existsSync57(pkgPath))
32859
32876
  return false;
32860
32877
  const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
@@ -32891,7 +32908,7 @@ function checkAgents(config, configPath) {
32891
32908
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
32892
32909
  });
32893
32910
  }
32894
- results.push(checkStartShStale(name, join58(agentDir, "start.sh")));
32911
+ results.push(checkStartShStale(name, join59(agentDir, "start.sh")));
32895
32912
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
32896
32913
  const status = statuses[name];
32897
32914
  const active = status?.active ?? "unknown";
@@ -32968,7 +32985,7 @@ function checkAgents(config, configPath) {
32968
32985
  }
32969
32986
  }
32970
32987
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
32971
- const mcpJsonPath = join58(agentDir, ".mcp.json");
32988
+ const mcpJsonPath = join59(agentDir, ".mcp.json");
32972
32989
  if (!existsSync57(mcpJsonPath)) {
32973
32990
  results.push({
32974
32991
  name: `${name}: .mcp.json`,
@@ -33270,7 +33287,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
33270
33287
  };
33271
33288
  }
33272
33289
  const credDir = dirname13(envPath);
33273
- const authScript = join58(credDir, "claude-auth.py");
33290
+ const authScript = join59(credDir, "claude-auth.py");
33274
33291
  if (!existsSync57(authScript)) {
33275
33292
  return {
33276
33293
  name: "mff: auth flow",
@@ -50182,12 +50199,12 @@ var {
50182
50199
  } = import__.default;
50183
50200
 
50184
50201
  // src/build-info.ts
50185
- var VERSION = "0.15.3";
50186
- var COMMIT_SHA = "af652154";
50202
+ var VERSION = "0.15.4";
50203
+ var COMMIT_SHA = "dd68b93e";
50187
50204
 
50188
50205
  // src/cli/agent.ts
50189
50206
  init_source();
50190
- import { join as join19, resolve as resolve21 } from "node:path";
50207
+ import { join as join20, resolve as resolve21 } from "node:path";
50191
50208
  import { rmSync as rmSync9, existsSync as existsSync28, readFileSync as readFileSync23, writeFileSync as writeFileSync13 } from "node:fs";
50192
50209
  import { homedir as homedir8 } from "node:os";
50193
50210
 
@@ -52922,8 +52939,6 @@ function classifyChange(path, agentDir, useHotReloadStable) {
52922
52939
  return "hot";
52923
52940
  if (relPath.startsWith("workspace/memory/") && relPath.endsWith(".md"))
52924
52941
  return "hot";
52925
- if (relPath === "workspace/HEARTBEAT.md")
52926
- return "hot";
52927
52942
  if (relPath === "workspace/SOUL.md" || relPath === "workspace/SOUL.custom.md") {
52928
52943
  return useHotReloadStable ? "hot" : "restart-required";
52929
52944
  }
@@ -53116,6 +53131,7 @@ function buildSettingsHooksBlock(p) {
53116
53131
  }
53117
53132
  ] : [];
53118
53133
  const useHotReloadStable = agentConfig.channels?.telegram?.hotReloadStable === true;
53134
+ const useInjectOnChange = agentConfig.channels?.telegram?.inject_on_change !== false;
53119
53135
  const turnPacingDirective = "<turn-pacing>You are messaging a human via Telegram. The framework " + "automatically shows the user a live preview in their compose area as " + 'you work \u2014 they see "Read a file", "Ran 2 commands", etc. as your ' + `tool_use events stream. You do NOT need to ack manually.
53120
53136
 
53121
53137
  ` + "ALWAYS reply to a message the user sends you. A direct message " + 'expects a response: a greeting ("hi", "hey", "you there?") gets a ' + "greeting back; a thanks gets a brief acknowledgement; a question " + "gets an answer. NEVER end a turn with NO_REPLY when the user has " + "just sent you something \u2014 NO_REPLY is only for genuine non-prompts " + `(a system-synthesized event you have already fully handled).
@@ -53150,7 +53166,7 @@ function buildSettingsHooksBlock(p) {
53150
53166
  hooks: [
53151
53167
  {
53152
53168
  type: "command",
53153
- command: wrap("hook:workspace-dynamic", `bash "${join9(DOCKER_BIN_PATH, "workspace-dynamic-hook.sh")}"`),
53169
+ command: wrap("hook:workspace-dynamic", `${useInjectOnChange ? `env SWITCHROOM_INJECT_ON_CHANGE=1 ` : ""}bash "${join9(DOCKER_BIN_PATH, "workspace-dynamic-hook.sh")}"`),
53154
53170
  timeout: 5
53155
53171
  }
53156
53172
  ]
@@ -53168,7 +53184,10 @@ function buildSettingsHooksBlock(p) {
53168
53184
  hooks: [
53169
53185
  {
53170
53186
  type: "command",
53171
- command: wrap("hook:turn-pacing", `printf '%s\\n' ${shellSingleQuote(turnPacingDirective)}`),
53187
+ command: useInjectOnChange ? (() => {
53188
+ const directiveHash = createHash2("sha256").update(turnPacingDirective).digest("hex");
53189
+ return wrap("hook:turn-pacing", `env TURN_PACING_DIRECTIVE=${shellSingleQuote(turnPacingDirective)} TURN_PACING_HASH=${shellSingleQuote(directiveHash)} bash "${join9(DOCKER_BIN_PATH, "turn-pacing-hook.sh")}"`);
53190
+ })() : wrap("hook:turn-pacing", `printf '%s\\n' ${shellSingleQuote(turnPacingDirective)}`),
53172
53191
  timeout: 3
53173
53192
  }
53174
53193
  ]
@@ -53984,7 +54003,7 @@ init_compose();
53984
54003
  import { chownSync as chownSync2 } from "node:fs";
53985
54004
  import { mkdir, readFile, writeFile } from "node:fs/promises";
53986
54005
  import { homedir as homedir6 } from "node:os";
53987
- import { dirname as dirname3 } from "node:path";
54006
+ import { basename as basename3, dirname as dirname3, join as join12 } from "node:path";
53988
54007
 
53989
54008
  // src/config/release-resolve.ts
53990
54009
  function resolveImageTag(release) {
@@ -54005,7 +54024,6 @@ function resolveRelease(opts) {
54005
54024
  return opts.root;
54006
54025
  return;
54007
54026
  }
54008
-
54009
54027
  // src/cli/operator-uid.ts
54010
54028
  import {
54011
54029
  chownSync,
@@ -54098,17 +54116,36 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
54098
54116
 
54099
54117
  // src/cli/write-compose.ts
54100
54118
  var AGENT_IMAGE_TAG_RE = /image:\s*\S*switchroom-agent:(\S+)/;
54119
+ var CONTAINER_CONFIG_PREFIX = "/state/config/";
54120
+ function resolveHostSwitchroomConfigPath(rawPath) {
54121
+ if (!isDockerRuntime()) {
54122
+ return rawPath;
54123
+ }
54124
+ if (!rawPath.startsWith(CONTAINER_CONFIG_PREFIX)) {
54125
+ return rawPath;
54126
+ }
54127
+ const hostHome = process.env.SWITCHROOM_HOST_HOME;
54128
+ if (!hostHome || hostHome.length === 0) {
54129
+ throw new Error(`switchroom apply: running inside a container but SWITCHROOM_HOST_HOME is not set.
54130
+ ` + `The config path "${rawPath}" is a container-internal path \u2014 emitting it as a ` + `Docker bind-mount source would cause Docker to create an empty directory at that ` + `path on the host, crashing the vault-broker and auth-broker with EISDIR.
54131
+
54132
+ ` + `Recovery: run \`switchroom apply\` once from the HOST (not from inside the container). ` + `That will regenerate the compose file with SWITCHROOM_HOST_HOME set, after which ` + `in-container apply will work correctly.`);
54133
+ }
54134
+ const filename = basename3(rawPath);
54135
+ return join12(hostHome, ".switchroom", filename);
54136
+ }
54101
54137
  async function writeComposeFile(opts) {
54102
54138
  const release = resolveRelease({ override: opts.releaseOverride, root: opts.config.release });
54103
54139
  const imageTag = resolveImageTag(release);
54104
54140
  const operatorUid = resolveOperatorUid();
54141
+ const resolvedConfigPath = opts.switchroomConfigPath !== undefined ? resolveHostSwitchroomConfigPath(opts.switchroomConfigPath) : undefined;
54105
54142
  const content = generateCompose({
54106
54143
  config: opts.config,
54107
54144
  imageTag,
54108
54145
  buildMode: opts.buildMode ?? "pull",
54109
54146
  buildContext: opts.buildContext,
54110
- homeDir: homedir6(),
54111
- switchroomConfigPath: opts.switchroomConfigPath,
54147
+ homeDir: process.env.SWITCHROOM_HOST_HOME || homedir6(),
54148
+ switchroomConfigPath: resolvedConfigPath,
54112
54149
  operatorUid
54113
54150
  });
54114
54151
  let previous = null;
@@ -54360,12 +54397,12 @@ function spinner(message) {
54360
54397
 
54361
54398
  // src/agents/status.ts
54362
54399
  import { existsSync as existsSync20, readFileSync as readFileSync18, statSync as statSync11 } from "node:fs";
54363
- import { join as join14 } from "node:path";
54400
+ import { join as join15 } from "node:path";
54364
54401
  import { execFileSync as execFileSync9 } from "node:child_process";
54365
54402
 
54366
54403
  // src/agents/handoff-summarizer.ts
54367
54404
  import { readFileSync as readFileSync16, writeFileSync as writeFileSync8, renameSync as renameSync5, mkdirSync as mkdirSync14, existsSync as existsSync18, statSync as statSync10, readdirSync as readdirSync9 } from "node:fs";
54368
- import { join as join13 } from "node:path";
54405
+ import { join as join14 } from "node:path";
54369
54406
  var DEFAULT_MAX_TURNS = 50;
54370
54407
  var TOPIC_MAX_CHARS = 117;
54371
54408
  var TURN_TEXT_MAX_CHARS = 1200;
@@ -54500,8 +54537,8 @@ function clampTopic(s) {
54500
54537
  }
54501
54538
  function writeSidecarsAtomic(agentDir, briefing, topic) {
54502
54539
  mkdirSync14(agentDir, { recursive: true });
54503
- const handoffPath = join13(agentDir, ".handoff.md");
54504
- const topicPath = join13(agentDir, ".handoff-topic");
54540
+ const handoffPath = join14(agentDir, ".handoff.md");
54541
+ const topicPath = join14(agentDir, ".handoff-topic");
54505
54542
  const handoffTmp = handoffPath + ".tmp";
54506
54543
  const topicTmp = topicPath + ".tmp";
54507
54544
  writeFileSync8(handoffTmp, briefing, "utf-8");
@@ -54562,7 +54599,7 @@ async function mirrorToHindsight(briefing, opts) {
54562
54599
  }
54563
54600
  }
54564
54601
  function findLatestSessionJsonl(claudeConfigDir) {
54565
- const projects = join13(claudeConfigDir, "projects");
54602
+ const projects = join14(claudeConfigDir, "projects");
54566
54603
  if (!existsSync18(projects))
54567
54604
  return null;
54568
54605
  let latest = null;
@@ -54574,7 +54611,7 @@ function findLatestSessionJsonl(claudeConfigDir) {
54574
54611
  return;
54575
54612
  }
54576
54613
  for (const name of entries) {
54577
- const full = join13(dir, name);
54614
+ const full = join14(dir, name);
54578
54615
  let st;
54579
54616
  try {
54580
54617
  st = statSync10(full);
@@ -54910,10 +54947,10 @@ async function buildAgentStatusReport(inputs) {
54910
54947
  };
54911
54948
  }
54912
54949
  }
54913
- const logPath = join14(inputs.agentDir, "telegram", "gateway.log");
54950
+ const logPath = join15(inputs.agentDir, "telegram", "gateway.log");
54914
54951
  const logContent = inputs.readGatewayLog(logPath);
54915
54952
  const polling = buildPollingStatus(logContent);
54916
- const historyDbPath = join14(inputs.agentDir, "telegram", "history.db");
54953
+ const historyDbPath = join15(inputs.agentDir, "telegram", "history.db");
54917
54954
  const messagesResult = inputs.getLastMessages(historyDbPath);
54918
54955
  const messages = buildMessageStatus(messagesResult);
54919
54956
  const checks = [
@@ -55191,7 +55228,7 @@ function defaultStatusInputs(params) {
55191
55228
  }
55192
55229
  function readCacheTelemetry(agentDir) {
55193
55230
  try {
55194
- const claudeConfigDir = join14(agentDir, ".claude");
55231
+ const claudeConfigDir = join15(agentDir, ".claude");
55195
55232
  const jsonl = findLatestSessionJsonl(claudeConfigDir);
55196
55233
  if (!jsonl)
55197
55234
  return null;
@@ -55431,7 +55468,7 @@ async function completeCreation(name, code, opts = {}) {
55431
55468
  }
55432
55469
 
55433
55470
  // src/agents/add-orchestrator.ts
55434
- import { resolve as resolve18, join as join17 } from "node:path";
55471
+ import { resolve as resolve18, join as join18 } from "node:path";
55435
55472
  import { existsSync as existsSync25, rmSync as rmSync7, statSync as statSync15 } from "node:fs";
55436
55473
  import { execFileSync as execFileSync12 } from "node:child_process";
55437
55474
 
@@ -55985,7 +56022,7 @@ function pruneBundledSkills(agentDir, keep, scope) {
55985
56022
  for (const entry of scopeSet) {
55986
56023
  if (keepSet.has(entry))
55987
56024
  continue;
55988
- const entryPath = join17(skillsDir, entry);
56025
+ const entryPath = join18(skillsDir, entry);
55989
56026
  if (!existsSync25(entryPath))
55990
56027
  continue;
55991
56028
  let isDir = false;
@@ -56706,7 +56743,7 @@ function registerAgentCommand(program3) {
56706
56743
  }
56707
56744
  return;
56708
56745
  }
56709
- const logsDir = join19(homedir8(), ".switchroom", "logs");
56746
+ const logsDir = join20(homedir8(), ".switchroom", "logs");
56710
56747
  const schedulerStates = Object.fromEntries(agentNames.map((n) => [n, getSchedulerState(n, logsDir)]));
56711
56748
  if (opts.json) {
56712
56749
  const data = agentNames.map((name) => {
@@ -57746,7 +57783,7 @@ init_lifecycle();
57746
57783
  import { execFile as execFile2 } from "node:child_process";
57747
57784
  import { promisify as promisify2 } from "node:util";
57748
57785
  import { homedir as homedir10 } from "node:os";
57749
- import { join as join21 } from "node:path";
57786
+ import { join as join22 } from "node:path";
57750
57787
  init_helpers();
57751
57788
  init_broker_call();
57752
57789
  var execFileP = promisify2(execFile2);
@@ -57818,7 +57855,7 @@ function registerStatusCommand(program3) {
57818
57855
  const config = getConfig(program3);
57819
57856
  const agentNames = Object.keys(config.agents ?? {}).sort();
57820
57857
  const statuses = getAllAgentStatuses(config);
57821
- const logsDir = join21(homedir10(), ".switchroom", "logs");
57858
+ const logsDir = join22(homedir10(), ".switchroom", "logs");
57822
57859
  const schedulerStates = Object.fromEntries(agentNames.map((n) => [n, getSchedulerState(n, logsDir)]));
57823
57860
  const accountsP = fetchAccounts();
57824
57861
  const mcpEnabled = opts.mcp !== false;
@@ -58228,7 +58265,7 @@ init_loader();
58228
58265
  init_manager();
58229
58266
  init_helpers();
58230
58267
  import { existsSync as existsSync33, readFileSync as readFileSync28 } from "node:fs";
58231
- import { join as join25, resolve as resolve24 } from "node:path";
58268
+ import { join as join26, resolve as resolve24 } from "node:path";
58232
58269
 
58233
58270
  // src/cli/auth-google.ts
58234
58271
  init_source();
@@ -59753,8 +59790,8 @@ var SEVERITY_RANK = {
59753
59790
  };
59754
59791
  function diagnoseAuthState(claudeConfigDir) {
59755
59792
  const findings = [];
59756
- const credsPath = join25(claudeConfigDir, ".credentials.json");
59757
- const oauthTokenPath2 = join25(claudeConfigDir, ".oauth-token");
59793
+ const credsPath = join26(claudeConfigDir, ".credentials.json");
59794
+ const oauthTokenPath2 = join26(claudeConfigDir, ".oauth-token");
59758
59795
  const hasCreds = existsSync33(credsPath);
59759
59796
  const hasOauthToken = existsSync33(oauthTokenPath2);
59760
59797
  if (!hasCreds && !hasOauthToken) {
@@ -59939,7 +59976,7 @@ function loadCredentialsFromAgent(agentName) {
59939
59976
  const config = getConfigSafe();
59940
59977
  const agentsDir = resolveAgentsDir(config);
59941
59978
  const agentDir = resolve24(agentsDir, agentName);
59942
- const credsPath = join25(agentDir, ".claude", ".credentials.json");
59979
+ const credsPath = join26(agentDir, ".claude", ".credentials.json");
59943
59980
  if (!existsSync33(credsPath)) {
59944
59981
  console.error(source_default.red(` Agent "${agentName}" has no .claude/.credentials.json \u2014 log it in first.`));
59945
59982
  process.exit(1);
@@ -60002,7 +60039,7 @@ async function loadCredentialsViaClaude(label) {
60002
60039
  import("node:os"),
60003
60040
  import("node:readline")
60004
60041
  ]);
60005
- const configDir = join25(homedir13(), ".switchroom", "accounts", label);
60042
+ const configDir = join26(homedir13(), ".switchroom", "accounts", label);
60006
60043
  const result = await runViaClaude2({
60007
60044
  configDir,
60008
60045
  promptForCode: async (_url) => {
@@ -60044,7 +60081,7 @@ function registerAuthCommand(program3) {
60044
60081
  await withConfigError(async () => {
60045
60082
  const config = getConfig(program3);
60046
60083
  const agentsDir = resolveAgentsDir(config);
60047
- configDir = join25(agentsDir, agent, ".claude");
60084
+ configDir = join26(agentsDir, agent, ".claude");
60048
60085
  })();
60049
60086
  }
60050
60087
  const diag = diagnoseAuthState(configDir);
@@ -60203,7 +60240,7 @@ function registerAuthCommand(program3) {
60203
60240
  }));
60204
60241
  auth.command("reauth <agent>").description("Start/resume an OAuth re-auth session for an agent; prints the login URL").option("--slot <label>", "Target a specific account slot/label instead of the agent's active one").option("--force", "Kill any existing session and force a fresh login (use to switch accounts)").action(withConfigError(async (agent, opts) => {
60205
60242
  const config = getConfig(program3);
60206
- const agentDir = join25(resolveAgentsDir(config), agent);
60243
+ const agentDir = join26(resolveAgentsDir(config), agent);
60207
60244
  const r = startAuthSession(agent, agentDir, {
60208
60245
  force: opts.force,
60209
60246
  slot: opts.slot
@@ -60216,7 +60253,7 @@ function registerAuthCommand(program3) {
60216
60253
  }));
60217
60254
  auth.command("code <agent> <code>").description("Submit the browser OAuth code to complete a pending `auth reauth`").option("--slot <label>", "Target a specific account slot/label").option("--json", "Emit the structured AuthCodeResult as JSON (consumed by the Telegram gateway)").action(withConfigError(async (agent, code, opts) => {
60218
60255
  const config = getConfig(program3);
60219
- const agentDir = join25(resolveAgentsDir(config), agent);
60256
+ const agentDir = join26(resolveAgentsDir(config), agent);
60220
60257
  const r = submitAuthCode(agent, agentDir, code, opts.slot);
60221
60258
  if (opts.json) {
60222
60259
  console.log(JSON.stringify({
@@ -60233,7 +60270,7 @@ function registerAuthCommand(program3) {
60233
60270
  }));
60234
60271
  auth.command("cancel <agent>").description("Cancel a pending `auth reauth` session for an agent").option("--slot <label>", "Target a specific account slot/label").action(withConfigError(async (agent, opts) => {
60235
60272
  const config = getConfig(program3);
60236
- const agentDir = join25(resolveAgentsDir(config), agent);
60273
+ const agentDir = join26(resolveAgentsDir(config), agent);
60237
60274
  const r = cancelAuthSession(agent, agentDir, opts.slot);
60238
60275
  for (const line of r.instructions)
60239
60276
  console.log(line);
@@ -60255,7 +60292,7 @@ init_vault();
60255
60292
  init_hindsight();
60256
60293
  import { readFileSync as readFileSync29, writeFileSync as writeFileSync17, copyFileSync as copyFileSync6, existsSync as existsSync34, readdirSync as readdirSync16, statSync as statSync18 } from "node:fs";
60257
60294
  import { execFileSync as execFileSync15 } from "node:child_process";
60258
- import { join as join26 } from "node:path";
60295
+ import { join as join27 } from "node:path";
60259
60296
  import { homedir as homedir13 } from "node:os";
60260
60297
  function getVaultPath2(configPath) {
60261
60298
  try {
@@ -60271,7 +60308,7 @@ function maskToken(s) {
60271
60308
  return "***";
60272
60309
  }
60273
60310
  function findClaudeTranscripts() {
60274
- const root = join26(homedir13(), ".claude", "projects");
60311
+ const root = join27(homedir13(), ".claude", "projects");
60275
60312
  if (!existsSync34(root))
60276
60313
  return [];
60277
60314
  const out = [];
@@ -60283,7 +60320,7 @@ function findClaudeTranscripts() {
60283
60320
  return;
60284
60321
  }
60285
60322
  for (const e of entries) {
60286
- const p = join26(dir, e);
60323
+ const p = join27(dir, e);
60287
60324
  let st;
60288
60325
  try {
60289
60326
  st = statSync18(p);
@@ -60302,8 +60339,8 @@ function findClaudeTranscripts() {
60302
60339
  }
60303
60340
  function findTelegramHistoryDb() {
60304
60341
  const candidates = [
60305
- process.env.TELEGRAM_STATE_DIR ? join26(process.env.TELEGRAM_STATE_DIR, "history.db") : null,
60306
- join26(homedir13(), ".claude", "channels", "telegram", "history.db")
60342
+ process.env.TELEGRAM_STATE_DIR ? join27(process.env.TELEGRAM_STATE_DIR, "history.db") : null,
60343
+ join27(homedir13(), ".claude", "channels", "telegram", "history.db")
60307
60344
  ].filter(Boolean);
60308
60345
  for (const c of candidates) {
60309
60346
  if (existsSync34(c))
@@ -60670,7 +60707,7 @@ init_compose();
60670
60707
  init_vault();
60671
60708
  import * as net3 from "node:net";
60672
60709
  import { mkdirSync as mkdirSync23, chmodSync as chmodSync7, chownSync as chownSync4, existsSync as existsSync37, readFileSync as readFileSync32, readdirSync as readdirSync17, statSync as statSync20, unlinkSync as unlinkSync8, writeFileSync as writeFileSync19, renameSync as renameSync10 } from "node:fs";
60673
- import { dirname as dirname7, resolve as resolve26, basename as basename4 } from "node:path";
60710
+ import { dirname as dirname7, resolve as resolve26, basename as basename5 } from "node:path";
60674
60711
  import * as os4 from "node:os";
60675
60712
  import * as path3 from "node:path";
60676
60713
 
@@ -60692,13 +60729,13 @@ import {
60692
60729
  unlinkSync as unlinkSync7
60693
60730
  } from "node:fs";
60694
60731
  import { createHash as createHash5 } from "node:crypto";
60695
- import { basename as basename3, dirname as dirname4, join as join27 } from "node:path";
60732
+ import { basename as basename4, dirname as dirname4, join as join28 } from "node:path";
60696
60733
  function vaultLayoutPaths(home2) {
60697
- const switchroomRoot = join27(home2, ".switchroom");
60734
+ const switchroomRoot = join28(home2, ".switchroom");
60698
60735
  return {
60699
- oldPath: join27(switchroomRoot, "vault.enc"),
60700
- newPath: join27(switchroomRoot, "vault", "vault.enc"),
60701
- parent: join27(switchroomRoot, "vault"),
60736
+ oldPath: join28(switchroomRoot, "vault.enc"),
60737
+ newPath: join28(switchroomRoot, "vault", "vault.enc"),
60738
+ parent: join28(switchroomRoot, "vault"),
60702
60739
  switchroomRoot
60703
60740
  };
60704
60741
  }
@@ -60845,7 +60882,7 @@ function sha256File(path) {
60845
60882
  return createHash5("sha256").update(data).digest("hex");
60846
60883
  }
60847
60884
  function atomicReplaceWithSymlink(target, linkTarget) {
60848
- const tmp = join27(dirname4(target), `.${basename3(target)}.symlink-tmp`);
60885
+ const tmp = join28(dirname4(target), `.${basename4(target)}.symlink-tmp`);
60849
60886
  if (existsSync35(tmp)) {
60850
60887
  try {
60851
60888
  unlinkSync7(tmp);
@@ -60998,7 +61035,7 @@ import * as path from "node:path";
60998
61035
  // src/vault/broker/test-isolation-guard.ts
60999
61036
  import { mkdtempSync as mkdtempSync4 } from "node:fs";
61000
61037
  import { homedir as homedir14, tmpdir as tmpdir3 } from "node:os";
61001
- import { join as join28, resolve as resolve25, sep } from "node:path";
61038
+ import { join as join29, resolve as resolve25, sep } from "node:path";
61002
61039
  function isTestRuntime() {
61003
61040
  return process.env.VITEST !== undefined;
61004
61041
  }
@@ -61019,7 +61056,7 @@ function safeVaultPath(requestedPath) {
61019
61056
  return requestedPath;
61020
61057
  if (!isUnderRealSwitchroomHome(requestedPath))
61021
61058
  return requestedPath;
61022
- const redirected = join28(mkdtempSync4(join28(tmpdir3(), "sw-test-vault-")), "vault.enc");
61059
+ const redirected = join29(mkdtempSync4(join29(tmpdir3(), "sw-test-vault-")), "vault.enc");
61023
61060
  if (!warnedVault) {
61024
61061
  warnedVault = true;
61025
61062
  process.stderr.write(`[test-isolation guard] vault path redirected away from the ` + `production tree to an isolated tmpdir \u2014 a vault/broker test ` + `resolved its vault path inside ~/.switchroom. See CLAUDE.md ` + `"Vault & shared-state test discipline".
@@ -61035,7 +61072,7 @@ function safeAuditLogPath(requestedPath) {
61035
61072
  if (!isUnderRealSwitchroomHome(requestedPath))
61036
61073
  return requestedPath;
61037
61074
  if (!cachedTmpAuditLog) {
61038
- cachedTmpAuditLog = join28(mkdtempSync4(join28(tmpdir3(), "sw-test-audit-")), "vault-audit.log");
61075
+ cachedTmpAuditLog = join29(mkdtempSync4(join29(tmpdir3(), "sw-test-audit-")), "vault-audit.log");
61039
61076
  }
61040
61077
  if (!warnedAudit) {
61041
61078
  warnedAudit = true;
@@ -64872,12 +64909,12 @@ class VaultBroker {
64872
64909
  }
64873
64910
  function detectVaultLayoutDrift(vaultPath) {
64874
64911
  const dir = dirname7(vaultPath);
64875
- if (basename4(dir) !== "vault")
64912
+ if (basename5(dir) !== "vault")
64876
64913
  return;
64877
- if (basename4(vaultPath) !== "vault.enc")
64914
+ if (basename5(vaultPath) !== "vault.enc")
64878
64915
  return;
64879
64916
  const switchroomDir = dirname7(dir);
64880
- if (basename4(switchroomDir) !== ".switchroom")
64917
+ if (basename5(switchroomDir) !== ".switchroom")
64881
64918
  return;
64882
64919
  const home2 = dirname7(switchroomDir);
64883
64920
  const result = inspectVaultLayout(home2);
@@ -64988,7 +65025,7 @@ var import_yaml10 = __toESM(require_dist(), 1);
64988
65025
  import { spawnSync as spawnSync3 } from "node:child_process";
64989
65026
  import { readFileSync as readFileSync33, writeFileSync as writeFileSync20 } from "node:fs";
64990
65027
  import { homedir as homedir18 } from "node:os";
64991
- import { join as join33 } from "node:path";
65028
+ import { join as join34 } from "node:path";
64992
65029
  class EncryptFailedError extends Error {
64993
65030
  detail;
64994
65031
  constructor(detail) {
@@ -65021,7 +65058,7 @@ function setVaultBrokerAutoUnlock(configPath, value) {
65021
65058
  doc.setIn(["vault", "broker", "autoUnlock"], value);
65022
65059
  writeFileSync20(configPath, doc.toString(), "utf-8");
65023
65060
  }
65024
- var DEFAULT_COMPOSE_FILE = join33(homedir18(), ".switchroom", "compose", "docker-compose.yml");
65061
+ var DEFAULT_COMPOSE_FILE = join34(homedir18(), ".switchroom", "compose", "docker-compose.yml");
65025
65062
  async function applyAutoUnlock(opts = {}) {
65026
65063
  const log = opts.log ?? ((s) => console.log(s));
65027
65064
  const err = opts.err ?? ((s) => console.error(s));
@@ -65740,7 +65777,7 @@ init_source();
65740
65777
  init_loader();
65741
65778
  init_loader();
65742
65779
  init_client();
65743
- import { join as join34 } from "node:path";
65780
+ import { join as join35 } from "node:path";
65744
65781
  import { homedir as homedir19 } from "node:os";
65745
65782
  function parseDuration(raw) {
65746
65783
  const lower = raw.toLowerCase().trim();
@@ -65833,7 +65870,7 @@ function registerVaultGrantCommands(vault, program3) {
65833
65870
  console.error(source_default.red(`Failed to mint grant: ${result.msg}`));
65834
65871
  process.exit(1);
65835
65872
  }
65836
- const tokenPath = join34(homedir19(), ".switchroom", "agents", agent, ".vault-token");
65873
+ const tokenPath = join35(homedir19(), ".switchroom", "agents", agent, ".vault-token");
65837
65874
  console.log(source_default.green(`\u2713 Grant minted`));
65838
65875
  if (process.stdout.isTTY) {
65839
65876
  console.log(source_default.bold("Token: ") + result.token);
@@ -65885,7 +65922,7 @@ The token file was written to the agent directory (mode 0600).`));
65885
65922
  init_source();
65886
65923
  init_loader();
65887
65924
  import { existsSync as existsSync42 } from "node:fs";
65888
- import { join as join36 } from "node:path";
65925
+ import { join as join37 } from "node:path";
65889
65926
 
65890
65927
  // src/vault/backup.ts
65891
65928
  import {
@@ -65908,7 +65945,7 @@ import {
65908
65945
  writeSync as writeSync5
65909
65946
  } from "node:fs";
65910
65947
  import { homedir as homedir20 } from "node:os";
65911
- import { join as join35, resolve as resolvePath2 } from "node:path";
65948
+ import { join as join36, resolve as resolvePath2 } from "node:path";
65912
65949
  var LATEST_SYMLINK = "vault.enc.latest.bak";
65913
65950
  var MANIFEST_FILE = "manifest.jsonl";
65914
65951
  var DEFAULT_RETAIN = 30;
@@ -65983,9 +66020,9 @@ function resolveBackupDestination(input) {
65983
66020
  if (input.configDestination)
65984
66021
  return resolvePath2(input.configDestination);
65985
66022
  if (input.hasSwitchroomConfigRepo) {
65986
- return join35(input.home, ".switchroom-config", "vault-backups");
66023
+ return join36(input.home, ".switchroom-config", "vault-backups");
65987
66024
  }
65988
- return join35(input.home, ".switchroom", "vault-backups");
66025
+ return join36(input.home, ".switchroom", "vault-backups");
65989
66026
  }
65990
66027
  function findAutoUnlockSibling(entries) {
65991
66028
  for (const name of entries) {
@@ -66036,9 +66073,9 @@ function backupVault(opts) {
66036
66073
  throw new Error(`vault backup refused: destination '${opts.destDir}' contains a file ` + `that looks like an auto-unlock credential ('${offender}'). The ` + `machine-bound auto-unlock blob MUST NOT be co-located with the ` + `encrypted vault \u2014 if they're together in version control, the ` + `passphrase gate is bypassed. Move/remove that file and retry.`);
66037
66074
  }
66038
66075
  const filename = computeBackupFilename(now);
66039
- const fullPath = join35(opts.destDir, filename);
66076
+ const fullPath = join36(opts.destDir, filename);
66040
66077
  const tmpName = `.tmp.${process.pid}.${randomBytes10(4).toString("hex")}`;
66041
- const tmpPath = join35(opts.destDir, tmpName);
66078
+ const tmpPath = join36(opts.destDir, tmpName);
66042
66079
  const src = readFileSync36(opts.vaultPath);
66043
66080
  const fd = openSync9(tmpPath, "wx", 384);
66044
66081
  try {
@@ -66062,9 +66099,9 @@ function backupVault(opts) {
66062
66099
  size_bytes: stat.size,
66063
66100
  sha256
66064
66101
  };
66065
- appendFileSync3(join35(opts.destDir, MANIFEST_FILE), JSON.stringify(row) + `
66102
+ appendFileSync3(join36(opts.destDir, MANIFEST_FILE), JSON.stringify(row) + `
66066
66103
  `, { mode: 384 });
66067
- const latestPath = join35(opts.destDir, LATEST_SYMLINK);
66104
+ const latestPath = join36(opts.destDir, LATEST_SYMLINK);
66068
66105
  try {
66069
66106
  unlinkSync10(latestPath);
66070
66107
  } catch {}
@@ -66083,7 +66120,7 @@ function backupVault(opts) {
66083
66120
  const pruneNames = selectBackupsToPrune(sorted, retain);
66084
66121
  for (const old of pruneNames) {
66085
66122
  try {
66086
- unlinkSync10(join35(opts.destDir, old));
66123
+ unlinkSync10(join36(opts.destDir, old));
66087
66124
  } catch {}
66088
66125
  }
66089
66126
  if (pruneNames.length > 0) {
@@ -66132,7 +66169,7 @@ function registerVaultBackupCommand(vault, program3) {
66132
66169
  }
66133
66170
  } catch {}
66134
66171
  const home2 = defaultHome();
66135
- const hasSwitchroomConfigRepo = existsSync42(join36(home2, ".switchroom-config"));
66172
+ const hasSwitchroomConfigRepo = existsSync42(join37(home2, ".switchroom-config"));
66136
66173
  const destDir = resolveBackupDestination({
66137
66174
  cliToFlag: opts.to ? resolvePath(opts.to) : undefined,
66138
66175
  configDestination,
@@ -66759,7 +66796,7 @@ Push passphrase to broker for future requests? [Y/n]: `);
66759
66796
  // src/cli/telegram.ts
66760
66797
  init_source();
66761
66798
  import { existsSync as existsSync43, readFileSync as readFileSync38, writeFileSync as writeFileSync21 } from "node:fs";
66762
- import { join as join37 } from "node:path";
66799
+ import { join as join38 } from "node:path";
66763
66800
 
66764
66801
  // src/web/webhook-dispatch.ts
66765
66802
  function renderTemplate2(template, ctx) {
@@ -67021,7 +67058,7 @@ async function runTopicsDiscover(program3, chatId, opts) {
67021
67058
  fail(`Unknown agent '${agentName}'. Check switchroom.yaml.`);
67022
67059
  }
67023
67060
  const agentsDir = process.env.SWITCHROOM_AGENTS_DIR ?? resolveStatePath("agents");
67024
- const dbPath = join37(agentsDir, agentName, "telegram", "history.db");
67061
+ const dbPath = join38(agentsDir, agentName, "telegram", "history.db");
67025
67062
  if (!existsSync43(dbPath)) {
67026
67063
  fail(`No history DB at ${dbPath}.
67027
67064
  ` + ` The agent may not have received any Telegram messages yet, or it may be offline.
@@ -67687,9 +67724,9 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
67687
67724
  init_loader();
67688
67725
  var import_yaml12 = __toESM(require_dist(), 1);
67689
67726
  import { existsSync as existsSync44, readFileSync as readFileSync39, writeFileSync as writeFileSync22 } from "node:fs";
67690
- import { join as join38 } from "node:path";
67727
+ import { join as join39 } from "node:path";
67691
67728
  function readRecallLog(agentDir, limit) {
67692
- const path4 = join38(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
67729
+ const path4 = join39(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
67693
67730
  if (!existsSync44(path4))
67694
67731
  return [];
67695
67732
  let raw;
@@ -67930,7 +67967,7 @@ Cross-agent reflection plan
67930
67967
  process.exit(1);
67931
67968
  })() : Object.keys(config.agents);
67932
67969
  for (const name of targets) {
67933
- const agentDir = join38(agentsDir, name);
67970
+ const agentDir = join39(agentsDir, name);
67934
67971
  const entries = readRecallLog(agentDir, limit);
67935
67972
  if (opts.json) {
67936
67973
  for (const e of entries) {
@@ -68006,7 +68043,7 @@ import {
68006
68043
  writeSync as writeSync6,
68007
68044
  constants as fsConstants3
68008
68045
  } from "node:fs";
68009
- import { resolve as resolve28, extname, join as join45, relative, dirname as dirname10 } from "node:path";
68046
+ import { resolve as resolve28, extname, join as join46, relative, dirname as dirname10 } from "node:path";
68010
68047
  import { homedir as homedir25 } from "node:os";
68011
68048
  import { spawn as spawn5 } from "node:child_process";
68012
68049
  import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
@@ -72726,19 +72763,19 @@ import {
72726
72763
  } from "node:fs";
72727
72764
  import { spawnSync as spawnSync4 } from "node:child_process";
72728
72765
  import { tmpdir as tmpdir4 } from "node:os";
72729
- import { join as join40 } from "node:path";
72766
+ import { join as join41 } from "node:path";
72730
72767
 
72731
72768
  class ConfigDiffError extends Error {
72732
72769
  }
72733
72770
  function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "git") {
72734
72771
  if (before === after)
72735
72772
  return "";
72736
- const dir = mkdtemp(join40(tmpdir4(), "switchroom-config-diff-"));
72773
+ const dir = mkdtemp(join41(tmpdir4(), "switchroom-config-diff-"));
72737
72774
  try {
72738
- mkdirSync26(join40(dir, "cur"), { recursive: true });
72739
- mkdirSync26(join40(dir, "new"), { recursive: true });
72740
- writeFileSync24(join40(dir, "cur", name), before);
72741
- writeFileSync24(join40(dir, "new", name), after);
72775
+ mkdirSync26(join41(dir, "cur"), { recursive: true });
72776
+ mkdirSync26(join41(dir, "new"), { recursive: true });
72777
+ writeFileSync24(join41(dir, "cur", name), before);
72778
+ writeFileSync24(join41(dir, "new", name), after);
72742
72779
  const r = spawnSync4(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
72743
72780
  if (r.status === 0)
72744
72781
  return "";
@@ -72772,7 +72809,7 @@ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "
72772
72809
  init_client4();
72773
72810
  import { existsSync as existsSync46 } from "node:fs";
72774
72811
  import { homedir as homedir22 } from "node:os";
72775
- import { join as join41 } from "node:path";
72812
+ import { join as join42 } from "node:path";
72776
72813
  var PROPOSE_TIMEOUT_MS = 11 * 60 * 1000;
72777
72814
  function resolveHostdOperatorSocket(env2 = process.env) {
72778
72815
  const override = env2.SWITCHROOM_HOSTD_OPERATOR_SOCKET;
@@ -72780,7 +72817,7 @@ function resolveHostdOperatorSocket(env2 = process.env) {
72780
72817
  return override;
72781
72818
  const candidates = [
72782
72819
  "/host-home/.switchroom/hostd/operator/sock",
72783
- join41(homedir22(), ".switchroom", "hostd", "operator", "sock")
72820
+ join42(homedir22(), ".switchroom", "hostd", "operator", "sock")
72784
72821
  ];
72785
72822
  for (const c of candidates) {
72786
72823
  if (existsSync46(c))
@@ -72876,7 +72913,7 @@ init_client2();
72876
72913
 
72877
72914
  // telegram-plugin/registry/turns-schema.ts
72878
72915
  import { chmodSync as chmodSync8, mkdirSync as mkdirSync27 } from "fs";
72879
- import { join as join42 } from "path";
72916
+ import { join as join43 } from "path";
72880
72917
  var DatabaseClass = null;
72881
72918
  function loadDatabaseClass() {
72882
72919
  if (DatabaseClass != null)
@@ -72939,9 +72976,9 @@ function applySchema(db) {
72939
72976
  }
72940
72977
  function openTurnsDb(agentDir) {
72941
72978
  const Database2 = loadDatabaseClass();
72942
- const dir = join42(agentDir, "telegram");
72979
+ const dir = join43(agentDir, "telegram");
72943
72980
  mkdirSync27(dir, { recursive: true, mode: 448 });
72944
- const path4 = join42(dir, "registry.db");
72981
+ const path4 = join43(dir, "registry.db");
72945
72982
  const db = new Database2(path4, { create: true });
72946
72983
  applySchema(db);
72947
72984
  try {
@@ -73736,7 +73773,7 @@ async function handleGetMemoryHealth(config, opts) {
73736
73773
 
73737
73774
  // src/web/webhook-handler.ts
73738
73775
  import { appendFileSync as appendFileSync4, existsSync as existsSync49, mkdirSync as mkdirSync28, readFileSync as readFileSync44, writeFileSync as writeFileSync25 } from "fs";
73739
- import { join as join44 } from "path";
73776
+ import { join as join45 } from "path";
73740
73777
  import { homedir as homedir24 } from "os";
73741
73778
 
73742
73779
  // src/web/webhook-verify.ts
@@ -73888,12 +73925,12 @@ function forwardToGateway(socketPath, req, opts = {}) {
73888
73925
 
73889
73926
  // src/web/webhook-edge.ts
73890
73927
  import { existsSync as existsSync48, readFileSync as readFileSync43 } from "fs";
73891
- import { join as join43 } from "path";
73928
+ import { join as join44 } from "path";
73892
73929
  import { homedir as homedir23 } from "os";
73893
73930
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
73894
73931
  var EDGE_HEADER = "x-switchroom-edge";
73895
73932
  function edgeSecretPath() {
73896
- return join43(homedir23(), ".switchroom", "webhook-edge-secret");
73933
+ return join44(homedir23(), ".switchroom", "webhook-edge-secret");
73897
73934
  }
73898
73935
  function loadEdgeSecret(path4) {
73899
73936
  const p = path4 ?? edgeSecretPath();
@@ -73956,8 +73993,8 @@ var agentDedupCache = new Map;
73956
73993
  function createFileDedupStore(resolveAgentDir) {
73957
73994
  return {
73958
73995
  check(agent, deliveryId, now) {
73959
- const telegramDir = join44(resolveAgentDir(agent), "telegram");
73960
- const filePath = join44(telegramDir, "webhook-dedup.json");
73996
+ const telegramDir = join45(resolveAgentDir(agent), "telegram");
73997
+ const filePath = join45(telegramDir, "webhook-dedup.json");
73961
73998
  if (!agentDedupCache.has(agent)) {
73962
73999
  agentDedupCache.set(agent, loadDedupFile(filePath));
73963
74000
  }
@@ -74009,7 +74046,7 @@ function shouldWriteThrottleIssue(agent, source, now, windowMap) {
74009
74046
  return true;
74010
74047
  }
74011
74048
  function writeThrottleIssue(agent, source, now, telegramDir, log) {
74012
- const issuesPath = join44(telegramDir, "issues.jsonl");
74049
+ const issuesPath = join45(telegramDir, "issues.jsonl");
74013
74050
  try {
74014
74051
  mkdirSync28(telegramDir, { recursive: true });
74015
74052
  const record = {
@@ -74034,7 +74071,7 @@ function writeThrottleIssue(agent, source, now, telegramDir, log) {
74034
74071
  async function handleWebhookIngest(args, deps = {}) {
74035
74072
  const log = deps.log ?? ((s) => process.stderr.write(s));
74036
74073
  const now = (deps.now ?? Date.now)();
74037
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join44(homedir24(), ".switchroom", "agents", a));
74074
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join45(homedir24(), ".switchroom", "agents", a));
74038
74075
  const rateLimiter = deps.rateLimiter ?? defaultRateLimiter;
74039
74076
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
74040
74077
  if (!args.agentExists) {
@@ -74098,7 +74135,7 @@ async function handleWebhookIngest(args, deps = {}) {
74098
74135
  if (retryAfter !== null) {
74099
74136
  if (!args.viaGateway) {
74100
74137
  const agentDir2 = resolveAgentDir(args.agent);
74101
- const telegramDir2 = join44(agentDir2, "telegram");
74138
+ const telegramDir2 = join45(agentDir2, "telegram");
74102
74139
  if (shouldWriteThrottleIssue(args.agent, source, now)) {
74103
74140
  writeThrottleIssue(args.agent, source, now, telegramDir2, log);
74104
74141
  }
@@ -74118,7 +74155,7 @@ async function handleWebhookIngest(args, deps = {}) {
74118
74155
  const eventType = source === "github" ? args.headers.get("x-github-event") ?? "unknown" : args.source;
74119
74156
  const rendered = source === "github" ? renderGithubEvent(eventType, payload) : renderGenericEvent(args.source, payload);
74120
74157
  if (args.viaGateway) {
74121
- const socketPath = join44(resolveAgentDir(args.agent), "telegram", "webhook.sock");
74158
+ const socketPath = join45(resolveAgentDir(args.agent), "telegram", "webhook.sock");
74122
74159
  const forward = deps.forwardFn ?? forwardToGateway;
74123
74160
  const deliveryId = source === "github" ? args.headers.get("x-github-delivery") ?? undefined : undefined;
74124
74161
  let resp;
@@ -74157,8 +74194,8 @@ async function handleWebhookIngest(args, deps = {}) {
74157
74194
  return jsonReply(202, { ok: true, recorded: true, ts: resp.ts });
74158
74195
  }
74159
74196
  const agentDir = resolveAgentDir(args.agent);
74160
- const telegramDir = join44(agentDir, "telegram");
74161
- const logPath = join44(telegramDir, "webhook-events.jsonl");
74197
+ const telegramDir = join45(agentDir, "telegram");
74198
+ const logPath = join45(telegramDir, "webhook-events.jsonl");
74162
74199
  try {
74163
74200
  mkdirSync28(telegramDir, { recursive: true });
74164
74201
  const record = {
@@ -74212,7 +74249,7 @@ function resolveWebToken() {
74212
74249
  if (fromEnv && fromEnv.length > 0)
74213
74250
  return fromEnv;
74214
74251
  const home2 = process.env.HOME ?? homedir25();
74215
- const tokenPath = join45(home2, ".switchroom", "web-token");
74252
+ const tokenPath = join46(home2, ".switchroom", "web-token");
74216
74253
  if (existsSync50(tokenPath)) {
74217
74254
  const existing = readFileSync45(tokenPath, "utf8").trim();
74218
74255
  if (existing.length > 0)
@@ -74297,7 +74334,7 @@ function checkWsAuth(req, token, server) {
74297
74334
  return presented !== null && constantTimeEqual(presented, token);
74298
74335
  }
74299
74336
  function loadWebhookSecrets() {
74300
- const path4 = join45(homedir25(), ".switchroom", "webhook-secrets.json");
74337
+ const path4 = join46(homedir25(), ".switchroom", "webhook-secrets.json");
74301
74338
  if (!existsSync50(path4))
74302
74339
  return {};
74303
74340
  try {
@@ -74633,7 +74670,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
74633
74670
  }
74634
74671
  }
74635
74672
  let filePath = pathname === "/" ? "/index.html" : pathname;
74636
- const fullPath = join45(uiDir, filePath);
74673
+ const fullPath = join46(uiDir, filePath);
74637
74674
  if (!existsSync50(fullPath)) {
74638
74675
  return new Response("Not Found", { status: 404 });
74639
74676
  }
@@ -75678,15 +75715,15 @@ init_loader();
75678
75715
  init_lifecycle();
75679
75716
  import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync51, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
75680
75717
  import { spawnSync as spawnSync9 } from "node:child_process";
75681
- import { join as join59, dirname as dirname14, resolve as resolve33 } from "node:path";
75718
+ import { join as join60, dirname as dirname14, resolve as resolve33 } from "node:path";
75682
75719
  import { homedir as homedir36 } from "node:os";
75683
- var DEFAULT_COMPOSE_PATH = join59(homedir36(), ".switchroom", "compose", "docker-compose.yml");
75720
+ var DEFAULT_COMPOSE_PATH = join60(homedir36(), ".switchroom", "compose", "docker-compose.yml");
75684
75721
  function runningFromSwitchroomCheckout(scriptPath) {
75685
75722
  let dir = dirname14(scriptPath);
75686
75723
  for (let i = 0;i < 12; i++) {
75687
- if (existsSync58(join59(dir, ".git"))) {
75724
+ if (existsSync58(join60(dir, ".git"))) {
75688
75725
  try {
75689
- const pkg = JSON.parse(readFileSync51(join59(dir, "package.json"), "utf-8"));
75726
+ const pkg = JSON.parse(readFileSync51(join60(dir, "package.json"), "utf-8"));
75690
75727
  if (pkg.name === "switchroom")
75691
75728
  return true;
75692
75729
  } catch {}
@@ -75837,7 +75874,7 @@ function planUpdate(opts) {
75837
75874
  return;
75838
75875
  }
75839
75876
  const source = resolve33(import.meta.dirname, "../../skills");
75840
- const dest = join59(homedir36(), ".switchroom", "skills", "_bundled");
75877
+ const dest = join60(homedir36(), ".switchroom", "skills", "_bundled");
75841
75878
  if (!existsSync58(source)) {
75842
75879
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
75843
75880
  `);
@@ -75951,7 +75988,7 @@ function defaultStatusProbe(composePath) {
75951
75988
  } catch {}
75952
75989
  let dir = dirname14(scriptPath);
75953
75990
  for (let i = 0;i < 8; i++) {
75954
- const pkgPath = join59(dir, "package.json");
75991
+ const pkgPath = join60(dir, "package.json");
75955
75992
  if (existsSync58(pkgPath)) {
75956
75993
  try {
75957
75994
  const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
@@ -76171,7 +76208,7 @@ init_helpers();
76171
76208
  init_lifecycle();
76172
76209
  import { execSync as execSync4 } from "node:child_process";
76173
76210
  import { existsSync as existsSync59, readFileSync as readFileSync52 } from "node:fs";
76174
- import { dirname as dirname15, join as join60 } from "node:path";
76211
+ import { dirname as dirname15, join as join61 } from "node:path";
76175
76212
  function getClaudeCodeVersion() {
76176
76213
  try {
76177
76214
  const out = execSync4("claude --version 2>/dev/null", {
@@ -76221,11 +76258,11 @@ function formatUptime3(timestamp) {
76221
76258
  function locateSwitchroomInstallDir() {
76222
76259
  let dir = import.meta.dirname;
76223
76260
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
76224
- const pkgPath = join60(dir, "package.json");
76261
+ const pkgPath = join61(dir, "package.json");
76225
76262
  if (existsSync59(pkgPath)) {
76226
76263
  try {
76227
76264
  const pkg = JSON.parse(readFileSync52(pkgPath, "utf-8"));
76228
- if (pkg.name === "switchroom" && existsSync59(join60(dir, ".git"))) {
76265
+ if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
76229
76266
  return dir;
76230
76267
  }
76231
76268
  } catch {}
@@ -76462,7 +76499,7 @@ import {
76462
76499
  writeFileSync as writeFileSync27,
76463
76500
  writeSync as writeSync7
76464
76501
  } from "node:fs";
76465
- import { join as join61 } from "node:path";
76502
+ import { join as join62 } from "node:path";
76466
76503
  import { randomBytes as randomBytes12 } from "node:crypto";
76467
76504
  import { execSync as execSync5 } from "node:child_process";
76468
76505
 
@@ -76860,7 +76897,7 @@ function redactedMarker(ruleId) {
76860
76897
  var ISSUES_FILE = "issues.jsonl";
76861
76898
  var ISSUES_LOCK = "issues.lock";
76862
76899
  function readAll(stateDir) {
76863
- const path4 = join61(stateDir, ISSUES_FILE);
76900
+ const path4 = join62(stateDir, ISSUES_FILE);
76864
76901
  if (!existsSync60(path4))
76865
76902
  return [];
76866
76903
  let raw;
@@ -76938,7 +76975,7 @@ function record(stateDir, input, nowFn = Date.now) {
76938
76975
  });
76939
76976
  }
76940
76977
  function resolve36(stateDir, fingerprint, nowFn = Date.now) {
76941
- if (!existsSync60(join61(stateDir, ISSUES_FILE)))
76978
+ if (!existsSync60(join62(stateDir, ISSUES_FILE)))
76942
76979
  return 0;
76943
76980
  return withLock(stateDir, () => {
76944
76981
  const all = readAll(stateDir);
@@ -76956,7 +76993,7 @@ function resolve36(stateDir, fingerprint, nowFn = Date.now) {
76956
76993
  });
76957
76994
  }
76958
76995
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
76959
- if (!existsSync60(join61(stateDir, ISSUES_FILE)))
76996
+ if (!existsSync60(join62(stateDir, ISSUES_FILE)))
76960
76997
  return 0;
76961
76998
  return withLock(stateDir, () => {
76962
76999
  const all = readAll(stateDir);
@@ -76974,7 +77011,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
76974
77011
  });
76975
77012
  }
76976
77013
  function prune(stateDir, opts = {}) {
76977
- if (!existsSync60(join61(stateDir, ISSUES_FILE)))
77014
+ if (!existsSync60(join62(stateDir, ISSUES_FILE)))
76978
77015
  return 0;
76979
77016
  return withLock(stateDir, () => {
76980
77017
  const all = readAll(stateDir);
@@ -77007,7 +77044,7 @@ function ensureDir(stateDir) {
77007
77044
  mkdirSync33(stateDir, { recursive: true });
77008
77045
  }
77009
77046
  function writeAll(stateDir, events) {
77010
- const path4 = join61(stateDir, ISSUES_FILE);
77047
+ const path4 = join62(stateDir, ISSUES_FILE);
77011
77048
  sweepOrphanTmpFiles(stateDir);
77012
77049
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
77013
77050
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -77029,7 +77066,7 @@ function sweepOrphanTmpFiles(stateDir) {
77029
77066
  for (const entry of entries) {
77030
77067
  if (!entry.startsWith(TMP_PREFIX))
77031
77068
  continue;
77032
- const tmpPath = join61(stateDir, entry);
77069
+ const tmpPath = join62(stateDir, entry);
77033
77070
  try {
77034
77071
  const stat = statSync27(tmpPath);
77035
77072
  if (stat.mtimeMs < cutoff) {
@@ -77041,7 +77078,7 @@ function sweepOrphanTmpFiles(stateDir) {
77041
77078
  var LOCK_RETRY_MS = 25;
77042
77079
  var LOCK_TIMEOUT_MS = 1e4;
77043
77080
  function withLock(stateDir, fn) {
77044
- const lockPath = join61(stateDir, ISSUES_LOCK);
77081
+ const lockPath = join62(stateDir, ISSUES_LOCK);
77045
77082
  const startedAt = Date.now();
77046
77083
  let fd = null;
77047
77084
  while (fd === null) {
@@ -77326,7 +77363,7 @@ function relTime(deltaMs) {
77326
77363
  init_source();
77327
77364
  import { existsSync as existsSync63 } from "node:fs";
77328
77365
  import { homedir as homedir39 } from "node:os";
77329
- import { join as join64, resolve as resolve37 } from "node:path";
77366
+ import { join as join65, resolve as resolve37 } from "node:path";
77330
77367
 
77331
77368
  // src/deps/python.ts
77332
77369
  import { createHash as createHash11 } from "node:crypto";
@@ -77337,7 +77374,7 @@ import {
77337
77374
  rmSync as rmSync13,
77338
77375
  writeFileSync as writeFileSync28
77339
77376
  } from "node:fs";
77340
- import { dirname as dirname16, join as join62 } from "node:path";
77377
+ import { dirname as dirname16, join as join63 } from "node:path";
77341
77378
  import { homedir as homedir37 } from "node:os";
77342
77379
  import { execFileSync as execFileSync19 } from "node:child_process";
77343
77380
 
@@ -77350,7 +77387,7 @@ class PythonEnvError extends Error {
77350
77387
  }
77351
77388
  }
77352
77389
  function defaultPythonCacheRoot() {
77353
- return join62(homedir37(), ".switchroom", "deps", "python");
77390
+ return join63(homedir37(), ".switchroom", "deps", "python");
77354
77391
  }
77355
77392
  function hashFile(path4) {
77356
77393
  return createHash11("sha256").update(readFileSync54(path4)).digest("hex");
@@ -77362,11 +77399,11 @@ function ensurePythonEnv(opts) {
77362
77399
  if (!existsSync61(requirementsPath)) {
77363
77400
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
77364
77401
  }
77365
- const venvDir = join62(cacheRoot, skillName);
77366
- const stampPath = join62(venvDir, ".requirements.sha256");
77367
- const binDir = join62(venvDir, "bin");
77368
- const pythonBin = join62(binDir, "python");
77369
- const pipBin = join62(binDir, "pip");
77402
+ const venvDir = join63(cacheRoot, skillName);
77403
+ const stampPath = join63(venvDir, ".requirements.sha256");
77404
+ const binDir = join63(venvDir, "bin");
77405
+ const pythonBin = join63(binDir, "python");
77406
+ const pipBin = join63(binDir, "pip");
77370
77407
  const targetHash = hashFile(requirementsPath);
77371
77408
  if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
77372
77409
  const existingHash = readFileSync54(stampPath, "utf8").trim();
@@ -77425,7 +77462,7 @@ import {
77425
77462
  rmSync as rmSync14,
77426
77463
  writeFileSync as writeFileSync29
77427
77464
  } from "node:fs";
77428
- import { dirname as dirname17, join as join63 } from "node:path";
77465
+ import { dirname as dirname17, join as join64 } from "node:path";
77429
77466
  import { homedir as homedir38 } from "node:os";
77430
77467
  import { execFileSync as execFileSync20 } from "node:child_process";
77431
77468
 
@@ -77449,7 +77486,7 @@ var LOCKFILES_FOR = {
77449
77486
  npm: ["package-lock.json"]
77450
77487
  };
77451
77488
  function defaultNodeCacheRoot() {
77452
- return join63(homedir38(), ".switchroom", "deps", "node");
77489
+ return join64(homedir38(), ".switchroom", "deps", "node");
77453
77490
  }
77454
77491
  function hashDepInputs(packageJsonPath) {
77455
77492
  const sourceDir = dirname17(packageJsonPath);
@@ -77458,7 +77495,7 @@ function hashDepInputs(packageJsonPath) {
77458
77495
  `);
77459
77496
  hasher.update(readFileSync55(packageJsonPath));
77460
77497
  for (const lockName of ALL_LOCKFILES) {
77461
- const lockPath = join63(sourceDir, lockName);
77498
+ const lockPath = join64(sourceDir, lockName);
77462
77499
  if (existsSync62(lockPath)) {
77463
77500
  hasher.update(`
77464
77501
  `);
@@ -77478,10 +77515,10 @@ function ensureNodeEnv(opts) {
77478
77515
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
77479
77516
  }
77480
77517
  const sourceDir = dirname17(packageJsonPath);
77481
- const envDir = join63(cacheRoot, skillName);
77482
- const stampPath = join63(envDir, ".package.sha256");
77483
- const nodeModulesDir = join63(envDir, "node_modules");
77484
- const binDir = join63(nodeModulesDir, ".bin");
77518
+ const envDir = join64(cacheRoot, skillName);
77519
+ const stampPath = join64(envDir, ".package.sha256");
77520
+ const nodeModulesDir = join64(envDir, "node_modules");
77521
+ const binDir = join64(nodeModulesDir, ".bin");
77485
77522
  const targetHash = hashDepInputs(packageJsonPath);
77486
77523
  if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
77487
77524
  const existingHash = readFileSync55(stampPath, "utf8").trim();
@@ -77499,12 +77536,12 @@ function ensureNodeEnv(opts) {
77499
77536
  rmSync14(envDir, { recursive: true, force: true });
77500
77537
  }
77501
77538
  mkdirSync35(envDir, { recursive: true });
77502
- copyFileSync9(packageJsonPath, join63(envDir, "package.json"));
77539
+ copyFileSync9(packageJsonPath, join64(envDir, "package.json"));
77503
77540
  let copiedLockfile = false;
77504
77541
  for (const lockName of LOCKFILES_FOR[installer]) {
77505
- const lockPath = join63(sourceDir, lockName);
77542
+ const lockPath = join64(sourceDir, lockName);
77506
77543
  if (existsSync62(lockPath)) {
77507
- copyFileSync9(lockPath, join63(envDir, lockName));
77544
+ copyFileSync9(lockPath, join64(envDir, lockName));
77508
77545
  copiedLockfile = true;
77509
77546
  }
77510
77547
  }
@@ -77543,13 +77580,13 @@ function registerDepsCommand(program3) {
77543
77580
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
77544
77581
  process.exit(1);
77545
77582
  }
77546
- const skillDir = join64(skillsRoot, skill);
77583
+ const skillDir = join65(skillsRoot, skill);
77547
77584
  if (!existsSync63(skillDir)) {
77548
77585
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
77549
77586
  process.exit(1);
77550
77587
  }
77551
- const requirementsPath = join64(skillDir, "requirements.txt");
77552
- const packageJsonPath = join64(skillDir, "package.json");
77588
+ const requirementsPath = join65(skillDir, "requirements.txt");
77589
+ const packageJsonPath = join65(skillDir, "package.json");
77553
77590
  const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync63(requirementsPath));
77554
77591
  const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync63(packageJsonPath));
77555
77592
  let did = 0;
@@ -77826,7 +77863,6 @@ var DEFAULT_SOUL_FILENAME = "SOUL.md";
77826
77863
  var DEFAULT_TOOLS_FILENAME = "TOOLS.md";
77827
77864
  var DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
77828
77865
  var DEFAULT_USER_FILENAME = "USER.md";
77829
- var DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
77830
77866
  var DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
77831
77867
  var DEFAULT_MEMORY_FILENAME = "MEMORY.md";
77832
77868
 
@@ -77843,8 +77879,7 @@ var STABLE_BOOTSTRAP_FILENAMES = [
77843
77879
  DEFAULT_BOOTSTRAP_FILENAME
77844
77880
  ];
77845
77881
  var DYNAMIC_BOOTSTRAP_FILENAMES = [
77846
- DEFAULT_MEMORY_FILENAME,
77847
- DEFAULT_HEARTBEAT_FILENAME
77882
+ DEFAULT_MEMORY_FILENAME
77848
77883
  ];
77849
77884
  var DEFAULT_BOOTSTRAP_MAX_CHARS = 12000;
77850
77885
  var DEFAULT_BOOTSTRAP_TOTAL_MAX_CHARS = 64000;
@@ -78258,7 +78293,7 @@ function registerWorkspaceCommand(program3) {
78258
78293
  process.stdout.write(`${dir}
78259
78294
  `);
78260
78295
  }));
78261
- cmd.command("render <agent>").description("Render the agent's workspace bootstrap block to stdout " + "(used by start.sh and the UserPromptSubmit hook)").option("--stable", "Render only stable files (AGENTS/SOUL/USER/IDENTITY/TOOLS/BOOTSTRAP)").option("--dynamic", "Render only dynamic files (MEMORY + today/yesterday daily + HEARTBEAT)").option("--warning-mode <mode>", "Truncation warning mode: off | once | always | error (default: off for start.sh use)", "off").option("--max-per-file <n>", "Per-file char cap", String(DEFAULT_BOOTSTRAP_MAX_CHARS)).option("--max-total <n>", "Total char cap (defaults differ for stable vs dynamic)").action(withConfigError(async (agentName, opts) => {
78296
+ cmd.command("render <agent>").description("Render the agent's workspace bootstrap block to stdout " + "(used by start.sh and the UserPromptSubmit hook)").option("--stable", "Render only stable files (AGENTS/SOUL/USER/IDENTITY/TOOLS/BOOTSTRAP)").option("--dynamic", "Render only dynamic files (MEMORY + today/yesterday daily)").option("--warning-mode <mode>", "Truncation warning mode: off | once | always | error (default: off for start.sh use)", "off").option("--max-per-file <n>", "Per-file char cap", String(DEFAULT_BOOTSTRAP_MAX_CHARS)).option("--max-total <n>", "Total char cap (defaults differ for stable vs dynamic)").action(withConfigError(async (agentName, opts) => {
78262
78297
  const dir = resolveAgentWorkspaceDirOrExit(program3, agentName);
78263
78298
  if (!dir)
78264
78299
  return;
@@ -78506,7 +78541,7 @@ init_helpers();
78506
78541
  init_loader();
78507
78542
  init_merge();
78508
78543
  import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync56, writeFileSync as writeFileSync30 } from "node:fs";
78509
- import { join as join65, resolve as resolve39 } from "node:path";
78544
+ import { join as join66, resolve as resolve39 } from "node:path";
78510
78545
  init_schema();
78511
78546
  function resolveSoulTargetOrExit(program3, agentName) {
78512
78547
  const config = getConfig(program3);
@@ -78530,7 +78565,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
78530
78565
  profileName,
78531
78566
  profilePath,
78532
78567
  workspaceDir,
78533
- soulPath: join65(workspaceDir, "SOUL.md"),
78568
+ soulPath: join66(workspaceDir, "SOUL.md"),
78534
78569
  soul: merged.soul
78535
78570
  };
78536
78571
  }
@@ -78597,7 +78632,7 @@ function registerSoulCommand(program3) {
78597
78632
  init_helpers();
78598
78633
  init_loader();
78599
78634
  import { existsSync as existsSync66, readFileSync as readFileSync57, readdirSync as readdirSync23, statSync as statSync28 } from "node:fs";
78600
- import { resolve as resolve40, join as join66 } from "node:path";
78635
+ import { resolve as resolve40, join as join67 } from "node:path";
78601
78636
  import { createHash as createHash13 } from "node:crypto";
78602
78637
  init_merge();
78603
78638
  init_hindsight();
@@ -78608,7 +78643,7 @@ function estimateTokens(bytes) {
78608
78643
  return Math.round(bytes / 3.7);
78609
78644
  }
78610
78645
  function readMcpServerNames(agentDir) {
78611
- const mcpPath = join66(agentDir, ".mcp.json");
78646
+ const mcpPath = join67(agentDir, ".mcp.json");
78612
78647
  if (!existsSync66(mcpPath))
78613
78648
  return [];
78614
78649
  try {
@@ -78622,7 +78657,7 @@ function sha256(content) {
78622
78657
  return createHash13("sha256").update(content).digest("hex").slice(0, 16);
78623
78658
  }
78624
78659
  function findLatestTranscriptJsonl(claudeConfigDir) {
78625
- const projectsDir = join66(claudeConfigDir, "projects");
78660
+ const projectsDir = join67(claudeConfigDir, "projects");
78626
78661
  if (!existsSync66(projectsDir))
78627
78662
  return;
78628
78663
  try {
@@ -78631,8 +78666,8 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
78631
78666
  for (const entry of entries) {
78632
78667
  if (!entry.isDirectory())
78633
78668
  continue;
78634
- const projectPath = join66(projectsDir, entry.name);
78635
- const transcriptPath = join66(projectPath, "transcript.jsonl");
78669
+ const projectPath = join67(projectsDir, entry.name);
78670
+ const transcriptPath = join67(projectPath, "transcript.jsonl");
78636
78671
  if (!existsSync66(transcriptPath))
78637
78672
  continue;
78638
78673
  const stat3 = statSync28(transcriptPath);
@@ -78701,11 +78736,11 @@ function registerDebugCommand(program3) {
78701
78736
  process.exit(1);
78702
78737
  }
78703
78738
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
78704
- const claudeConfigDir = join66(agentDir, ".claude");
78705
- const claudeMdPath = join66(agentDir, "CLAUDE.md");
78706
- const soulMdPath = join66(agentDir, "SOUL.md");
78707
- const workspaceSoulMdPath = join66(workspaceDir, "SOUL.md");
78708
- const handoffPath = join66(agentDir, ".handoff.md");
78739
+ const claudeConfigDir = join67(agentDir, ".claude");
78740
+ const claudeMdPath = join67(agentDir, "CLAUDE.md");
78741
+ const soulMdPath = join67(agentDir, "SOUL.md");
78742
+ const workspaceSoulMdPath = join67(workspaceDir, "SOUL.md");
78743
+ const handoffPath = join67(agentDir, ".handoff.md");
78709
78744
  const lastN = parseInt(opts.last, 10);
78710
78745
  if (isNaN(lastN) || lastN < 1) {
78711
78746
  console.error("--last must be a positive integer");
@@ -78834,9 +78869,9 @@ function registerDebugCommand(program3) {
78834
78869
  const soulMdBytes = soulMdContent.length;
78835
78870
  const perTurnBytes = dynamicResult.concatenated.length;
78836
78871
  const userBytes = userMessage?.text.length ?? 0;
78837
- const fleetDir = join66(agentsDir, "..", "fleet");
78838
- const fleetInvPath = join66(fleetDir, "switchroom-invariants.md");
78839
- const fleetClaudePath = join66(fleetDir, "CLAUDE.md");
78872
+ const fleetDir = join67(agentsDir, "..", "fleet");
78873
+ const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
78874
+ const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
78840
78875
  const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync57(fleetInvPath, "utf-8").length : 0;
78841
78876
  const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync57(fleetClaudePath, "utf-8").length : 0;
78842
78877
  const fleetBytes = fleetInvBytes + fleetClaudeBytes;
@@ -78872,7 +78907,7 @@ init_source();
78872
78907
  // src/worktree/claim.ts
78873
78908
  import { execFileSync as execFileSync21 } from "node:child_process";
78874
78909
  import { closeSync as closeSync12, mkdirSync as mkdirSync37, openSync as openSync12, existsSync as existsSync68, unlinkSync as unlinkSync13 } from "node:fs";
78875
- import { join as join68, resolve as resolve42 } from "node:path";
78910
+ import { join as join69, resolve as resolve42 } from "node:path";
78876
78911
  import { homedir as homedir41 } from "node:os";
78877
78912
  import { randomBytes as randomBytes13 } from "node:crypto";
78878
78913
 
@@ -78886,13 +78921,13 @@ import {
78886
78921
  existsSync as existsSync67,
78887
78922
  renameSync as renameSync13
78888
78923
  } from "node:fs";
78889
- import { join as join67, resolve as resolve41 } from "node:path";
78924
+ import { join as join68, resolve as resolve41 } from "node:path";
78890
78925
  import { homedir as homedir40 } from "node:os";
78891
78926
  function registryDir() {
78892
- return resolve41(process.env.SWITCHROOM_WORKTREE_DIR ?? join67(homedir40(), ".switchroom", "worktrees"));
78927
+ return resolve41(process.env.SWITCHROOM_WORKTREE_DIR ?? join68(homedir40(), ".switchroom", "worktrees"));
78893
78928
  }
78894
78929
  function recordPath(id) {
78895
- return join67(registryDir(), `${id}.json`);
78930
+ return join68(registryDir(), `${id}.json`);
78896
78931
  }
78897
78932
  function ensureDir2() {
78898
78933
  mkdirSync36(registryDir(), { recursive: true });
@@ -78943,7 +78978,7 @@ function acquireRepoLock(repoPath) {
78943
78978
  const lockDir = registryDir();
78944
78979
  mkdirSync37(lockDir, { recursive: true });
78945
78980
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
78946
- const lockPath = join68(lockDir, `.lock-${lockName}`);
78981
+ const lockPath = join69(lockDir, `.lock-${lockName}`);
78947
78982
  const deadline = Date.now() + 5000;
78948
78983
  let fd = null;
78949
78984
  while (fd === null) {
@@ -78970,7 +79005,7 @@ function acquireRepoLock(repoPath) {
78970
79005
  }
78971
79006
  var DEFAULT_CONCURRENCY = 5;
78972
79007
  function worktreesBaseDir() {
78973
- return resolve42(process.env.SWITCHROOM_WORKTREE_BASE ?? join68(homedir41(), ".switchroom", "worktree-checkouts"));
79008
+ return resolve42(process.env.SWITCHROOM_WORKTREE_BASE ?? join69(homedir41(), ".switchroom", "worktree-checkouts"));
78974
79009
  }
78975
79010
  function shortId() {
78976
79011
  return randomBytes13(4).toString("hex");
@@ -78992,7 +79027,7 @@ function resolveRepoPath(repo, codeRepos) {
78992
79027
  }
78993
79028
  function expandHome(p) {
78994
79029
  if (p.startsWith("~/"))
78995
- return join68(homedir41(), p.slice(2));
79030
+ return join69(homedir41(), p.slice(2));
78996
79031
  return p;
78997
79032
  }
78998
79033
  async function claimWorktree(input, codeRepos) {
@@ -79020,7 +79055,7 @@ async function claimWorktree(input, codeRepos) {
79020
79055
  branch = `task/${taskSuffix}-${id}`;
79021
79056
  const baseDir = worktreesBaseDir();
79022
79057
  mkdirSync37(baseDir, { recursive: true });
79023
- worktreePath = join68(baseDir, `${id}-${taskSuffix}`);
79058
+ worktreePath = join69(baseDir, `${id}-${taskSuffix}`);
79024
79059
  const now = new Date().toISOString();
79025
79060
  const record2 = {
79026
79061
  id,
@@ -79275,7 +79310,7 @@ import {
79275
79310
  rmSync as rmSync15,
79276
79311
  writeFileSync as writeFileSync32
79277
79312
  } from "node:fs";
79278
- import { join as join69 } from "node:path";
79313
+ import { join as join70 } from "node:path";
79279
79314
  function encodeCredentialsFilename(email) {
79280
79315
  const SAFE = new Set([
79281
79316
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -79465,16 +79500,16 @@ function resolveCredentialsDir(env2) {
79465
79500
  if (explicit && explicit.length > 0)
79466
79501
  return explicit;
79467
79502
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
79468
- return join69(stateBase, "google-workspace-mcp", "credentials");
79503
+ return join70(stateBase, "google-workspace-mcp", "credentials");
79469
79504
  }
79470
79505
  function writeSeedFile(dir, email, seed) {
79471
79506
  mkdirSync38(dir, { recursive: true, mode: 448 });
79472
79507
  chmodSync9(dir, 448);
79473
79508
  for (const name of readdirSync25(dir)) {
79474
- rmSync15(join69(dir, name), { force: true, recursive: true });
79509
+ rmSync15(join70(dir, name), { force: true, recursive: true });
79475
79510
  }
79476
79511
  const filename = encodeCredentialsFilename(email);
79477
- const filePath = join69(dir, filename);
79512
+ const filePath = join70(dir, filename);
79478
79513
  writeFileSync32(filePath, JSON.stringify(seed), { mode: 384 });
79479
79514
  chmodSync9(filePath, 384);
79480
79515
  return filePath;
@@ -79634,7 +79669,7 @@ function registerDriveMcpLauncherCommand(program3) {
79634
79669
  init_scaffold_integration();
79635
79670
  import { spawn as spawn6 } from "node:child_process";
79636
79671
  import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync39 } from "node:fs";
79637
- import { dirname as dirname18, join as join70 } from "node:path";
79672
+ import { dirname as dirname18, join as join71 } from "node:path";
79638
79673
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
79639
79674
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
79640
79675
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -79666,7 +79701,7 @@ function writeRefreshHeartbeat(agentName, data) {
79666
79701
  function heartbeatPath(agentName) {
79667
79702
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
79668
79703
  if (override) {
79669
- return join70(override, `m365-launcher-${agentName}.heartbeat.json`);
79704
+ return join71(override, `m365-launcher-${agentName}.heartbeat.json`);
79670
79705
  }
79671
79706
  return "/state/agent/m365-launcher.heartbeat.json";
79672
79707
  }
@@ -79976,10 +80011,10 @@ function registerNotionMcpLauncherCommand(program3) {
79976
80011
  // src/cli/deliver-file.ts
79977
80012
  init_client2();
79978
80013
  import { readFileSync as readFileSync59, statSync as statSync29 } from "node:fs";
79979
- import { basename as basename7 } from "node:path";
80014
+ import { basename as basename8 } from "node:path";
79980
80015
 
79981
80016
  // src/delivery/onedrive.ts
79982
- import { basename as basename5 } from "node:path";
80017
+ import { basename as basename6 } from "node:path";
79983
80018
  var GRAPH = "https://graph.microsoft.com/v1.0";
79984
80019
  var ONEDRIVE_INLINE_MAX_BYTES = 4 * 1024 * 1024;
79985
80020
  function authHeaders(token) {
@@ -80102,14 +80137,14 @@ async function createShareLink(deps, item, scopes = ["anonymous", "organization"
80102
80137
  async function deliverToOneDrive(args) {
80103
80138
  const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
80104
80139
  const folder = await ensureSwitchroomFolder(deps, args.agentName);
80105
- const filename = basename5(args.localPath);
80140
+ const filename = basename6(args.localPath);
80106
80141
  const item = await uploadFile(deps, folder.id, filename, args.bytes);
80107
80142
  const link = await createShareLink(deps, item, args.linkScopes);
80108
80143
  return { itemId: item.id, link, folderPath: `Switchroom/${args.agentName}` };
80109
80144
  }
80110
80145
 
80111
80146
  // src/delivery/gdrive.ts
80112
- import { basename as basename6 } from "node:path";
80147
+ import { basename as basename7 } from "node:path";
80113
80148
  var DRIVE = "https://www.googleapis.com/drive/v3";
80114
80149
  var UPLOAD = "https://www.googleapis.com/upload/drive/v3";
80115
80150
  var FOLDER_MIME = "application/vnd.google-apps.folder";
@@ -80257,7 +80292,7 @@ async function createShareLink2(deps, file, scopes = ["anyone"]) {
80257
80292
  async function deliverToGoogleDrive(args) {
80258
80293
  const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
80259
80294
  const folder = await ensureSwitchroomFolder2(deps, args.agentName);
80260
- const filename = basename6(args.localPath);
80295
+ const filename = basename7(args.localPath);
80261
80296
  const file = await uploadFile2(deps, folder.id, filename, args.bytes);
80262
80297
  const link = await createShareLink2(deps, file, args.linkScopes);
80263
80298
  return { itemId: file.id, link, folderPath: `Switchroom/${args.agentName}` };
@@ -80336,7 +80371,7 @@ async function runDeliverFile(localPath, deps = {}) {
80336
80371
  try {
80337
80372
  const bytes = read(localPath);
80338
80373
  const out = await provider.deliver({ agentName, localPath, bytes });
80339
- return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename7(localPath) };
80374
+ return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename8(localPath) };
80340
80375
  } catch (err) {
80341
80376
  return { ok: false, provider: provider.name, error: `upload failed: ${err.message}` };
80342
80377
  }
@@ -80994,7 +81029,7 @@ agents:
80994
81029
 
80995
81030
  // src/cli/apply.ts
80996
81031
  init_resolver();
80997
- import { dirname as dirname22, join as join73, resolve as resolve44 } from "node:path";
81032
+ import { dirname as dirname22, join as join74, resolve as resolve44 } from "node:path";
80998
81033
  import { homedir as homedir43 } from "node:os";
80999
81034
  import { execFileSync as execFileSync24 } from "node:child_process";
81000
81035
  init_vault();
@@ -81003,7 +81038,7 @@ init_loader();
81003
81038
 
81004
81039
  // src/cli/update-prompt-hook.ts
81005
81040
  import { existsSync as existsSync72, readFileSync as readFileSync60, writeFileSync as writeFileSync35, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
81006
- import { join as join71 } from "node:path";
81041
+ import { join as join72 } from "node:path";
81007
81042
  var HOOK_FILENAME = "update-card-on-prompt.sh";
81008
81043
  function updatePromptHookScript() {
81009
81044
  return `#!/bin/bash
@@ -81069,9 +81104,9 @@ exit 0
81069
81104
  `;
81070
81105
  }
81071
81106
  function installUpdatePromptHook(agentDir) {
81072
- const hooksDir = join71(agentDir, ".claude", "hooks");
81107
+ const hooksDir = join72(agentDir, ".claude", "hooks");
81073
81108
  mkdirSync41(hooksDir, { recursive: true });
81074
- const scriptPath = join71(hooksDir, HOOK_FILENAME);
81109
+ const scriptPath = join72(hooksDir, HOOK_FILENAME);
81075
81110
  const desired = updatePromptHookScript();
81076
81111
  let installed = false;
81077
81112
  const existing = existsSync72(scriptPath) ? readFileSync60(scriptPath, "utf-8") : "";
@@ -81084,7 +81119,7 @@ function installUpdatePromptHook(agentDir) {
81084
81119
  chmodSync10(scriptPath, 493);
81085
81120
  } catch {}
81086
81121
  }
81087
- const settingsPath = join71(agentDir, ".claude", "settings.json");
81122
+ const settingsPath = join72(agentDir, ".claude", "settings.json");
81088
81123
  if (!existsSync72(settingsPath)) {
81089
81124
  return { scriptPath, settingsPath, installed };
81090
81125
  }
@@ -81186,14 +81221,14 @@ var EMBEDDED_EXAMPLES = {
81186
81221
  switchroom: switchroom_default,
81187
81222
  minimal: minimal_default
81188
81223
  };
81189
- var DEFAULT_COMPOSE_PATH2 = join73(homedir43(), ".switchroom", "compose", "docker-compose.yml");
81224
+ var DEFAULT_COMPOSE_PATH2 = join74(homedir43(), ".switchroom", "compose", "docker-compose.yml");
81190
81225
  var COMPOSE_PROJECT2 = "switchroom";
81191
81226
  function resolveVaultBindMountDir(homeDir, ctx) {
81192
81227
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
81193
81228
  if (isCustomPath && ctx.customVaultPath) {
81194
81229
  return dirname22(ctx.customVaultPath);
81195
81230
  }
81196
- return join73(homeDir, ".switchroom", "vault");
81231
+ return join74(homeDir, ".switchroom", "vault");
81197
81232
  }
81198
81233
  function inspectVaultBindMountDir(vaultDir) {
81199
81234
  if (!existsSync74(vaultDir))
@@ -81224,42 +81259,42 @@ function hasVaultRefs(value) {
81224
81259
  async function ensureHostMountSources(config) {
81225
81260
  const home2 = homedir43();
81226
81261
  const dirs = [
81227
- join73(home2, ".switchroom", "approvals"),
81228
- join73(home2, ".switchroom", "scheduler"),
81229
- join73(home2, ".switchroom", "logs"),
81230
- join73(home2, ".switchroom", "compose"),
81231
- join73(home2, ".switchroom", "broker-operator")
81262
+ join74(home2, ".switchroom", "approvals"),
81263
+ join74(home2, ".switchroom", "scheduler"),
81264
+ join74(home2, ".switchroom", "logs"),
81265
+ join74(home2, ".switchroom", "compose"),
81266
+ join74(home2, ".switchroom", "broker-operator")
81232
81267
  ];
81233
81268
  for (const name of Object.keys(config.agents)) {
81234
- dirs.push(join73(home2, ".switchroom", "agents", name));
81235
- dirs.push(join73(home2, ".switchroom", "logs", name));
81236
- dirs.push(join73(home2, ".claude", "projects", name));
81237
- dirs.push(join73(home2, ".switchroom", "audit", name));
81238
- if (existsSync74(join73(home2, ".switchroom-config"))) {
81239
- dirs.push(join73(home2, ".switchroom-config", "agents", name, "personal-skills"));
81269
+ dirs.push(join74(home2, ".switchroom", "agents", name));
81270
+ dirs.push(join74(home2, ".switchroom", "logs", name));
81271
+ dirs.push(join74(home2, ".claude", "projects", name));
81272
+ dirs.push(join74(home2, ".switchroom", "audit", name));
81273
+ if (existsSync74(join74(home2, ".switchroom-config"))) {
81274
+ dirs.push(join74(home2, ".switchroom-config", "agents", name, "personal-skills"));
81240
81275
  }
81241
81276
  }
81242
81277
  for (const dir of dirs) {
81243
81278
  await mkdir2(dir, { recursive: true });
81244
81279
  }
81245
- const autoUnlockPath = join73(home2, ".switchroom", "vault-auto-unlock");
81280
+ const autoUnlockPath = join74(home2, ".switchroom", "vault-auto-unlock");
81246
81281
  if (!existsSync74(autoUnlockPath)) {
81247
81282
  writeFileSync36(autoUnlockPath, "", { mode: 384 });
81248
81283
  }
81249
- const auditLogPath = join73(home2, ".switchroom", "vault-audit.log");
81284
+ const auditLogPath = join74(home2, ".switchroom", "vault-audit.log");
81250
81285
  if (!existsSync74(auditLogPath)) {
81251
81286
  writeFileSync36(auditLogPath, "", { mode: 420 });
81252
81287
  }
81253
- const grantsDbPath = join73(home2, ".switchroom", "vault-grants.db");
81288
+ const grantsDbPath = join74(home2, ".switchroom", "vault-grants.db");
81254
81289
  if (!existsSync74(grantsDbPath)) {
81255
81290
  writeFileSync36(grantsDbPath, "", { mode: 384 });
81256
81291
  }
81257
- const hostdAuditLogPath = join73(home2, ".switchroom", "host-control-audit.log");
81292
+ const hostdAuditLogPath = join74(home2, ".switchroom", "host-control-audit.log");
81258
81293
  if (!existsSync74(hostdAuditLogPath)) {
81259
81294
  writeFileSync36(hostdAuditLogPath, "", { mode: 420 });
81260
81295
  }
81261
81296
  for (const name of Object.keys(config.agents)) {
81262
- const tokenPath = join73(home2, ".switchroom", "agents", name, ".vault-token");
81297
+ const tokenPath = join74(home2, ".switchroom", "agents", name, ".vault-token");
81263
81298
  if (!existsSync74(tokenPath)) {
81264
81299
  writeFileSync36(tokenPath, "", { mode: 384 });
81265
81300
  }
@@ -81268,15 +81303,15 @@ async function ensureHostMountSources(config) {
81268
81303
  chownSync5(tokenPath, uid, uid);
81269
81304
  } catch {}
81270
81305
  }
81271
- const fleetDir = join73(home2, ".switchroom", "fleet");
81306
+ const fleetDir = join74(home2, ".switchroom", "fleet");
81272
81307
  await mkdir2(fleetDir, { recursive: true });
81273
- const invariantsPath = join73(fleetDir, "switchroom-invariants.md");
81308
+ const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
81274
81309
  const invariantsCanonical = renderFleetInvariants();
81275
81310
  const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync61(invariantsPath, "utf-8") : null;
81276
81311
  if (invariantsCurrent !== invariantsCanonical) {
81277
81312
  writeFileSync36(invariantsPath, invariantsCanonical, { mode: 420 });
81278
81313
  }
81279
- const fleetClaudePath = join73(fleetDir, "CLAUDE.md");
81314
+ const fleetClaudePath = join74(fleetDir, "CLAUDE.md");
81280
81315
  if (!existsSync74(fleetClaudePath)) {
81281
81316
  writeFileSync36(fleetClaudePath, [
81282
81317
  "# Switchroom fleet defaults",
@@ -81361,8 +81396,8 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
81361
81396
  }
81362
81397
  function writeInstallTypeCache(homeDir = homedir43()) {
81363
81398
  const ctx = detectInstallType();
81364
- const dir = join73(homeDir, ".switchroom");
81365
- const out = join73(dir, "install-type.json");
81399
+ const dir = join74(homeDir, ".switchroom");
81400
+ const out = join74(dir, "install-type.json");
81366
81401
  const tmp = `${out}.tmp`;
81367
81402
  mkdirSync42(dir, { recursive: true });
81368
81403
  const payload = {
@@ -81411,14 +81446,14 @@ Applying switchroom config...
81411
81446
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
81412
81447
  `));
81413
81448
  try {
81414
- installUpdatePromptHook(join73(agentsDir, name));
81449
+ installUpdatePromptHook(join74(agentsDir, name));
81415
81450
  } catch (hookErr) {
81416
81451
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
81417
81452
  `));
81418
81453
  }
81419
81454
  try {
81420
81455
  const uid = allocateAgentUid(name);
81421
- alignAgentUid(name, join73(agentsDir, name), uid, {
81456
+ alignAgentUid(name, join74(agentsDir, name), uid, {
81422
81457
  confirm: !options.nonInteractive,
81423
81458
  writeOut
81424
81459
  });
@@ -81455,7 +81490,7 @@ Applying switchroom config...
81455
81490
  for (const name of agentNames) {
81456
81491
  try {
81457
81492
  const uid = allocateAgentUid(name);
81458
- alignAgentUid(name, join73(agentsDir, name), uid, {
81493
+ alignAgentUid(name, join74(agentsDir, name), uid, {
81459
81494
  confirm: !options.nonInteractive,
81460
81495
  writeOut
81461
81496
  });
@@ -81618,7 +81653,7 @@ function findUnwritableAgentDirs(config, opts) {
81618
81653
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
81619
81654
  const unwritable = [];
81620
81655
  for (const name of targets) {
81621
- const startSh = join73(agentsDir, name, "start.sh");
81656
+ const startSh = join74(agentsDir, name, "start.sh");
81622
81657
  if (!existsSync74(startSh))
81623
81658
  continue;
81624
81659
  try {
@@ -81685,7 +81720,7 @@ function registerApplyCommand(program3) {
81685
81720
  }
81686
81721
  const parentOpts = program3.opts();
81687
81722
  const config = loadConfig(parentOpts.config);
81688
- const switchroomConfigPath = parentOpts.config ?? findConfigFile();
81723
+ const switchroomConfigPath = resolveHostSwitchroomConfigPath(parentOpts.config ?? findConfigFile());
81689
81724
  if (opts.printSudoCmd) {
81690
81725
  const argv = ["sudo", ...buildSelfElevateArgv()];
81691
81726
  process.stdout.write(argv.join(" ") + `
@@ -81798,7 +81833,7 @@ function runRedactStdin() {
81798
81833
 
81799
81834
  // src/cli/status-ask.ts
81800
81835
  import { readFileSync as readFileSync62, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
81801
- import { join as join74 } from "node:path";
81836
+ import { join as join75 } from "node:path";
81802
81837
  import { homedir as homedir44 } from "node:os";
81803
81838
 
81804
81839
  // src/status-ask/report.ts
@@ -82134,7 +82169,7 @@ function resolveSources(explicitPath) {
82134
82169
  const config = loadConfig();
82135
82170
  agentsDir = resolveAgentsDir(config);
82136
82171
  } catch {
82137
- agentsDir = join74(homedir44(), ".switchroom", "agents");
82172
+ agentsDir = join75(homedir44(), ".switchroom", "agents");
82138
82173
  }
82139
82174
  if (!existsSync75(agentsDir))
82140
82175
  return [];
@@ -82146,7 +82181,7 @@ function resolveSources(explicitPath) {
82146
82181
  return [];
82147
82182
  }
82148
82183
  for (const name of entries) {
82149
- const path8 = join74(agentsDir, name, "runtime-metrics.jsonl");
82184
+ const path8 = join75(agentsDir, name, "runtime-metrics.jsonl");
82150
82185
  if (existsSync75(path8)) {
82151
82186
  sources.push({ path: path8, agent: name });
82152
82187
  }
@@ -82184,21 +82219,21 @@ import {
82184
82219
  unlinkSync as unlinkSync14,
82185
82220
  writeSync as writeSync8
82186
82221
  } from "node:fs";
82187
- import { join as join75, resolve as resolve45 } from "node:path";
82222
+ import { join as join76, resolve as resolve45 } from "node:path";
82188
82223
  var STAGING_SUBDIR = ".staging";
82189
82224
  function overlayPathsFor(agent, opts = {}) {
82190
82225
  const base = opts.root ? resolve45(opts.root, agent) : resolve45(resolveDualPath(`~/.switchroom/agents/${agent}`));
82191
- const scheduleDir = join75(base, "schedule.d");
82192
- const scheduleStagingDir = join75(scheduleDir, STAGING_SUBDIR);
82193
- const skillsDir = join75(base, "skills.d");
82194
- const skillsStagingDir = join75(skillsDir, STAGING_SUBDIR);
82226
+ const scheduleDir = join76(base, "schedule.d");
82227
+ const scheduleStagingDir = join76(scheduleDir, STAGING_SUBDIR);
82228
+ const skillsDir = join76(base, "skills.d");
82229
+ const skillsStagingDir = join76(skillsDir, STAGING_SUBDIR);
82195
82230
  return {
82196
82231
  agentRoot: base,
82197
82232
  scheduleDir,
82198
82233
  scheduleStagingDir,
82199
82234
  skillsDir,
82200
82235
  skillsStagingDir,
82201
- lockPath: join75(base, ".lock"),
82236
+ lockPath: join76(base, ".lock"),
82202
82237
  stagingDir: scheduleStagingDir
82203
82238
  };
82204
82239
  }
@@ -82252,8 +82287,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
82252
82287
  const paths = overlayPathsFor(agent, opts);
82253
82288
  return withAgentLock(paths, () => {
82254
82289
  ensureDirs(paths);
82255
- const stagingPath = join75(paths.scheduleStagingDir, `${slug}.yaml`);
82256
- const finalPath = join75(paths.scheduleDir, `${slug}.yaml`);
82290
+ const stagingPath = join76(paths.scheduleStagingDir, `${slug}.yaml`);
82291
+ const finalPath = join76(paths.scheduleDir, `${slug}.yaml`);
82257
82292
  const fd = openSync13(stagingPath, "w", 384);
82258
82293
  try {
82259
82294
  writeSync8(fd, yamlText);
@@ -82269,8 +82304,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
82269
82304
  const paths = overlayPathsFor(agent, opts);
82270
82305
  return withAgentLock(paths, () => {
82271
82306
  ensureSkillsDirs(paths);
82272
- const stagingPath = join75(paths.skillsStagingDir, `${slug}.yaml`);
82273
- const finalPath = join75(paths.skillsDir, `${slug}.yaml`);
82307
+ const stagingPath = join76(paths.skillsStagingDir, `${slug}.yaml`);
82308
+ const finalPath = join76(paths.skillsDir, `${slug}.yaml`);
82274
82309
  const fd = openSync13(stagingPath, "w", 384);
82275
82310
  try {
82276
82311
  writeSync8(fd, yamlText);
@@ -82285,7 +82320,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
82285
82320
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
82286
82321
  const paths = overlayPathsFor(agent, opts);
82287
82322
  return withAgentLock(paths, () => {
82288
- const finalPath = join75(paths.skillsDir, `${slug}.yaml`);
82323
+ const finalPath = join76(paths.skillsDir, `${slug}.yaml`);
82289
82324
  if (!existsSync76(finalPath))
82290
82325
  return false;
82291
82326
  unlinkSync14(finalPath);
@@ -82300,7 +82335,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
82300
82335
  for (const name of readdirSync28(paths.skillsDir)) {
82301
82336
  if (!/\.ya?ml$/i.test(name))
82302
82337
  continue;
82303
- const full = join75(paths.skillsDir, name);
82338
+ const full = join76(paths.skillsDir, name);
82304
82339
  try {
82305
82340
  const raw = readFileSync63(full, "utf-8");
82306
82341
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -82312,7 +82347,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
82312
82347
  function deleteOverlayEntry(agent, slug, opts = {}) {
82313
82348
  const paths = overlayPathsFor(agent, opts);
82314
82349
  return withAgentLock(paths, () => {
82315
- const finalPath = join75(paths.scheduleDir, `${slug}.yaml`);
82350
+ const finalPath = join76(paths.scheduleDir, `${slug}.yaml`);
82316
82351
  if (!existsSync76(finalPath))
82317
82352
  return false;
82318
82353
  unlinkSync14(finalPath);
@@ -82327,7 +82362,7 @@ function listOverlayEntries(agent, opts = {}) {
82327
82362
  for (const name of readdirSync28(paths.scheduleDir)) {
82328
82363
  if (!/\.ya?ml$/i.test(name))
82329
82364
  continue;
82330
- const full = join75(paths.scheduleDir, name);
82365
+ const full = join76(paths.scheduleDir, name);
82331
82366
  try {
82332
82367
  const raw = readFileSync63(full, "utf-8");
82333
82368
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -82483,12 +82518,12 @@ import {
82483
82518
  writeFileSync as writeFileSync37,
82484
82519
  writeSync as writeSync9
82485
82520
  } from "node:fs";
82486
- import { join as join76 } from "node:path";
82521
+ import { join as join77 } from "node:path";
82487
82522
  import { randomBytes as randomBytes14 } from "node:crypto";
82488
82523
  var STAGE_ID_PREFIX = "cap_";
82489
82524
  function pendingDir(agent, opts = {}) {
82490
82525
  const paths = overlayPathsFor(agent, opts);
82491
- return join76(paths.scheduleDir, ".pending");
82526
+ return join77(paths.scheduleDir, ".pending");
82492
82527
  }
82493
82528
  function ensurePendingDir(agent, opts = {}) {
82494
82529
  const dir = pendingDir(agent, opts);
@@ -82501,8 +82536,8 @@ function newStageId() {
82501
82536
  function stagePendingScheduleEntry(opts) {
82502
82537
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
82503
82538
  const stageId = opts.stageId ?? newStageId();
82504
- const yamlPath = join76(dir, `${stageId}.yaml`);
82505
- const metaPath = join76(dir, `${stageId}.meta.json`);
82539
+ const yamlPath = join77(dir, `${stageId}.yaml`);
82540
+ const metaPath = join77(dir, `${stageId}.meta.json`);
82506
82541
  const meta = {
82507
82542
  v: 1,
82508
82543
  stage_id: stageId,
@@ -82536,8 +82571,8 @@ function listPendingScheduleEntries(agent, opts = {}) {
82536
82571
  if (!name.endsWith(".meta.json"))
82537
82572
  continue;
82538
82573
  const stageId = name.slice(0, -".meta.json".length);
82539
- const metaPath = join76(dir, name);
82540
- const yamlPath = join76(dir, `${stageId}.yaml`);
82574
+ const metaPath = join77(dir, name);
82575
+ const yamlPath = join77(dir, `${stageId}.yaml`);
82541
82576
  if (!existsSync77(yamlPath))
82542
82577
  continue;
82543
82578
  try {
@@ -82556,7 +82591,7 @@ function commitPendingScheduleEntry(opts) {
82556
82591
  return { committed: false, reason: "not_found" };
82557
82592
  const slug = match.meta.entry.name ?? match.stageId;
82558
82593
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
82559
- const finalPath = join76(paths.scheduleDir, `${slug}.yaml`);
82594
+ const finalPath = join77(paths.scheduleDir, `${slug}.yaml`);
82560
82595
  if (existsSync77(finalPath)) {
82561
82596
  return { committed: false, reason: "slug_collision" };
82562
82597
  }
@@ -83171,7 +83206,7 @@ var import_yaml20 = __toESM(require_dist(), 1);
83171
83206
  import { existsSync as existsSync79 } from "node:fs";
83172
83207
  init_reconcile_default_skills();
83173
83208
  var import_yaml21 = __toESM(require_dist(), 1);
83174
- import { join as join77 } from "node:path";
83209
+ import { join as join78 } from "node:path";
83175
83210
  var MAX_SKILLS_PER_AGENT = 20;
83176
83211
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
83177
83212
  function exitCodeFor2(code) {
@@ -83246,7 +83281,7 @@ function skillInstall(opts) {
83246
83281
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
83247
83282
  }
83248
83283
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
83249
- const skillPath = join77(poolDir, skillName);
83284
+ const skillPath = join78(poolDir, skillName);
83250
83285
  if (!existsSync79(skillPath)) {
83251
83286
  return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
83252
83287
  }
@@ -83425,7 +83460,7 @@ import {
83425
83460
  writeFileSync as writeFileSync38
83426
83461
  } from "node:fs";
83427
83462
  import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
83428
- import { dirname as dirname23, join as join78, relative as relative2, resolve as resolve46 } from "node:path";
83463
+ import { dirname as dirname23, join as join79, relative as relative2, resolve as resolve46 } from "node:path";
83429
83464
  import { spawnSync as spawnSync11 } from "node:child_process";
83430
83465
 
83431
83466
  // src/cli/skill-common.ts
@@ -83619,7 +83654,7 @@ function scanForClaudeP2(content) {
83619
83654
  function resolveSkillsPoolDir2(override) {
83620
83655
  const raw = override ?? "~/.switchroom/skills";
83621
83656
  if (raw.startsWith("~/")) {
83622
- return join78(homedir45(), raw.slice(2));
83657
+ return join79(homedir45(), raw.slice(2));
83623
83658
  }
83624
83659
  if (raw === "~")
83625
83660
  return homedir45();
@@ -83658,7 +83693,7 @@ function loadFromDir(dir) {
83658
83693
  const walk2 = (sub) => {
83659
83694
  const entries = readdirSync30(sub, { withFileTypes: true });
83660
83695
  for (const ent of entries) {
83661
- const full = join78(sub, ent.name);
83696
+ const full = join79(sub, ent.name);
83662
83697
  const rel = relative2(abs, full);
83663
83698
  if (ent.isSymbolicLink()) {
83664
83699
  fail2(`refusing to read symlink inside --from dir: ${rel}`);
@@ -83693,7 +83728,7 @@ function loadFromTarball(tarPath) {
83693
83728
  fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
83694
83729
  }
83695
83730
  }
83696
- const staging = mkdtempSync5(join78(tmpdir5(), "skill-apply-extract-"));
83731
+ const staging = mkdtempSync5(join79(tmpdir5(), "skill-apply-extract-"));
83697
83732
  try {
83698
83733
  const flags = isGz ? ["-xzf"] : ["-xf"];
83699
83734
  const r = spawnSync11("tar", [
@@ -83779,8 +83814,8 @@ function validatePayload(name, files) {
83779
83814
  errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
83780
83815
  }
83781
83816
  } else if (PY_SCRIPT_RE2.test(path8)) {
83782
- const tmp = mkdtempSync5(join78(tmpdir5(), "skill-apply-py-"));
83783
- const tmpPy = join78(tmp, "check.py");
83817
+ const tmp = mkdtempSync5(join79(tmpdir5(), "skill-apply-py-"));
83818
+ const tmpPy = join79(tmp, "check.py");
83784
83819
  try {
83785
83820
  writeFileSync38(tmpPy, content);
83786
83821
  const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
@@ -83803,7 +83838,7 @@ function diffSummary(currentDir, files) {
83803
83838
  if (existsSync80(currentDir)) {
83804
83839
  const walk2 = (sub) => {
83805
83840
  for (const ent of readdirSync30(sub, { withFileTypes: true })) {
83806
- const full = join78(sub, ent.name);
83841
+ const full = join79(sub, ent.name);
83807
83842
  const rel = relative2(currentDir, full);
83808
83843
  if (ent.isDirectory()) {
83809
83844
  walk2(full);
@@ -83839,7 +83874,7 @@ function writePayload(poolDir, name, files) {
83839
83874
  if (!existsSync80(poolDir)) {
83840
83875
  mkdirSync45(poolDir, { recursive: true, mode: 493 });
83841
83876
  }
83842
- const target = join78(poolDir, name);
83877
+ const target = join79(poolDir, name);
83843
83878
  let targetIsSymlink = false;
83844
83879
  try {
83845
83880
  const st = lstatSync8(target);
@@ -83850,11 +83885,11 @@ function writePayload(poolDir, name, files) {
83850
83885
  if (targetIsSymlink) {
83851
83886
  fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
83852
83887
  }
83853
- const staging = mkdtempSync5(join78(poolDir, `.skill-apply-stage-${name}-`));
83888
+ const staging = mkdtempSync5(join79(poolDir, `.skill-apply-stage-${name}-`));
83854
83889
  let oldRename = null;
83855
83890
  try {
83856
83891
  for (const [path8, content] of Object.entries(files)) {
83857
- const full = join78(staging, path8);
83892
+ const full = join79(staging, path8);
83858
83893
  mkdirSync45(dirname23(full), { recursive: true, mode: 493 });
83859
83894
  const fd = openSync15(full, "wx");
83860
83895
  try {
@@ -83932,7 +83967,7 @@ function registerSkillCommand(program3) {
83932
83967
  }
83933
83968
  const config = loadConfig();
83934
83969
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
83935
- const currentDir = join78(poolDir, name);
83970
+ const currentDir = join79(poolDir, name);
83936
83971
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
83937
83972
  console.log(source_default.bold("Diff vs current pool content:"));
83938
83973
  console.log(diffSummary(currentDir, files));
@@ -83976,7 +84011,7 @@ import {
83976
84011
  utimesSync,
83977
84012
  writeFileSync as writeFileSync39
83978
84013
  } from "node:fs";
83979
- import { dirname as dirname24, join as join79, relative as relative3, resolve as resolve47 } from "node:path";
84014
+ import { dirname as dirname24, join as join80, relative as relative3, resolve as resolve47 } from "node:path";
83980
84015
  import { homedir as homedir46, tmpdir as tmpdir6 } from "node:os";
83981
84016
  import { spawnSync as spawnSync12 } from "node:child_process";
83982
84017
  init_helpers();
@@ -83987,10 +84022,10 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
83987
84022
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
83988
84023
  function resolveConfigSkillsDir(agent) {
83989
84024
  const override = process.env.SWITCHROOM_CONFIG_DIR;
83990
- const candidate = override ? resolve47(override) : join79(homedir46(), ".switchroom-config");
84025
+ const candidate = override ? resolve47(override) : join80(homedir46(), ".switchroom-config");
83991
84026
  if (!existsSync81(candidate))
83992
84027
  return null;
83993
- return join79(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
84028
+ return join80(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
83994
84029
  }
83995
84030
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
83996
84031
  function sweepMirrorPriors(configSkillsRoot) {
@@ -84008,7 +84043,7 @@ function sweepMirrorPriors(configSkillsRoot) {
84008
84043
  if (now - ts < MIRROR_PRIOR_TTL_MS)
84009
84044
  continue;
84010
84045
  try {
84011
- rmSync17(join79(configSkillsRoot, ent), { recursive: true, force: true });
84046
+ rmSync17(join80(configSkillsRoot, ent), { recursive: true, force: true });
84012
84047
  } catch {}
84013
84048
  }
84014
84049
  } catch {}
@@ -84017,7 +84052,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84017
84052
  const configSkillsRoot = resolveConfigSkillsDir(agent);
84018
84053
  if (!configSkillsRoot)
84019
84054
  return;
84020
- const dest = join79(configSkillsRoot, name);
84055
+ const dest = join80(configSkillsRoot, name);
84021
84056
  try {
84022
84057
  if (liveSkillDir !== null) {
84023
84058
  try {
@@ -84032,19 +84067,19 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84032
84067
  if (liveSkillDir === null) {
84033
84068
  sweepMirrorPriors(configSkillsRoot);
84034
84069
  if (existsSync81(dest)) {
84035
- const trash = join79(configSkillsRoot, `.${name}-trash-${Date.now()}`);
84070
+ const trash = join80(configSkillsRoot, `.${name}-trash-${Date.now()}`);
84036
84071
  renameSync18(dest, trash);
84037
84072
  }
84038
84073
  return;
84039
84074
  }
84040
84075
  mkdirSync46(configSkillsRoot, { recursive: true, mode: 493 });
84041
84076
  sweepMirrorPriors(configSkillsRoot);
84042
- const staging = mkdtempSync6(join79(configSkillsRoot, `.${name}-staging-`));
84077
+ const staging = mkdtempSync6(join80(configSkillsRoot, `.${name}-staging-`));
84043
84078
  const walk2 = (src, dst) => {
84044
84079
  mkdirSync46(dst, { recursive: true, mode: 493 });
84045
84080
  for (const ent of readdirSync31(src, { withFileTypes: true })) {
84046
- const s = join79(src, ent.name);
84047
- const d = join79(dst, ent.name);
84081
+ const s = join80(src, ent.name);
84082
+ const d = join80(dst, ent.name);
84048
84083
  if (ent.isSymbolicLink())
84049
84084
  continue;
84050
84085
  if (ent.isDirectory())
@@ -84056,7 +84091,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
84056
84091
  };
84057
84092
  walk2(liveSkillDir, staging);
84058
84093
  if (existsSync81(dest)) {
84059
- const prior = join79(configSkillsRoot, `.${name}-prior-${Date.now()}`);
84094
+ const prior = join80(configSkillsRoot, `.${name}-prior-${Date.now()}`);
84060
84095
  renameSync18(dest, prior);
84061
84096
  }
84062
84097
  renameSync18(staging, dest);
@@ -84083,13 +84118,13 @@ function resolveAgent(opts) {
84083
84118
  function resolveAgentsRoot(opts) {
84084
84119
  if (opts.root)
84085
84120
  return resolve47(opts.root);
84086
- return join79(homedir46(), ".switchroom", "agents");
84121
+ return join80(homedir46(), ".switchroom", "agents");
84087
84122
  }
84088
84123
  function personalSkillDir(agentsRoot, agent, name) {
84089
- return join79(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
84124
+ return join80(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
84090
84125
  }
84091
84126
  function trashDir(agentsRoot, agent) {
84092
- return join79(agentsRoot, agent, ".claude", TRASH_DIRNAME);
84127
+ return join80(agentsRoot, agent, ".claude", TRASH_DIRNAME);
84093
84128
  }
84094
84129
  function readStdinSync2() {
84095
84130
  const chunks = [];
@@ -84119,7 +84154,7 @@ function loadFromDir2(dir) {
84119
84154
  const files = {};
84120
84155
  const walk2 = (sub) => {
84121
84156
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
84122
- const full = join79(sub, ent.name);
84157
+ const full = join80(sub, ent.name);
84123
84158
  if (ent.isSymbolicLink()) {
84124
84159
  fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
84125
84160
  }
@@ -84172,8 +84207,8 @@ function behavioralValidate(files) {
84172
84207
  errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
84173
84208
  }
84174
84209
  } else if (PY_SCRIPT_RE.test(path8)) {
84175
- const tmp = mkdtempSync6(join79(tmpdir6(), "skill-personal-py-"));
84176
- const tmpPy = join79(tmp, "check.py");
84210
+ const tmp = mkdtempSync6(join80(tmpdir6(), "skill-personal-py-"));
84211
+ const tmpPy = join80(tmp, "check.py");
84177
84212
  try {
84178
84213
  writeFileSync39(tmpPy, content);
84179
84214
  const r = spawnSync12("python3", ["-m", "py_compile", tmpPy], {
@@ -84197,7 +84232,7 @@ function sweepTrash(agentsRoot, agent) {
84197
84232
  for (const ent of readdirSync31(trash, { withFileTypes: true })) {
84198
84233
  if (!ent.isDirectory())
84199
84234
  continue;
84200
- const entPath = join79(trash, ent.name);
84235
+ const entPath = join80(trash, ent.name);
84201
84236
  try {
84202
84237
  const st = statSync32(entPath);
84203
84238
  if (now - st.mtimeMs > TRASH_TTL_MS) {
@@ -84218,11 +84253,11 @@ function writePersonalSkill(targetDir, files) {
84218
84253
  fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
84219
84254
  }
84220
84255
  mkdirSync46(dirname24(targetDir), { recursive: true, mode: 493 });
84221
- const staging = mkdtempSync6(join79(dirname24(targetDir), `.skill-personal-stage-`));
84256
+ const staging = mkdtempSync6(join80(dirname24(targetDir), `.skill-personal-stage-`));
84222
84257
  let oldRename = null;
84223
84258
  try {
84224
84259
  for (const [path8, content] of Object.entries(files)) {
84225
- const full = join79(staging, path8);
84260
+ const full = join80(staging, path8);
84226
84261
  mkdirSync46(dirname24(full), { recursive: true, mode: 493 });
84227
84262
  const fd = openSync16(full, "wx");
84228
84263
  try {
@@ -84355,10 +84390,10 @@ function editPersonalAction(name, opts) {
84355
84390
  }
84356
84391
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
84357
84392
  function defaultSharedRoot() {
84358
- return join79(homedir46(), ".switchroom", "skills");
84393
+ return join80(homedir46(), ".switchroom", "skills");
84359
84394
  }
84360
84395
  function defaultBundledRoot() {
84361
- return join79(homedir46(), ".switchroom", "skills", "_bundled");
84396
+ return join80(homedir46(), ".switchroom", "skills", "_bundled");
84362
84397
  }
84363
84398
  function resolveCloneSource(source, opts) {
84364
84399
  const m = CLONE_SOURCE_RE.exec(source);
@@ -84368,7 +84403,7 @@ function resolveCloneSource(source, opts) {
84368
84403
  const tier = m[1];
84369
84404
  const slug = m[2];
84370
84405
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
84371
- const dir = join79(root, slug);
84406
+ const dir = join80(root, slug);
84372
84407
  if (!existsSync81(dir)) {
84373
84408
  fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
84374
84409
  }
@@ -84384,7 +84419,7 @@ function readSourceFiles(dir) {
84384
84419
  const skipped = [];
84385
84420
  const walk2 = (sub) => {
84386
84421
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
84387
- const full = join79(sub, ent.name);
84422
+ const full = join80(sub, ent.name);
84388
84423
  if (ent.isSymbolicLink()) {
84389
84424
  continue;
84390
84425
  }
@@ -84495,7 +84530,7 @@ function removePersonalAction(name, opts) {
84495
84530
  const trashRoot = trashDir(agentsRoot, agent);
84496
84531
  mkdirSync46(trashRoot, { recursive: true, mode: 493 });
84497
84532
  const ts = Date.now();
84498
- const trashTarget = join79(trashRoot, `${name}-${ts}`);
84533
+ const trashTarget = join80(trashRoot, `${name}-${ts}`);
84499
84534
  renameSync18(target, trashTarget);
84500
84535
  const now = new Date(ts);
84501
84536
  utimesSync(trashTarget, now, now);
@@ -84514,7 +84549,7 @@ function listPersonalAction(opts) {
84514
84549
  const agent = resolveAgent(opts);
84515
84550
  const agentsRoot = resolveAgentsRoot(opts);
84516
84551
  sweepTrash(agentsRoot, agent);
84517
- const skillsDir = join79(agentsRoot, agent, ".claude", "skills");
84552
+ const skillsDir = join80(agentsRoot, agent, ".claude", "skills");
84518
84553
  const personal = [];
84519
84554
  if (existsSync81(skillsDir)) {
84520
84555
  for (const ent of readdirSync31(skillsDir, { withFileTypes: true })) {
@@ -84523,7 +84558,7 @@ function listPersonalAction(opts) {
84523
84558
  if (!ent.name.startsWith(PERSONAL_PREFIX))
84524
84559
  continue;
84525
84560
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
84526
- const skillPath = join79(skillsDir, ent.name);
84561
+ const skillPath = join80(skillsDir, ent.name);
84527
84562
  let fileCount = 0;
84528
84563
  let totalBytes = 0;
84529
84564
  const walk2 = (sub) => {
@@ -84531,10 +84566,10 @@ function listPersonalAction(opts) {
84531
84566
  if (e.isFile()) {
84532
84567
  fileCount += 1;
84533
84568
  try {
84534
- totalBytes += statSync32(join79(sub, e.name)).size;
84569
+ totalBytes += statSync32(join80(sub, e.name)).size;
84535
84570
  } catch {}
84536
84571
  } else if (e.isDirectory()) {
84537
- walk2(join79(sub, e.name));
84572
+ walk2(join80(sub, e.name));
84538
84573
  }
84539
84574
  }
84540
84575
  };
@@ -84575,7 +84610,7 @@ init_helpers();
84575
84610
  var import_yaml23 = __toESM(require_dist(), 1);
84576
84611
  import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync68, statSync as statSync33 } from "node:fs";
84577
84612
  import { homedir as homedir47 } from "node:os";
84578
- import { join as join80, resolve as resolve48 } from "node:path";
84613
+ import { join as join81, resolve as resolve48 } from "node:path";
84579
84614
  var PERSONAL_PREFIX2 = "personal-";
84580
84615
  var BUNDLED_SUBDIR = "_bundled";
84581
84616
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
@@ -84589,7 +84624,7 @@ function defaultBundledRoot2() {
84589
84624
  return resolve48(homedir47(), ".switchroom/skills/_bundled");
84590
84625
  }
84591
84626
  function readSkillFrontmatter(skillDir) {
84592
- const mdPath = join80(skillDir, "SKILL.md");
84627
+ const mdPath = join81(skillDir, "SKILL.md");
84593
84628
  if (!existsSync82(mdPath))
84594
84629
  return null;
84595
84630
  let content;
@@ -84622,7 +84657,7 @@ function readSkillFrontmatter(skillDir) {
84622
84657
  return { fm: parsed };
84623
84658
  }
84624
84659
  function statSkillMd(skillDir) {
84625
- const mdPath = join80(skillDir, "SKILL.md");
84660
+ const mdPath = join81(skillDir, "SKILL.md");
84626
84661
  try {
84627
84662
  const st = statSync33(mdPath);
84628
84663
  return { size: st.size, mtime: st.mtime.toISOString() };
@@ -84633,7 +84668,7 @@ function statSkillMd(skillDir) {
84633
84668
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
84634
84669
  if (!AGENT_NAME_RE3.test(agent))
84635
84670
  return [];
84636
- const skillsDir = join80(agentsRoot, agent, ".claude/skills");
84671
+ const skillsDir = join81(agentsRoot, agent, ".claude/skills");
84637
84672
  if (!existsSync82(skillsDir))
84638
84673
  return [];
84639
84674
  const out = [];
@@ -84646,7 +84681,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
84646
84681
  for (const ent of entries) {
84647
84682
  if (!ent.startsWith(PERSONAL_PREFIX2))
84648
84683
  continue;
84649
- const dirPath = join80(skillsDir, ent);
84684
+ const dirPath = join81(skillsDir, ent);
84650
84685
  try {
84651
84686
  if (!statSync33(dirPath).isDirectory())
84652
84687
  continue;
@@ -84686,7 +84721,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
84686
84721
  continue;
84687
84722
  if (ent.startsWith("."))
84688
84723
  continue;
84689
- const dirPath = join80(sharedRoot, ent);
84724
+ const dirPath = join81(sharedRoot, ent);
84690
84725
  try {
84691
84726
  if (!statSync33(dirPath).isDirectory())
84692
84727
  continue;
@@ -84722,7 +84757,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
84722
84757
  for (const ent of entries) {
84723
84758
  if (ent.startsWith("."))
84724
84759
  continue;
84725
- const dirPath = join80(bundledRoot, ent);
84760
+ const dirPath = join81(bundledRoot, ent);
84726
84761
  try {
84727
84762
  if (!statSync33(dirPath).isDirectory())
84728
84763
  continue;
@@ -84868,7 +84903,7 @@ init_source();
84868
84903
  init_helpers();
84869
84904
  import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync70, writeFileSync as writeFileSync40, statSync as statSync34, copyFileSync as copyFileSync12 } from "node:fs";
84870
84905
  import { homedir as homedir48 } from "node:os";
84871
- import { join as join81 } from "node:path";
84906
+ import { join as join82 } from "node:path";
84872
84907
  import { spawnSync as spawnSync14 } from "node:child_process";
84873
84908
  init_audit_reader();
84874
84909
  var DEFAULT_IMAGE_TAG = "latest";
@@ -84937,6 +84972,19 @@ services:
84937
84972
  # operator's home), so the socket paths the agent fleet sees
84938
84973
  # (~/.switchroom/hostd/<agent>/sock) match the paths hostd binds.
84939
84974
  HOME: /host-home
84975
+ # The REAL host home path. HOME above is pinned to /host-home (the
84976
+ # in-container mount point) for the socket-path convention, but that
84977
+ # is NOT a host filesystem path. When hostd shells out \`switchroom
84978
+ # apply\` / \`agent restart\`, the compose generator must emit HOST
84979
+ # paths as bind-mount SOURCES \u2014 homedir() inside here returns
84980
+ # /host-home, which docker would auto-create as empty dirs on the
84981
+ # host (start.sh missing \u2192 every agent exec-fails 127; broker EISDIR
84982
+ # \u2014 the 2026-06-11/12 fleet outages). SWITCHROOM_HOST_HOME is the
84983
+ # generator's authoritative host home (write-compose.ts prefers it
84984
+ # over homedir(); compose.ts bakes it into each agent). Without it,
84985
+ # an in-container apply poisons every bind source with /host-home AND
84986
+ # re-bakes /host-home into the fleet, self-perpetuating.
84987
+ SWITCHROOM_HOST_HOME: ${hostHome}
84940
84988
  # Hostd's CLI shellouts (\`switchroom <verb>\`) need to pick up the
84941
84989
  # same config the agent fleet's compose generator did. Point at
84942
84990
  # the resolved /state/config bind so the agent fleet's config-path
@@ -84959,10 +85007,10 @@ networks:
84959
85007
  `;
84960
85008
  }
84961
85009
  function hostdDir() {
84962
- return join81(homedir48(), ".switchroom", "hostd");
85010
+ return join82(homedir48(), ".switchroom", "hostd");
84963
85011
  }
84964
85012
  function hostdComposePath() {
84965
- return join81(hostdDir(), "docker-compose.yml");
85013
+ return join82(hostdDir(), "docker-compose.yml");
84966
85014
  }
84967
85015
  function backupExistingCompose() {
84968
85016
  const p = hostdComposePath();
@@ -85072,7 +85120,7 @@ function doStatus() {
85072
85120
  for (const name of readdirSync33(dir)) {
85073
85121
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
85074
85122
  continue;
85075
- const sockPath = join81(dir, name, "sock");
85123
+ const sockPath = join82(dir, name, "sock");
85076
85124
  if (existsSync84(sockPath)) {
85077
85125
  const st = statSync34(sockPath);
85078
85126
  if ((st.mode & 61440) === 49152) {
@@ -85165,7 +85213,7 @@ init_source();
85165
85213
  init_helpers();
85166
85214
  import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync41, copyFileSync as copyFileSync13 } from "node:fs";
85167
85215
  import { homedir as homedir49 } from "node:os";
85168
- import { join as join82 } from "node:path";
85216
+ import { join as join83 } from "node:path";
85169
85217
  import { spawnSync as spawnSync15 } from "node:child_process";
85170
85218
  var DEFAULT_IMAGE_TAG2 = "latest";
85171
85219
  var WEB_COMPOSE_PROJECT = "switchroom-web";
@@ -85246,10 +85294,10 @@ services:
85246
85294
  `;
85247
85295
  }
85248
85296
  function webdDir() {
85249
- return join82(homedir49(), ".switchroom", "web");
85297
+ return join83(homedir49(), ".switchroom", "web");
85250
85298
  }
85251
85299
  function webdComposePath() {
85252
- return join82(webdDir(), "docker-compose.yml");
85300
+ return join83(webdDir(), "docker-compose.yml");
85253
85301
  }
85254
85302
  function backupExistingCompose2() {
85255
85303
  const p = webdComposePath();