switchroom 0.13.54 → 0.13.56

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.
@@ -16334,10 +16334,10 @@ __export(exports_client, {
16334
16334
  AuthBrokerClient: () => AuthBrokerClient
16335
16335
  });
16336
16336
  import * as net from "node:net";
16337
- import { existsSync as existsSync6 } from "node:fs";
16337
+ import { existsSync as existsSync7 } from "node:fs";
16338
16338
  import { homedir as homedir3 } from "node:os";
16339
16339
  import { randomUUID as randomUUID3 } from "node:crypto";
16340
- import { join as join8 } from "node:path";
16340
+ import { join as join9 } from "node:path";
16341
16341
  function reviveDate(v) {
16342
16342
  if (v == null)
16343
16343
  return null;
@@ -16347,7 +16347,7 @@ function reviveDate(v) {
16347
16347
  return Number.isNaN(d.getTime()) ? null : d;
16348
16348
  }
16349
16349
  function operatorSocketPath(home = homedir3()) {
16350
- return join8(home, ".switchroom", "state", "auth-broker-operator", "sock");
16350
+ return join9(home, ".switchroom", "state", "auth-broker-operator", "sock");
16351
16351
  }
16352
16352
  function resolveAuthBrokerSocketPath(opts) {
16353
16353
  if (opts?.socket)
@@ -16617,7 +16617,7 @@ async function withAuthBrokerClient(fn, opts) {
16617
16617
  }
16618
16618
  }
16619
16619
  function authBrokerSocketExists(opts) {
16620
- return existsSync6(resolveAuthBrokerSocketPath(opts));
16620
+ return existsSync7(resolveAuthBrokerSocketPath(opts));
16621
16621
  }
16622
16622
  var DEFAULT_TIMEOUT_MS = 5000, AuthBrokerError, AuthBrokerUnreachableError;
16623
16623
  var init_client = __esm(() => {
@@ -23607,7 +23607,7 @@ var init_dist = __esm(() => {
23607
23607
  });
23608
23608
 
23609
23609
  // ../src/config/schema.ts
23610
- var CodeRepoEntrySchema, AgentBindMountSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, ReactionsSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, HostdConfigSchema, SwitchroomConfigSchema;
23610
+ var CodeRepoEntrySchema, AgentBindMountSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, HostdConfigSchema, SwitchroomConfigSchema;
23611
23611
  var init_schema = __esm(() => {
23612
23612
  init_zod();
23613
23613
  CodeRepoEntrySchema = exports_external.object({
@@ -23792,6 +23792,16 @@ var init_schema = __esm(() => {
23792
23792
  authority: exports_external.string().url().optional().describe("Microsoft authority endpoint. Defaults to " + "'https://login.microsoftonline.com/common' which accepts both " + "personal MSA and work/school tenants. Override only for " + "single-tenant deployments."),
23793
23793
  org_mode: exports_external.boolean().optional().describe("Opt-in to Teams + SharePoint surfaces (RFC \u00a76.4). When true, " + "the v1 scope set adds Sites.ReadWrite.All AND the launcher " + "spawns softeria with --org-mode. Defaults to false \u2014 personal " + "MSA + standard work surfaces only. Flipping for an existing " + "consented account requires re-running 'auth microsoft account " + "add --replace' to consent the additional scope.")
23794
23794
  }).optional();
23795
+ NotionWorkspaceConfigSchema = exports_external.object({
23796
+ vault_key: exports_external.string().min(1).default("notion/integration-token").describe("Vault key holding the Notion internal-integration token. Default " + "`notion/integration-token`. Override only for non-standard vault " + "layouts. The broker's --allow ACL on this key is the authoritative " + "list of which agents may receive the token."),
23797
+ databases: exports_external.record(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/, {
23798
+ message: "notion_workspace.databases friendly names must match " + "/^[a-z0-9][a-z0-9_-]{0,62}$/ \u2014 lowercase letters, digits, " + "hyphens, underscores. Got: '%s'."
23799
+ }), exports_external.string().regex(/^[0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12}$/, {
23800
+ message: "notion_workspace.databases values must be Notion database " + "UUIDs (32 hex characters, optional dashes)."
23801
+ })).default({}).describe("Friendly-name \u2192 Notion database UUID map. Operator-managed; agents " + "reference databases by friendly name only \u2014 they never see or type " + "UUIDs. Populate via `switchroom notion list-dbs` (PR 4) after " + "vault-putting the integration token and sharing DBs with the " + "integration in Notion's UI."),
23802
+ mcp_version: exports_external.string().min(1).optional().describe("Optional pin for the upstream `@notionhq/notion-mcp-server` npm " + "package version. Default is the build-time `NOTION_MCP_PINNED_VERSION` " + "constant. Override only when reproducing operator-specific bugs."),
23803
+ rate_limit_rps: exports_external.number().int().positive().max(10).optional().describe("Optional global rate-limit budget in requests per second across all " + "switchroom agents sharing this integration token. Defaults to 3 " + "(Notion's documented public-API limit). Lower it if you also use " + "the integration token from outside switchroom and need to share " + "budget. Higher than 10 is rejected \u2014 if you think you need it, " + "your usage probably needs a workspace-tier upgrade with Notion.")
23804
+ }).optional();
23795
23805
  AgentGoogleWorkspaceConfigSchema = exports_external.object({
23796
23806
  account: exports_external.string().regex(/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/, {
23797
23807
  message: "google_workspace.account must be a Google account email like " + "'alice@example.com' (colons not allowed)"
@@ -23805,6 +23815,13 @@ var init_schema = __esm(() => {
23805
23815
  }).transform((v) => v.trim().toLowerCase()).optional().describe("RFC #1873: the Microsoft account this agent uses for the M365 MCP. " + "Must be a key in top-level `microsoft_accounts:` with this agent " + "listed in its `enabled_for[]`. Read by the auth-broker " + "(get-credentials, provider=microsoft) and by the scaffold to " + "decide whether to emit the `ms-365` MCP entry. Normalized to " + "lowercase so it matches the microsoft_accounts key (which is " + "also normalized)."),
23806
23816
  org_mode: exports_external.boolean().optional().describe("Per-agent org_mode override (RFC #1873 \u00a76.4). When set, replaces " + "the top-level microsoft_workspace.org_mode for this agent. " + "Defaults to top-level value (which defaults to false).")
23807
23817
  }).optional();
23818
+ AgentNotionWorkspaceConfigSchema = exports_external.object({
23819
+ databases: exports_external.array(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/, {
23820
+ message: "notion_workspace.databases entries must be friendly names " + "matching /^[a-z0-9][a-z0-9_-]{0,62}$/ \u2014 these must appear as " + "keys in top-level notion_workspace.databases."
23821
+ })).min(1, {
23822
+ message: "notion_workspace.databases must list at least one friendly " + "name. An empty list rejects every Notion tool call \u2014 if you " + "want to remove this agent's Notion access, delete the entire " + "notion_workspace block instead."
23823
+ }).optional().describe("Optional per-agent allowlist of database friendly names this " + "agent may read/write. Each name must exist as a key in top-level " + "notion_workspace.databases. Omit the field (or leave it undefined) " + "to grant access to every DB the upstream integration can see \u2014 " + "appropriate for an admin/orchestrator agent. Set the list to " + "narrow access for specialist agents.")
23824
+ }).optional();
23808
23825
  ReactionsSchema = exports_external.object({
23809
23826
  enabled: exports_external.boolean().optional().describe("Master switch for the reaction-trigger path. When false, " + "reactions are still persisted via recordReaction but never " + "dispatched to the agent as synthetic inbound turns. Default true."),
23810
23827
  trigger_emojis: exports_external.array(exports_external.string()).optional().describe("Emoji allowlist that triggers a synthetic inbound when reacted " + "to a bot message. Default ['\uD83D\uDC4E', '\u274c', '\uD83D\uDC4D', '\u2705']. Cascade " + "mode: REPLACE (not union) \u2014 setting this at a layer replaces " + "lower layers entirely, so an operator can narrow to [] to " + "disable triggering without flipping `enabled`."),
@@ -23941,6 +23958,7 @@ var init_schema = __esm(() => {
23941
23958
  drive: AgentGoogleWorkspaceConfigSchema.describe("RFC D legacy key \u2014 use `google_workspace:` instead. Per-agent " + "google_workspace overrides (currently approvers + tier). When set, " + "replaces the top-level approvers list for this agent. " + "google_client_id/secret are not per-agent \u2014 they live at the top level."),
23942
23959
  google_workspace: AgentGoogleWorkspaceConfigSchema.describe("RFC G canonical key. Per-agent Google Workspace overrides \u2014 currently " + "approvers (replaces, does not extend the top-level list) and tier " + "(`core` | `extended` | `complete`, replaces top-level default). " + "google_client_id/secret are not per-agent \u2014 they live at the top level. " + "Mutually exclusive with `drive:` on the same agent (loader fails fast " + "if both are set)."),
23943
23960
  microsoft_workspace: AgentMicrosoftWorkspaceConfigSchema.describe("RFC #1873 (Microsoft 365 integration). Per-agent Microsoft Workspace " + "override \u2014 pins the Microsoft account this agent reads via the " + "auth-broker (must be a key in top-level `microsoft_accounts:` with " + "this agent in its `enabled_for[]`) and optionally overrides org_mode. " + "microsoft_client_id/secret are not per-agent."),
23961
+ notion_workspace: AgentNotionWorkspaceConfigSchema.describe("RFC docs/rfcs/notion-integration.md. Per-agent Notion access. " + "Presence opts the agent IN (launcher scaffolded, MCP entry emitted, " + "broker grants the integration token). Optional `databases:` filter " + "narrows which DBs this agent may read/write \u2014 names must resolve in " + "top-level notion_workspace.databases. Absence opts the agent OUT."),
23944
23962
  repos: exports_external.record(exports_external.string().regex(/^[a-z0-9][a-z0-9-]*$/, "Repo slug must be kebab-case ASCII: start with a lowercase letter or digit, contain only lowercase letters, digits, and hyphens"), exports_external.object({
23945
23963
  url: exports_external.string().min(1).describe("Git remote URL for the repo (e.g. 'git@github.com:org/repo.git' or " + "'https://github.com/org/repo.git'). Used verbatim for git clone."),
23946
23964
  branch_default: exports_external.string().optional().describe("Default branch to track (defaults to the remote's HEAD, typically 'main'). " + "The per-agent branch 'agent/<agentName>/main' fast-forwards to this branch " + "when the worktree is clean on session start.")
@@ -24064,6 +24082,7 @@ var init_schema = __esm(() => {
24064
24082
  drive: GoogleWorkspaceConfigSchema.describe("RFC D legacy key \u2014 use `google_workspace:` instead. Optional Google " + "Workspace onboarding configuration. When set, supplies Google OAuth " + "client credentials, the approver allowlist for `switchroom drive " + "connect`, and the optional tier knob. Env vars " + "(SWITCHROOM_GOOGLE_CLIENT_ID, SWITCHROOM_GOOGLE_CLIENT_SECRET, " + "SWITCHROOM_APPROVER_USER_ID) take precedence over this block when " + "set, preserving back-compat with the env-only flow shipped in #766."),
24065
24083
  google_workspace: GoogleWorkspaceConfigSchema.describe("RFC G canonical key. Top-level Google Workspace configuration \u2014 " + "OAuth client credentials, approver allowlist, and tier knob (`core` " + "| `extended` | `complete`, default `core`). Mutually exclusive with " + "`drive:` at the top level (loader fails fast if both are set)."),
24066
24084
  microsoft_workspace: MicrosoftWorkspaceConfigSchema.describe("RFC #1873 (Microsoft 365 integration). Top-level Microsoft Workspace " + "configuration \u2014 OAuth client credentials (Entra app), authority " + "endpoint (defaults to /common for personal MSA + work), and the " + "org_mode opt-in for Teams/SharePoint surfaces. Block is optional; " + "when omitted the broker does not register the Microsoft provider."),
24085
+ notion_workspace: NotionWorkspaceConfigSchema.describe("RFC docs/rfcs/notion-integration.md. Top-level Notion integration " + "config \u2014 vault key for the integration token, friendly-name \u2192 " + "database UUID map, optional MCP-package version pin, and optional " + "global rate-limit override (default 3 rps, Notion's documented " + "public-API limit). Block is optional; when omitted no agent gets a " + "Notion MCP entry regardless of per-agent config."),
24067
24086
  quota: QuotaConfigSchema.optional().describe("Optional weekly/monthly USD spend budgets rendered in the session " + "greeting. Usage is read from ccusage at runtime; no network calls."),
24068
24087
  host_control: HostControlConfigSchema.default({}).describe("Host-control daemon configuration. Defaults to enabled=true since " + "RFC C Phase 2 (docs/rfcs/host-control-daemon.md). Omit the block " + "to accept defaults; set `enabled: false` only on legacy systemd-" + "mode installs (removal tracked as RFC C Phase 3)."),
24069
24088
  hostd: HostdConfigSchema.default({}).describe("hostd verb-level knobs (RFC admin-agent-config-edit). Distinct " + "from `host_control:` which governs whether the daemon runs at " + "all. Currently scopes the opt-in flag and rate cap for the new " + "`config_propose_edit` verb (PR 1a \u2014 disabled by default)."),
@@ -24090,7 +24109,7 @@ var init_schema = __esm(() => {
24090
24109
  });
24091
24110
 
24092
24111
  // ../src/config/paths.ts
24093
- import { existsSync as existsSync7 } from "node:fs";
24112
+ import { existsSync as existsSync8 } from "node:fs";
24094
24113
  import { resolve as resolve2 } from "node:path";
24095
24114
  function home() {
24096
24115
  return process.env.HOME ?? "/root";
@@ -24109,9 +24128,9 @@ function resolveDualPath(pathStr) {
24109
24128
  const absolute = resolve2(h, rest);
24110
24129
  if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
24111
24130
  const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
24112
- if (!existsSync7(absolute)) {
24131
+ if (!existsSync8(absolute)) {
24113
24132
  const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
24114
- if (existsSync7(legacy)) {
24133
+ if (existsSync8(legacy)) {
24115
24134
  warnLegacyStateOnce(legacy);
24116
24135
  return legacy;
24117
24136
  }
@@ -24136,14 +24155,14 @@ var init_overlay_schema = __esm(() => {
24136
24155
  });
24137
24156
 
24138
24157
  // ../src/config/overlay-loader.ts
24139
- import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync, statSync as statSync2 } from "node:fs";
24158
+ import { existsSync as existsSync9, readFileSync as readFileSync4, readdirSync, statSync as statSync2 } from "node:fs";
24140
24159
  import { resolve as resolve3 } from "node:path";
24141
24160
  function overlayDirFor(agentName3, subdir) {
24142
24161
  const base = resolveDualPath(`~/.switchroom/agents/${agentName3}/${subdir}`);
24143
24162
  return resolve3(base);
24144
24163
  }
24145
24164
  function listYamlFiles(dir) {
24146
- if (!existsSync8(dir))
24165
+ if (!existsSync9(dir))
24147
24166
  return [];
24148
24167
  let entries;
24149
24168
  try {
@@ -24571,8 +24590,36 @@ var init_merge = __esm(() => {
24571
24590
  })(mergeAgentConfig ||= {});
24572
24591
  });
24573
24592
 
24593
+ // ../src/config/notion-workspace-acl.ts
24594
+ function validateNotionWorkspaceConfig(config) {
24595
+ const issues = [];
24596
+ const dbMap = config.notion_workspace?.databases ?? {};
24597
+ const known = new Set(Object.keys(dbMap));
24598
+ for (const [agentName3, agentRaw] of Object.entries(config.agents ?? {})) {
24599
+ if (!agentRaw)
24600
+ continue;
24601
+ const dbFilter = agentRaw.notion_workspace?.databases;
24602
+ if (dbFilter === undefined)
24603
+ continue;
24604
+ if (dbFilter.length === 0) {
24605
+ issues.push(` agents.${agentName3}.notion_workspace.databases is an empty ` + `list. Delete the entire notion_workspace block to remove Notion ` + `access, or list at least one database friendly name.`);
24606
+ continue;
24607
+ }
24608
+ if (config.notion_workspace === undefined) {
24609
+ issues.push(` agents.${agentName3}.notion_workspace is set but the top-level ` + `notion_workspace block is missing. Configure the integration ` + `globally first (vault_key + databases map), then grant per-agent ` + `access.`);
24610
+ continue;
24611
+ }
24612
+ for (const name of dbFilter) {
24613
+ if (!known.has(name)) {
24614
+ issues.push(` agents.${agentName3}.notion_workspace.databases references ` + `unknown database "${name}". Add it to top-level ` + `notion_workspace.databases (run \`switchroom notion list-dbs\` ` + `to see the UUIDs the integration can read), or remove the ` + `reference.`);
24615
+ }
24616
+ }
24617
+ }
24618
+ return issues;
24619
+ }
24620
+
24574
24621
  // ../src/config/loader.ts
24575
- import { readFileSync as readFileSync5, existsSync as existsSync9 } from "node:fs";
24622
+ import { readFileSync as readFileSync5, existsSync as existsSync10 } from "node:fs";
24576
24623
  import { homedir as homedir5 } from "node:os";
24577
24624
  import { resolve as resolve4 } from "node:path";
24578
24625
  function formatZodErrors(error) {
@@ -24639,7 +24686,7 @@ function findConfigFile(startDir) {
24639
24686
  resolve4(userDir, "clerk.yml")
24640
24687
  ].filter(Boolean);
24641
24688
  for (const path of searchPaths) {
24642
- if (existsSync9(path)) {
24689
+ if (existsSync10(path)) {
24643
24690
  return path;
24644
24691
  }
24645
24692
  }
@@ -24647,7 +24694,7 @@ function findConfigFile(startDir) {
24647
24694
  }
24648
24695
  function loadConfig(configPath) {
24649
24696
  const filePath = configPath ?? findConfigFile();
24650
- if (!existsSync9(filePath)) {
24697
+ if (!existsSync10(filePath)) {
24651
24698
  throw new ConfigError(`Config file not found: ${filePath}`);
24652
24699
  }
24653
24700
  let raw;
@@ -24685,6 +24732,10 @@ function loadConfig(configPath) {
24685
24732
  }
24686
24733
  applyAgentOverlays(config);
24687
24734
  validateAllCronTopicAliases(config, filePath);
24735
+ const notionIssues = validateNotionWorkspaceConfig(config);
24736
+ if (notionIssues.length > 0) {
24737
+ throw new ConfigError(`Invalid notion_workspace configuration in ${filePath}`, notionIssues);
24738
+ }
24688
24739
  return config;
24689
24740
  }
24690
24741
  function validateAllCronTopicAliases(config, filePath) {
@@ -24751,7 +24802,7 @@ __export(exports_history, {
24751
24802
  _resetForTests: () => _resetForTests
24752
24803
  });
24753
24804
  import { chmodSync as chmodSync2, mkdirSync as mkdirSync9 } from "fs";
24754
- import { join as join11 } from "path";
24805
+ import { join as join12 } from "path";
24755
24806
  function loadDatabaseClass() {
24756
24807
  if (DatabaseClass != null)
24757
24808
  return DatabaseClass;
@@ -24774,7 +24825,7 @@ function initHistory(stateDir, retentionDays = 30) {
24774
24825
  return;
24775
24826
  const Database = loadDatabaseClass();
24776
24827
  mkdirSync9(stateDir, { recursive: true, mode: 448 });
24777
- const path = join11(stateDir, "history.db");
24828
+ const path = join12(stateDir, "history.db");
24778
24829
  db = new Database(path, { create: true });
24779
24830
  db.exec("PRAGMA journal_mode = WAL");
24780
24831
  db.exec("PRAGMA synchronous = NORMAL");
@@ -24964,11 +25015,11 @@ var DatabaseClass = null, DEFAULT_LIMIT = 10, MAX_LIMIT = 50, db = null;
24964
25015
  var init_history = () => {};
24965
25016
 
24966
25017
  // quota-check.ts
24967
- import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
24968
- import { join as join12 } from "path";
25018
+ import { readFileSync as readFileSync8, existsSync as existsSync13 } from "fs";
25019
+ import { join as join13 } from "path";
24969
25020
  function readOauthToken(claudeConfigDir) {
24970
- const tokenFile = join12(claudeConfigDir, ".oauth-token");
24971
- if (!existsSync12(tokenFile))
25021
+ const tokenFile = join13(claudeConfigDir, ".oauth-token");
25022
+ if (!existsSync13(tokenFile))
24972
25023
  return null;
24973
25024
  try {
24974
25025
  const raw = readFileSync8(tokenFile, "utf-8").trim();
@@ -25450,7 +25501,7 @@ function isDockerRuntime() {
25450
25501
  import * as net3 from "node:net";
25451
25502
  import * as fs from "node:fs";
25452
25503
  import { homedir as homedir7 } from "node:os";
25453
- import { join as join16 } from "node:path";
25504
+ import { join as join17 } from "node:path";
25454
25505
  function defaultBrokerSocketPath() {
25455
25506
  if (fs.existsSync(OPERATOR_SOCKET_PATH))
25456
25507
  return OPERATOR_SOCKET_PATH;
@@ -25459,7 +25510,7 @@ function defaultBrokerSocketPath() {
25459
25510
  return LEGACY_SOCKET_PATH;
25460
25511
  }
25461
25512
  function vaultTokenFilePath(agentSlug) {
25462
- return join16(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
25513
+ return join17(homedir7(), ".switchroom", "agents", agentSlug, ".vault-token");
25463
25514
  }
25464
25515
  function readVaultTokenFile(agentSlug) {
25465
25516
  const filePath = vaultTokenFilePath(agentSlug);
@@ -25614,8 +25665,8 @@ var DEFAULT_TIMEOUT_MS3 = 2000, LEGACY_SOCKET_PATH, OPERATOR_SOCKET_PATH;
25614
25665
  var init_client2 = __esm(() => {
25615
25666
  init_protocol2();
25616
25667
  init_peercred();
25617
- LEGACY_SOCKET_PATH = join16(homedir7(), ".switchroom", "vault-broker.sock");
25618
- OPERATOR_SOCKET_PATH = join16(homedir7(), ".switchroom", "broker-operator", "sock");
25668
+ LEGACY_SOCKET_PATH = join17(homedir7(), ".switchroom", "vault-broker.sock");
25669
+ OPERATOR_SOCKET_PATH = join17(homedir7(), ".switchroom", "broker-operator", "sock");
25619
25670
  });
25620
25671
 
25621
25672
  // ../src/drive/deep-links.ts
@@ -27804,15 +27855,15 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
27804
27855
  }
27805
27856
 
27806
27857
  // gateway/quota-cache.ts
27807
- import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync11 } from "fs";
27808
- import { join as join20, dirname as dirname8 } from "path";
27858
+ import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync11 } from "fs";
27859
+ import { join as join21, dirname as dirname8 } from "path";
27809
27860
  function defaultCachePath() {
27810
- return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join20(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
27861
+ return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join21(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
27811
27862
  }
27812
27863
  function readQuotaCache(opts = {}) {
27813
27864
  const path = opts.path ?? defaultCachePath();
27814
27865
  const now = opts.now ?? Date.now();
27815
- if (!existsSync23(path))
27866
+ if (!existsSync24(path))
27816
27867
  return null;
27817
27868
  let entry;
27818
27869
  try {
@@ -27853,8 +27904,8 @@ var init_quota_cache = __esm(() => {
27853
27904
  });
27854
27905
 
27855
27906
  // gateway/boot-probes.ts
27856
- import { readFileSync as readFileSync23, readdirSync as readdirSync4, existsSync as existsSync24 } from "fs";
27857
- import { join as join21 } from "path";
27907
+ import { readFileSync as readFileSync23, readdirSync as readdirSync4, existsSync as existsSync25 } from "fs";
27908
+ import { join as join22 } from "path";
27858
27909
  import { execFile as execFileCb } from "child_process";
27859
27910
  import { promisify as promisify3 } from "util";
27860
27911
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
@@ -27896,8 +27947,8 @@ function mapPlan(billingType, hasExtra) {
27896
27947
  }
27897
27948
  async function probeAccount(agentDir) {
27898
27949
  return withTimeout("Account", (async () => {
27899
- const claudeDir = join21(agentDir, ".claude");
27900
- const claudeJsonPath = join21(claudeDir, ".claude.json");
27950
+ const claudeDir = join22(agentDir, ".claude");
27951
+ const claudeJsonPath = join22(claudeDir, ".claude.json");
27901
27952
  let cfg = {};
27902
27953
  try {
27903
27954
  const raw = readFileSync23(claudeJsonPath, "utf8");
@@ -27918,10 +27969,10 @@ async function probeAccount(agentDir) {
27918
27969
  let tokenStr = "";
27919
27970
  let status = "ok";
27920
27971
  for (const candidate of [
27921
- join21(claudeDir, ".oauth-token.meta.json"),
27922
- join21(claudeDir, "accounts", "default", ".oauth-token.meta.json")
27972
+ join22(claudeDir, ".oauth-token.meta.json"),
27973
+ join22(claudeDir, "accounts", "default", ".oauth-token.meta.json")
27923
27974
  ]) {
27924
- if (existsSync24(candidate)) {
27975
+ if (existsSync25(candidate)) {
27925
27976
  try {
27926
27977
  const meta = JSON.parse(readFileSync23(candidate, "utf8"));
27927
27978
  if (meta.expiresAt) {
@@ -28098,7 +28149,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
28098
28149
  if (!cgroup)
28099
28150
  return null;
28100
28151
  const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
28101
- if (!existsSync24(procsPath))
28152
+ if (!existsSync25(procsPath))
28102
28153
  return null;
28103
28154
  const pidsRaw = readFileSync23(procsPath, "utf-8");
28104
28155
  const pids = pidsRaw.split(`
@@ -28305,9 +28356,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
28305
28356
  let claudeDirForProbe = null;
28306
28357
  for (const candidate of [
28307
28358
  claudeConfigDir,
28308
- join21(claudeConfigDir, "accounts", "default")
28359
+ join22(claudeConfigDir, "accounts", "default")
28309
28360
  ]) {
28310
- if (existsSync24(join21(candidate, ".oauth-token"))) {
28361
+ if (existsSync25(join22(candidate, ".oauth-token"))) {
28311
28362
  claudeDirForProbe = candidate;
28312
28363
  break;
28313
28364
  }
@@ -28478,7 +28529,7 @@ async function probeUds(label, socketPath, opts = {}) {
28478
28529
  }
28479
28530
  return withTimeout(label, (async () => {
28480
28531
  if (!opts.connectImpl) {
28481
- if (!existsSync24(socketPath)) {
28532
+ if (!existsSync25(socketPath)) {
28482
28533
  return {
28483
28534
  status: "fail",
28484
28535
  label,
@@ -28542,7 +28593,7 @@ async function probeSkills(agentDir, opts = {}) {
28542
28593
  return withTimeout("Skills", (async () => {
28543
28594
  const fs2 = opts.fs ?? realSkillsFs;
28544
28595
  const max = opts.maxNamesShown ?? 3;
28545
- const skillsDir = join21(agentDir, ".claude", "skills");
28596
+ const skillsDir = join22(agentDir, ".claude", "skills");
28546
28597
  if (!fs2.exists(skillsDir)) {
28547
28598
  return { status: "ok", label: "Skills", detail: "no skills dir" };
28548
28599
  }
@@ -28557,17 +28608,17 @@ async function probeSkills(agentDir, opts = {}) {
28557
28608
  }
28558
28609
  const dangling = [];
28559
28610
  for (const name of entries) {
28560
- const skillPath = join21(skillsDir, name);
28611
+ const skillPath = join22(skillsDir, name);
28561
28612
  if (!fs2.exists(skillPath)) {
28562
28613
  dangling.push(name);
28563
28614
  continue;
28564
28615
  }
28565
- const skillMd = join21(skillPath, "SKILL.md");
28616
+ const skillMd = join22(skillPath, "SKILL.md");
28566
28617
  if (!fs2.exists(skillMd) && !fs2.exists(skillPath + ".md")) {
28567
28618
  continue;
28568
28619
  }
28569
28620
  }
28570
- const overlayDir = opts.overlaySkillsDir ?? join21(agentDir, "skills.d");
28621
+ const overlayDir = opts.overlaySkillsDir ?? join22(agentDir, "skills.d");
28571
28622
  const overlaySlugs = new Set;
28572
28623
  if (fs2.exists(overlayDir)) {
28573
28624
  let overlayEntries = [];
@@ -28622,16 +28673,16 @@ var init_boot_probes = __esm(() => {
28622
28673
  const { statSync: statSync7 } = __require("fs");
28623
28674
  return statSync7(p).mtimeMs;
28624
28675
  },
28625
- exists: (p) => existsSync24(p)
28676
+ exists: (p) => existsSync25(p)
28626
28677
  };
28627
28678
  realSkillsFs = {
28628
28679
  readdir: (p) => readdirSync4(p),
28629
- exists: (p) => existsSync24(p)
28680
+ exists: (p) => existsSync25(p)
28630
28681
  };
28631
28682
  });
28632
28683
 
28633
28684
  // gateway/boot-issue-cache.ts
28634
- import { existsSync as existsSync25, readFileSync as readFileSync24, writeFileSync as writeFileSync14, mkdirSync as mkdirSync12, renameSync as renameSync9 } from "fs";
28685
+ import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync14, mkdirSync as mkdirSync12, renameSync as renameSync9 } from "fs";
28635
28686
  import { dirname as dirname9 } from "path";
28636
28687
  function fingerprintProbe(key, r) {
28637
28688
  if (r.status === "ok")
@@ -28711,7 +28762,7 @@ function diffProbes(probes, cache, opts = {}) {
28711
28762
  return out;
28712
28763
  }
28713
28764
  function loadCache(path, now = Date.now) {
28714
- if (!existsSync25(path))
28765
+ if (!existsSync26(path))
28715
28766
  return { ...EMPTY_CACHE, probes: {} };
28716
28767
  let raw;
28717
28768
  try {
@@ -28782,7 +28833,7 @@ __export(exports_boot_card, {
28782
28833
  renderBootCard: () => renderBootCard,
28783
28834
  renderAccountRows: () => renderAuthLine
28784
28835
  });
28785
- import { join as join22 } from "path";
28836
+ import { join as join23 } from "path";
28786
28837
  function resolvePersonaName(slug, loadConfig3) {
28787
28838
  try {
28788
28839
  const config = loadConfig3 ? loadConfig3() : loadConfig();
@@ -28863,7 +28914,7 @@ function renderBootCard(opts) {
28863
28914
  `);
28864
28915
  }
28865
28916
  async function runAllProbes(opts) {
28866
- const claudeDir = join22(opts.agentDir, ".claude");
28917
+ const claudeDir = join23(opts.agentDir, ".claude");
28867
28918
  const probes = {};
28868
28919
  const slug = opts.agentSlug ?? opts.agentName;
28869
28920
  await Promise.allSettled([
@@ -29107,10 +29158,10 @@ import { randomBytes as randomBytes5, scryptSync, createCipheriv, createDecipher
29107
29158
  import {
29108
29159
  readFileSync as readFileSync31,
29109
29160
  writeFileSync as writeFileSync19,
29110
- existsSync as existsSync32,
29161
+ existsSync as existsSync33,
29111
29162
  renameSync as renameSync11,
29112
29163
  mkdirSync as mkdirSync18,
29113
- unlinkSync as unlinkSync12,
29164
+ unlinkSync as unlinkSync13,
29114
29165
  lstatSync,
29115
29166
  realpathSync
29116
29167
  } from "node:fs";
@@ -29145,7 +29196,7 @@ function normalizeSecrets(raw) {
29145
29196
  return out;
29146
29197
  }
29147
29198
  function openVault(passphrase, vaultPath) {
29148
- if (!existsSync32(vaultPath)) {
29199
+ if (!existsSync33(vaultPath)) {
29149
29200
  throw new VaultError(`Vault file not found: ${vaultPath}`);
29150
29201
  }
29151
29202
  let vaultFile;
@@ -29196,15 +29247,15 @@ var init_vault = __esm(() => {
29196
29247
  // ../src/vault/resolver.ts
29197
29248
  import {
29198
29249
  chmodSync as chmodSync4,
29199
- closeSync as closeSync7,
29250
+ closeSync as closeSync8,
29200
29251
  mkdirSync as mkdirSync19,
29201
29252
  mkdtempSync as mkdtempSync2,
29202
- openSync as openSync7,
29253
+ openSync as openSync8,
29203
29254
  rmSync as rmSync3,
29204
29255
  statSync as statSync11,
29205
29256
  writeSync as writeSync2
29206
29257
  } from "node:fs";
29207
- import { join as join31 } from "node:path";
29258
+ import { join as join32 } from "node:path";
29208
29259
  import { tmpdir } from "node:os";
29209
29260
  import { constants as fsConstants } from "node:fs";
29210
29261
  function isVaultReference(value) {
@@ -29256,26 +29307,26 @@ function materializationRoot() {
29256
29307
  return cachedRoot;
29257
29308
  const xdg = process.env.XDG_RUNTIME_DIR;
29258
29309
  if (xdg) {
29259
- const base = join31(xdg, "switchroom", "vault");
29310
+ const base = join32(xdg, "switchroom", "vault");
29260
29311
  mkdirSync19(base, { recursive: true, mode: 448 });
29261
- cachedRoot = mkdtempSync2(join31(base, "run-"));
29312
+ cachedRoot = mkdtempSync2(join32(base, "run-"));
29262
29313
  } else {
29263
- cachedRoot = mkdtempSync2(join31(tmpdir(), "switchroom-vault-"));
29314
+ cachedRoot = mkdtempSync2(join32(tmpdir(), "switchroom-vault-"));
29264
29315
  }
29265
29316
  chmodSync4(cachedRoot, 448);
29266
29317
  return cachedRoot;
29267
29318
  }
29268
29319
  function writeFileExclusive(filePath, content) {
29269
29320
  const buf = typeof content === "string" ? Buffer.from(content, "utf8") : content;
29270
- const fd = openSync7(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
29321
+ const fd = openSync8(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
29271
29322
  try {
29272
29323
  writeSync2(fd, buf);
29273
29324
  } finally {
29274
- closeSync7(fd);
29325
+ closeSync8(fd);
29275
29326
  }
29276
29327
  }
29277
29328
  function materializeFilesEntry(key, files) {
29278
- const dir = join31(materializationRoot(), key);
29329
+ const dir = join32(materializationRoot(), key);
29279
29330
  if (materializedDirs.has(dir)) {
29280
29331
  try {
29281
29332
  rmSync3(dir, { recursive: true, force: true });
@@ -29291,7 +29342,7 @@ function materializeFilesEntry(key, files) {
29291
29342
  if (filename.includes("/") || filename.includes("\\") || filename === ".." || filename === "." || filename.includes("\x00")) {
29292
29343
  throw new Error(`Refusing to materialize vault file with unsafe name: ${filename}`);
29293
29344
  }
29294
- const filePath = join31(dir, filename);
29345
+ const filePath = join32(dir, filename);
29295
29346
  const content = encoding === "base64" ? Buffer.from(value, "base64") : value;
29296
29347
  writeFileExclusive(filePath, content);
29297
29348
  }
@@ -29424,7 +29475,7 @@ __export(exports_materialize_bot_token, {
29424
29475
  materializeBotToken: () => materializeBotToken,
29425
29476
  BotTokenMaterializeError: () => BotTokenMaterializeError
29426
29477
  });
29427
- import { existsSync as existsSync33 } from "node:fs";
29478
+ import { existsSync as existsSync34 } from "node:fs";
29428
29479
  function pickConfiguredToken(config, agentName3) {
29429
29480
  if (agentName3) {
29430
29481
  const agent = config.agents?.[agentName3];
@@ -29438,7 +29489,7 @@ function tryDirectVaultRead(ref, config, passphrase) {
29438
29489
  if (!passphrase)
29439
29490
  return null;
29440
29491
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
29441
- if (!existsSync33(vaultPath))
29492
+ if (!existsSync34(vaultPath))
29442
29493
  return null;
29443
29494
  try {
29444
29495
  const secrets = openVault(passphrase, vaultPath);
@@ -29743,7 +29794,7 @@ __export(exports_tmux, {
29743
29794
  captureAgentPane: () => captureAgentPane
29744
29795
  });
29745
29796
  import { execFileSync as execFileSync4 } from "node:child_process";
29746
- import { mkdirSync as mkdirSync20, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync13, writeFileSync as writeFileSync20 } from "node:fs";
29797
+ import { mkdirSync as mkdirSync20, readdirSync as readdirSync6, statSync as statSync12, unlinkSync as unlinkSync14, writeFileSync as writeFileSync20 } from "node:fs";
29747
29798
  import { resolve as resolve7 } from "node:path";
29748
29799
  function captureAgentPane(opts) {
29749
29800
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -29857,7 +29908,7 @@ function pruneOldReports(dir, retain) {
29857
29908
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
29858
29909
  for (const stale of files.slice(retain)) {
29859
29910
  try {
29860
- unlinkSync13(stale.full);
29911
+ unlinkSync14(stale.full);
29861
29912
  } catch {}
29862
29913
  }
29863
29914
  }
@@ -30453,14 +30504,14 @@ import {
30453
30504
  renameSync as renameSync12,
30454
30505
  realpathSync as realpathSync2,
30455
30506
  chmodSync as chmodSync5,
30456
- openSync as openSync8,
30457
- closeSync as closeSync8,
30458
- existsSync as existsSync34,
30459
- unlinkSync as unlinkSync14,
30507
+ openSync as openSync9,
30508
+ closeSync as closeSync9,
30509
+ existsSync as existsSync35,
30510
+ unlinkSync as unlinkSync15,
30460
30511
  appendFileSync as appendFileSync3
30461
30512
  } from "fs";
30462
30513
  import { homedir as homedir12 } from "os";
30463
- import { join as join32, extname, sep as sep3, basename as basename7 } from "path";
30514
+ import { join as join33, extname, sep as sep3, basename as basename7 } from "path";
30464
30515
 
30465
30516
  // plugin-logger.ts
30466
30517
  import { appendFileSync, mkdirSync, renameSync, statSync, existsSync } from "fs";
@@ -37702,6 +37753,7 @@ function classifyInbound(text) {
37702
37753
  // silence-poke.ts
37703
37754
  var DEFAULT_THRESHOLDS = {
37704
37755
  ack: 1e4,
37756
+ awarenessPing: 60000,
37705
37757
  soft: 75000,
37706
37758
  firm: 180000,
37707
37759
  fallback: 300000,
@@ -37728,6 +37780,7 @@ function startTurn(key, now) {
37728
37780
  lastThinkingAt: null,
37729
37781
  fallbackFired: false,
37730
37782
  ackPokeFired: false,
37783
+ awarenessPingFired: false,
37731
37784
  lastPokeFiredAt: null,
37732
37785
  inFlightTools: new Map
37733
37786
  });
@@ -37845,6 +37898,42 @@ function tick(now) {
37845
37898
  });
37846
37899
  continue;
37847
37900
  }
37901
+ if (!s.awarenessPingFired && s.lastOutboundAt == null && !s.subagentDispatchActive && silence >= thresholds.awarenessPing) {
37902
+ s.awarenessPingFired = true;
37903
+ const { chatId, threadId } = parseKey(key);
37904
+ const recentThinking = s.lastThinkingAt != null && now - s.lastThinkingAt < 30000;
37905
+ const fallbackKind = recentThinking ? "thinking" : "working";
37906
+ const inFlightTools = Array.from(s.inFlightTools.values()).sort((a, b) => a.startedAt - b.startedAt).map((t) => ({
37907
+ name: t.name,
37908
+ label: t.label,
37909
+ durationMs: now - t.startedAt
37910
+ }));
37911
+ activeDeps.emitMetric({
37912
+ kind: "awareness_ping_sent",
37913
+ key,
37914
+ fallback_kind: fallbackKind,
37915
+ silence_ms: silence
37916
+ });
37917
+ try {
37918
+ const ret = activeDeps.onAwarenessPing({
37919
+ key,
37920
+ chatId,
37921
+ threadId,
37922
+ fallbackKind,
37923
+ silenceMs: silence,
37924
+ inFlightTools
37925
+ });
37926
+ if (ret != null && typeof ret.then === "function") {
37927
+ ret.catch((err) => {
37928
+ process.stderr.write(`silence-poke: awareness-ping handler rejected: ${err}
37929
+ `);
37930
+ });
37931
+ }
37932
+ } catch (err) {
37933
+ process.stderr.write(`silence-poke: awareness-ping handler threw: ${err}
37934
+ `);
37935
+ }
37936
+ }
37848
37937
  if (s.pokesFired === 0 && silence >= softThreshold) {
37849
37938
  s.pokeArmed = { level: "soft" };
37850
37939
  s.pokesFired = 1;
@@ -38167,6 +38256,45 @@ function recordSilentTurnEnd(args, deps) {
38167
38256
  }
38168
38257
  var recordUndeliveredTurnEnd = recordSilentTurnEnd;
38169
38258
 
38259
+ // ack-flag.ts
38260
+ import { closeSync, existsSync as existsSync6, openSync, unlinkSync as unlinkSync2 } from "node:fs";
38261
+ import { join as join7 } from "node:path";
38262
+ var ACK_SENT_MARKER = "ack-sent.flag";
38263
+ function markerPath() {
38264
+ const dir = process.env.TELEGRAM_STATE_DIR;
38265
+ if (!dir)
38266
+ return null;
38267
+ return join7(dir, ACK_SENT_MARKER);
38268
+ }
38269
+ function markAckSent() {
38270
+ const path = markerPath();
38271
+ if (path == null)
38272
+ return;
38273
+ if (existsSync6(path))
38274
+ return;
38275
+ try {
38276
+ const fd = openSync(path, "w");
38277
+ closeSync(fd);
38278
+ } catch (err) {
38279
+ process.stderr.write(`ack-flag: markAckSent failed path=${path}: ${err}
38280
+ `);
38281
+ }
38282
+ }
38283
+ function clearAckSent() {
38284
+ const path = markerPath();
38285
+ if (path == null)
38286
+ return;
38287
+ try {
38288
+ unlinkSync2(path);
38289
+ } catch (err) {
38290
+ const code = err?.code;
38291
+ if (code === "ENOENT")
38292
+ return;
38293
+ process.stderr.write(`ack-flag: clearAckSent failed path=${path}: ${err}
38294
+ `);
38295
+ }
38296
+ }
38297
+
38170
38298
  // final-answer-detect.ts
38171
38299
  var FINAL_ANSWER_MIN_CHARS = 200;
38172
38300
  function isFinalAnswerReply(input) {
@@ -38590,7 +38718,7 @@ async function gatewayStartupRetry(fn, opts = {}) {
38590
38718
 
38591
38719
  // gateway/quarantine.ts
38592
38720
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "node:fs";
38593
- import { join as join7 } from "node:path";
38721
+ import { join as join8 } from "node:path";
38594
38722
  var QUARANTINE_FILENAME = "quarantine.json";
38595
38723
  function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.now) {
38596
38724
  mkdirSync6(telegramStateDir, { recursive: true, mode: 448 });
@@ -38600,7 +38728,7 @@ function writeQuarantineMarker(telegramStateDir, reason, detail, nowFn = Date.no
38600
38728
  ts: nowFn(),
38601
38729
  detail
38602
38730
  };
38603
- writeFileSync4(join7(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
38731
+ writeFileSync4(join8(telegramStateDir, QUARANTINE_FILENAME), JSON.stringify(marker) + `
38604
38732
  `, "utf-8");
38605
38733
  }
38606
38734
 
@@ -39485,7 +39613,7 @@ import * as net2 from "node:net";
39485
39613
  init_protocol();
39486
39614
  import { homedir as homedir4 } from "node:os";
39487
39615
  import { randomUUID as randomUUID4 } from "node:crypto";
39488
- import { join as join9 } from "node:path";
39616
+ import { join as join10 } from "node:path";
39489
39617
  var DEFAULT_TIMEOUT_MS2 = 5000;
39490
39618
  function reviveDate2(v) {
39491
39619
  if (v == null)
@@ -39496,7 +39624,7 @@ function reviveDate2(v) {
39496
39624
  return Number.isNaN(d.getTime()) ? null : d;
39497
39625
  }
39498
39626
  function operatorSocketPath2(home = homedir4()) {
39499
- return join9(home, ".switchroom", "state", "auth-broker-operator", "sock");
39627
+ return join10(home, ".switchroom", "state", "auth-broker-operator", "sock");
39500
39628
  }
39501
39629
  function resolveAuthBrokerSocketPath2(opts) {
39502
39630
  if (opts?.socket)
@@ -39828,16 +39956,16 @@ function createFleetFallbackGate(opts) {
39828
39956
 
39829
39957
  // gateway/auth-add-flow.ts
39830
39958
  import { spawn } from "node:child_process";
39831
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
39959
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
39832
39960
  import { homedir as homedir6 } from "node:os";
39833
- import { join as join10 } from "node:path";
39961
+ import { join as join11 } from "node:path";
39834
39962
  import { randomBytes as randomBytes2 } from "node:crypto";
39835
39963
 
39836
39964
  // ../src/auth/manager.ts
39837
39965
  import {
39838
39966
  readFileSync as readFileSync6,
39839
39967
  readdirSync as readdirSync2,
39840
- existsSync as existsSync10,
39968
+ existsSync as existsSync11,
39841
39969
  writeFileSync as writeFileSync5,
39842
39970
  mkdirSync as mkdirSync7,
39843
39971
  mkdtempSync,
@@ -39864,7 +39992,7 @@ function parseSetupTokenUrl(output) {
39864
39992
  }
39865
39993
  function readTokenFromCredentialsFile(credentialsFilePath) {
39866
39994
  try {
39867
- if (!existsSync10(credentialsFilePath))
39995
+ if (!existsSync11(credentialsFilePath))
39868
39996
  return null;
39869
39997
  const raw = readFileSync6(credentialsFilePath, "utf-8");
39870
39998
  const parsed = JSON.parse(raw);
@@ -39883,7 +40011,7 @@ function readTokenFromCredentialsFile(credentialsFilePath) {
39883
40011
  var pendingAuthAddFlows = new Map;
39884
40012
  function pickScratchDir(label, home2 = homedir6()) {
39885
40013
  const suffix = randomBytes2(8).toString("hex");
39886
- return join10(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
40014
+ return join11(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
39887
40015
  }
39888
40016
  function cleanScratchDir(scratchDir) {
39889
40017
  try {
@@ -39944,7 +40072,7 @@ async function startAccountAuthSession(label, opts = {}) {
39944
40072
  async function submitAccountAuthCode(flow, code, opts = {}) {
39945
40073
  const pollIntervalMs = opts.pollIntervalMs ?? 250;
39946
40074
  const pollTimeoutMs = opts.pollTimeoutMs ?? 120000;
39947
- const credentialsPath = join10(flow.scratchDir, ".credentials.json");
40075
+ const credentialsPath = join11(flow.scratchDir, ".credentials.json");
39948
40076
  if (!flow.child.stdin || flow.child.stdin.destroyed) {
39949
40077
  cleanScratchDir(flow.scratchDir);
39950
40078
  throw new Error("claude setup-token process stdin is not writable (child may have exited)");
@@ -39954,7 +40082,7 @@ async function submitAccountAuthCode(flow, code, opts = {}) {
39954
40082
  const deadline = Date.now() + pollTimeoutMs;
39955
40083
  while (Date.now() < deadline) {
39956
40084
  await new Promise((r) => setTimeout(r, pollIntervalMs));
39957
- if (existsSync11(credentialsPath)) {
40085
+ if (existsSync12(credentialsPath)) {
39958
40086
  const token = readTokenFromCredentialsFile(credentialsPath);
39959
40087
  if (token) {
39960
40088
  try {
@@ -41514,8 +41642,8 @@ function isTurnFlushSafetyEnabled(env = process.env) {
41514
41642
  }
41515
41643
 
41516
41644
  // handoff-continuity.ts
41517
- import { readFileSync as readFileSync9, unlinkSync as unlinkSync2, existsSync as existsSync13, writeFileSync as writeFileSync6, renameSync as renameSync2 } from "node:fs";
41518
- import { dirname as dirname7, join as join13 } from "node:path";
41645
+ import { readFileSync as readFileSync9, unlinkSync as unlinkSync3, existsSync as existsSync14, writeFileSync as writeFileSync6, renameSync as renameSync2 } from "node:fs";
41646
+ import { dirname as dirname7, join as join14 } from "node:path";
41519
41647
  var TOPIC_DISPLAY_MAX = 117;
41520
41648
  var HANDOFF_TOPIC_FILENAME = ".handoff-topic";
41521
41649
  var LAST_TURN_SUMMARY_FILENAME = ".last-turn-summary";
@@ -41526,8 +41654,8 @@ function resolveAgentDirFromEnv() {
41526
41654
  return dirname7(state3);
41527
41655
  }
41528
41656
  function readHandoffTopic(agentDir) {
41529
- const p = join13(agentDir, HANDOFF_TOPIC_FILENAME);
41530
- if (!existsSync13(p))
41657
+ const p = join14(agentDir, HANDOFF_TOPIC_FILENAME);
41658
+ if (!existsSync14(p))
41531
41659
  return null;
41532
41660
  let raw;
41533
41661
  try {
@@ -41545,8 +41673,8 @@ function readHandoffTopic(agentDir) {
41545
41673
  return topic;
41546
41674
  }
41547
41675
  function readLastTurnSummary(agentDir) {
41548
- const p = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
41549
- if (!existsSync13(p))
41676
+ const p = join14(agentDir, LAST_TURN_SUMMARY_FILENAME);
41677
+ if (!existsSync14(p))
41550
41678
  return null;
41551
41679
  let raw;
41552
41680
  try {
@@ -41565,16 +41693,16 @@ function readLastTurnSummary(agentDir) {
41565
41693
  }
41566
41694
  function consumeHandoffTopic(agentDir) {
41567
41695
  const primary = readHandoffTopic(agentDir);
41568
- const primaryPath = join13(agentDir, HANDOFF_TOPIC_FILENAME);
41569
- const fallbackPath = join13(agentDir, LAST_TURN_SUMMARY_FILENAME);
41696
+ const primaryPath = join14(agentDir, HANDOFF_TOPIC_FILENAME);
41697
+ const fallbackPath = join14(agentDir, LAST_TURN_SUMMARY_FILENAME);
41570
41698
  const removeFallback = () => {
41571
41699
  try {
41572
- unlinkSync2(fallbackPath);
41700
+ unlinkSync3(fallbackPath);
41573
41701
  } catch {}
41574
41702
  };
41575
41703
  if (primary !== null) {
41576
41704
  try {
41577
- unlinkSync2(primaryPath);
41705
+ unlinkSync3(primaryPath);
41578
41706
  } catch {}
41579
41707
  removeFallback();
41580
41708
  return primary;
@@ -41619,15 +41747,15 @@ function escapeMarkdownV2(s) {
41619
41747
  }
41620
41748
 
41621
41749
  // active-reactions.ts
41622
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync14, unlinkSync as unlinkSync3 } from "node:fs";
41623
- import { join as join14 } from "node:path";
41750
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync15, unlinkSync as unlinkSync4 } from "node:fs";
41751
+ import { join as join15 } from "node:path";
41624
41752
  var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
41625
41753
  function reactionsPath(agentDir) {
41626
- return join14(agentDir, ACTIVE_REACTIONS_FILENAME);
41754
+ return join15(agentDir, ACTIVE_REACTIONS_FILENAME);
41627
41755
  }
41628
41756
  function readActiveReactions(agentDir) {
41629
41757
  const p = reactionsPath(agentDir);
41630
- if (!existsSync14(p))
41758
+ if (!existsSync15(p))
41631
41759
  return [];
41632
41760
  let raw;
41633
41761
  try {
@@ -41657,7 +41785,7 @@ function writeActiveReactions(agentDir, reactions) {
41657
41785
  const p = reactionsPath(agentDir);
41658
41786
  if (reactions.length === 0) {
41659
41787
  try {
41660
- unlinkSync3(p);
41788
+ unlinkSync4(p);
41661
41789
  } catch {}
41662
41790
  return;
41663
41791
  }
@@ -41682,20 +41810,20 @@ function removeActiveReaction(agentDir, chatId, messageId) {
41682
41810
  }
41683
41811
  function clearActiveReactions(agentDir) {
41684
41812
  try {
41685
- unlinkSync3(reactionsPath(agentDir));
41813
+ unlinkSync4(reactionsPath(agentDir));
41686
41814
  } catch {}
41687
41815
  }
41688
41816
 
41689
41817
  // active-reactions.ts
41690
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync4, existsSync as existsSync15, unlinkSync as unlinkSync4 } from "node:fs";
41691
- import { join as join15 } from "node:path";
41818
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync4, existsSync as existsSync16, unlinkSync as unlinkSync5 } from "node:fs";
41819
+ import { join as join16 } from "node:path";
41692
41820
  var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
41693
41821
  function reactionsPath2(agentDir) {
41694
- return join15(agentDir, ACTIVE_REACTIONS_FILENAME2);
41822
+ return join16(agentDir, ACTIVE_REACTIONS_FILENAME2);
41695
41823
  }
41696
41824
  function readActiveReactions2(agentDir) {
41697
41825
  const p = reactionsPath2(agentDir);
41698
- if (!existsSync15(p))
41826
+ if (!existsSync16(p))
41699
41827
  return [];
41700
41828
  let raw;
41701
41829
  try {
@@ -41723,7 +41851,7 @@ function readActiveReactions2(agentDir) {
41723
41851
  }
41724
41852
  function clearActiveReactions2(agentDir) {
41725
41853
  try {
41726
- unlinkSync4(reactionsPath2(agentDir));
41854
+ unlinkSync5(reactionsPath2(agentDir));
41727
41855
  } catch {}
41728
41856
  }
41729
41857
 
@@ -42576,14 +42704,14 @@ async function approvalRecord(args, opts) {
42576
42704
  }
42577
42705
 
42578
42706
  // quota-check.ts
42579
- import { readFileSync as readFileSync13, existsSync as existsSync17 } from "fs";
42580
- import { join as join17 } from "path";
42707
+ import { readFileSync as readFileSync13, existsSync as existsSync18 } from "fs";
42708
+ import { join as join18 } from "path";
42581
42709
  var OAUTH_BETA2 = "oauth-2025-04-20";
42582
42710
  var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
42583
42711
  var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
42584
42712
  function readOauthToken2(claudeConfigDir) {
42585
- const tokenFile = join17(claudeConfigDir, ".oauth-token");
42586
- if (!existsSync17(tokenFile))
42713
+ const tokenFile = join18(claudeConfigDir, ".oauth-token");
42714
+ if (!existsSync18(tokenFile))
42587
42715
  return null;
42588
42716
  try {
42589
42717
  const raw = readFileSync13(tokenFile, "utf-8").trim();
@@ -43182,7 +43310,7 @@ init_schema();
43182
43310
  init_paths();
43183
43311
  init_overlay_loader();
43184
43312
  init_merge();
43185
- import { readFileSync as readFileSync14, existsSync as existsSync18 } from "node:fs";
43313
+ import { readFileSync as readFileSync14, existsSync as existsSync19 } from "node:fs";
43186
43314
  import { homedir as homedir8 } from "node:os";
43187
43315
  import { resolve as resolve5 } from "node:path";
43188
43316
 
@@ -43258,7 +43386,7 @@ function findConfigFile2(startDir) {
43258
43386
  resolve5(userDir, "clerk.yml")
43259
43387
  ].filter(Boolean);
43260
43388
  for (const path of searchPaths) {
43261
- if (existsSync18(path)) {
43389
+ if (existsSync19(path)) {
43262
43390
  return path;
43263
43391
  }
43264
43392
  }
@@ -43266,7 +43394,7 @@ function findConfigFile2(startDir) {
43266
43394
  }
43267
43395
  function loadConfig2(configPath) {
43268
43396
  const filePath = configPath ?? findConfigFile2();
43269
- if (!existsSync18(filePath)) {
43397
+ if (!existsSync19(filePath)) {
43270
43398
  throw new ConfigError2(`Config file not found: ${filePath}`);
43271
43399
  }
43272
43400
  let raw;
@@ -43304,6 +43432,10 @@ function loadConfig2(configPath) {
43304
43432
  }
43305
43433
  applyAgentOverlays(config);
43306
43434
  validateAllCronTopicAliases2(config, filePath);
43435
+ const notionIssues = validateNotionWorkspaceConfig(config);
43436
+ if (notionIssues.length > 0) {
43437
+ throw new ConfigError2(`Invalid notion_workspace configuration in ${filePath}`, notionIssues);
43438
+ }
43307
43439
  return config;
43308
43440
  }
43309
43441
  function validateAllCronTopicAliases2(config, filePath) {
@@ -43695,9 +43827,9 @@ function resolveOutboundTopic(config, event) {
43695
43827
  }
43696
43828
 
43697
43829
  // ../src/agents/perf.ts
43698
- import { existsSync as existsSync19, readFileSync as readFileSync15 } from "node:fs";
43830
+ import { existsSync as existsSync20, readFileSync as readFileSync15 } from "node:fs";
43699
43831
  function readTurnUsages(jsonlPath, lastN) {
43700
- if (!existsSync19(jsonlPath))
43832
+ if (!existsSync20(jsonlPath))
43701
43833
  return [];
43702
43834
  if (lastN <= 0)
43703
43835
  return [];
@@ -43841,7 +43973,7 @@ function nextCompactNotify(state3, ev) {
43841
43973
  }
43842
43974
 
43843
43975
  // gateway/hostd-dispatch.ts
43844
- import { existsSync as existsSync20 } from "node:fs";
43976
+ import { existsSync as existsSync21 } from "node:fs";
43845
43977
  import { randomBytes as randomBytes3 } from "node:crypto";
43846
43978
 
43847
43979
  // ../src/host-control/client.ts
@@ -44132,13 +44264,13 @@ function hostdSocketPath(agentName3) {
44132
44264
  function hostdWillBeUsed(agentName3) {
44133
44265
  if (!isHostdEnabled())
44134
44266
  return false;
44135
- return existsSync20(hostdSocketPath(agentName3));
44267
+ return existsSync21(hostdSocketPath(agentName3));
44136
44268
  }
44137
44269
  async function tryHostdDispatch(agentName3, req) {
44138
44270
  if (!isHostdEnabled())
44139
44271
  return "not-configured";
44140
44272
  const sockPath = hostdSocketPath(agentName3);
44141
- if (!existsSync20(sockPath))
44273
+ if (!existsSync21(sockPath))
44142
44274
  return "not-configured";
44143
44275
  try {
44144
44276
  return await hostdRequest({ socketPath: sockPath, timeoutMs: 5000 }, req);
@@ -44162,7 +44294,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
44162
44294
  if (!isHostdEnabled())
44163
44295
  return "not-configured";
44164
44296
  const sockPath = hostdSocketPath(agentName3);
44165
- if (!existsSync20(sockPath))
44297
+ if (!existsSync21(sockPath))
44166
44298
  return "not-configured";
44167
44299
  try {
44168
44300
  const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
@@ -44183,7 +44315,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
44183
44315
  if (!isHostdEnabled())
44184
44316
  return "not-configured";
44185
44317
  const sockPath = hostdSocketPath(agentName3);
44186
- if (!existsSync20(sockPath))
44318
+ if (!existsSync21(sockPath))
44187
44319
  return "not-configured";
44188
44320
  const now = opts.now ?? Date.now;
44189
44321
  const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
@@ -44261,7 +44393,7 @@ function shouldSweepChatAtBoot(chatId) {
44261
44393
  }
44262
44394
 
44263
44395
  // gateway/ipc-server.ts
44264
- import { renameSync as renameSync5, unlinkSync as unlinkSync5 } from "fs";
44396
+ import { renameSync as renameSync5, unlinkSync as unlinkSync6 } from "fs";
44265
44397
  var MAX_BUFFER_SIZE = 1024 * 1024;
44266
44398
  var VALID_OPERATOR_KINDS = new Set([
44267
44399
  "credentials-expired",
@@ -44379,7 +44511,7 @@ function createIpcServer(options) {
44379
44511
  renameSync5(socketPath, socketPath + ".bak");
44380
44512
  } catch {}
44381
44513
  try {
44382
- unlinkSync5(socketPath + ".bak");
44514
+ unlinkSync6(socketPath + ".bak");
44383
44515
  } catch {}
44384
44516
  const clients = new Set;
44385
44517
  const agentIndex = new Map;
@@ -46442,7 +46574,7 @@ function escapeBody(s) {
46442
46574
  }
46443
46575
 
46444
46576
  // gateway/pid-file.ts
46445
- import { writeFileSync as writeFileSync9, readFileSync as readFileSync16, unlinkSync as unlinkSync6, renameSync as renameSync6 } from "node:fs";
46577
+ import { writeFileSync as writeFileSync9, readFileSync as readFileSync16, unlinkSync as unlinkSync7, renameSync as renameSync6 } from "node:fs";
46446
46578
  function writePidFile(path, record) {
46447
46579
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
46448
46580
  writeFileSync9(tmp, JSON.stringify(record), "utf-8");
@@ -46450,7 +46582,7 @@ function writePidFile(path, record) {
46450
46582
  }
46451
46583
  function clearPidFile(path) {
46452
46584
  try {
46453
- unlinkSync6(path);
46585
+ unlinkSync7(path);
46454
46586
  } catch {}
46455
46587
  }
46456
46588
 
@@ -46667,7 +46799,7 @@ function safeCount(fn) {
46667
46799
  }
46668
46800
 
46669
46801
  // gateway/session-marker.ts
46670
- import { writeFileSync as writeFileSync10, readFileSync as readFileSync18, renameSync as renameSync7, unlinkSync as unlinkSync7 } from "node:fs";
46802
+ import { writeFileSync as writeFileSync10, readFileSync as readFileSync18, renameSync as renameSync7, unlinkSync as unlinkSync8 } from "node:fs";
46671
46803
  function writeSessionMarker(path, marker) {
46672
46804
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
46673
46805
  writeFileSync10(tmp, JSON.stringify(marker), "utf-8");
@@ -46697,7 +46829,7 @@ function shouldFireRestartBanner(input) {
46697
46829
  }
46698
46830
 
46699
46831
  // gateway/clean-shutdown-marker.ts
46700
- import { writeFileSync as writeFileSync11, readFileSync as readFileSync19, renameSync as renameSync8, unlinkSync as unlinkSync8 } from "node:fs";
46832
+ import { writeFileSync as writeFileSync11, readFileSync as readFileSync19, renameSync as renameSync8, unlinkSync as unlinkSync9 } from "node:fs";
46701
46833
  var DEFAULT_MAX_AGE_MS = 60000;
46702
46834
  function writeCleanShutdownMarker(path, marker) {
46703
46835
  const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
@@ -46721,7 +46853,7 @@ function readCleanShutdownMarker(path) {
46721
46853
  }
46722
46854
  function clearCleanShutdownMarker(path) {
46723
46855
  try {
46724
- unlinkSync8(path);
46856
+ unlinkSync9(path);
46725
46857
  } catch {}
46726
46858
  }
46727
46859
  function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
@@ -47501,16 +47633,16 @@ function classifyAdminGate(text, myAgentName) {
47501
47633
 
47502
47634
  // subagent-watcher.ts
47503
47635
  import {
47504
- existsSync as existsSync22,
47505
- openSync as openSync2,
47636
+ existsSync as existsSync23,
47637
+ openSync as openSync3,
47506
47638
  readSync,
47507
47639
  statSync as statSync6,
47508
- closeSync as closeSync2,
47640
+ closeSync as closeSync3,
47509
47641
  watch,
47510
47642
  readdirSync as readdirSync3,
47511
47643
  readFileSync as readFileSync21
47512
47644
  } from "fs";
47513
- import { join as join19 } from "path";
47645
+ import { join as join20 } from "path";
47514
47646
 
47515
47647
  // operator-events.ts
47516
47648
  var DEFAULT_OPERATOR_EVENT_COOLDOWN_MS2 = 5 * 60000;
@@ -47752,29 +47884,29 @@ function bumpSubagentActivity(db2, args) {
47752
47884
 
47753
47885
  // gateway/turn-active-marker.ts
47754
47886
  import {
47755
- closeSync,
47756
- existsSync as existsSync21,
47887
+ closeSync as closeSync2,
47888
+ existsSync as existsSync22,
47757
47889
  mkdirSync as mkdirSync10,
47758
- openSync,
47890
+ openSync as openSync2,
47759
47891
  readFileSync as readFileSync20,
47760
47892
  statSync as statSync5,
47761
- unlinkSync as unlinkSync9,
47893
+ unlinkSync as unlinkSync10,
47762
47894
  utimesSync,
47763
47895
  writeFileSync as writeFileSync12
47764
47896
  } from "node:fs";
47765
- import { join as join18 } from "node:path";
47897
+ import { join as join19 } from "node:path";
47766
47898
  var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
47767
47899
  function touchTurnActiveMarker(stateDir) {
47768
- const path = join18(stateDir, TURN_ACTIVE_MARKER_FILE);
47769
- if (!existsSync21(path))
47900
+ const path = join19(stateDir, TURN_ACTIVE_MARKER_FILE);
47901
+ if (!existsSync22(path))
47770
47902
  return;
47771
47903
  const now = new Date;
47772
47904
  try {
47773
47905
  utimesSync(path, now, now);
47774
47906
  } catch {
47775
47907
  try {
47776
- const fd = openSync(path, "r+");
47777
- closeSync(fd);
47908
+ const fd = openSync2(path, "r+");
47909
+ closeSync2(fd);
47778
47910
  } catch {}
47779
47911
  }
47780
47912
  }
@@ -47988,11 +48120,11 @@ function startSubagentWatcher(config) {
47988
48120
  clearTimeout(ref.ref);
47989
48121
  });
47990
48122
  const fs2 = config.fs ?? {
47991
- existsSync: existsSync22,
48123
+ existsSync: existsSync23,
47992
48124
  readdirSync: readdirSync3,
47993
48125
  statSync: statSync6,
47994
- openSync: openSync2,
47995
- closeSync: closeSync2,
48126
+ openSync: openSync3,
48127
+ closeSync: closeSync3,
47996
48128
  readSync,
47997
48129
  watch
47998
48130
  };
@@ -48222,8 +48354,8 @@ function startSubagentWatcher(config) {
48222
48354
  function rescanSubagentDirs() {
48223
48355
  if (stopped)
48224
48356
  return;
48225
- const claudeHome = join19(agentDir, ".claude");
48226
- const projectsRoot = join19(claudeHome, "projects");
48357
+ const claudeHome = join20(agentDir, ".claude");
48358
+ const projectsRoot = join20(claudeHome, "projects");
48227
48359
  if (!fs2.existsSync(projectsRoot))
48228
48360
  return;
48229
48361
  let projectDirs;
@@ -48240,7 +48372,7 @@ function startSubagentWatcher(config) {
48240
48372
  }
48241
48373
  continue;
48242
48374
  }
48243
- const projectPath = join19(projectsRoot, pDir);
48375
+ const projectPath = join20(projectsRoot, pDir);
48244
48376
  let sessionDirs;
48245
48377
  try {
48246
48378
  sessionDirs = fs2.readdirSync(projectPath);
@@ -48250,7 +48382,7 @@ function startSubagentWatcher(config) {
48250
48382
  for (const sDir of sessionDirs) {
48251
48383
  if (sDir.endsWith(".jsonl"))
48252
48384
  continue;
48253
- const subagentsPath = join19(projectPath, sDir, "subagents");
48385
+ const subagentsPath = join20(projectPath, sDir, "subagents");
48254
48386
  if (!fs2.existsSync(subagentsPath))
48255
48387
  continue;
48256
48388
  if (!dirWatchers.has(subagentsPath)) {
@@ -48258,7 +48390,7 @@ function startSubagentWatcher(config) {
48258
48390
  const w = fs2.watch(subagentsPath, (_event, filename) => {
48259
48391
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
48260
48392
  return;
48261
- const filePath = join19(subagentsPath, filename.toString());
48393
+ const filePath = join20(subagentsPath, filename.toString());
48262
48394
  if (!knownFiles.has(filePath)) {
48263
48395
  scanSubagentsDir(subagentsPath);
48264
48396
  }
@@ -48283,7 +48415,7 @@ function startSubagentWatcher(config) {
48283
48415
  for (const e of entries) {
48284
48416
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
48285
48417
  continue;
48286
- const filePath = join19(subagentsPath, e);
48418
+ const filePath = join20(subagentsPath, e);
48287
48419
  if (knownFiles.has(filePath))
48288
48420
  continue;
48289
48421
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -48400,15 +48532,15 @@ function determineRestartReason(opts) {
48400
48532
  init_boot_card();
48401
48533
 
48402
48534
  // gateway/update-announce.ts
48403
- import { existsSync as existsSync26, mkdirSync as mkdirSync13, openSync as openSync3, closeSync as closeSync3, readFileSync as readFileSync25 } from "node:fs";
48404
- import { join as join24 } from "node:path";
48535
+ import { existsSync as existsSync27, mkdirSync as mkdirSync13, openSync as openSync4, closeSync as closeSync4, readFileSync as readFileSync25 } from "node:fs";
48536
+ import { join as join25 } from "node:path";
48405
48537
  import { homedir as homedir10 } from "node:os";
48406
48538
 
48407
48539
  // ../src/host-control/audit-reader.ts
48408
48540
  import { homedir as homedir9 } from "node:os";
48409
- import { join as join23 } from "node:path";
48541
+ import { join as join24 } from "node:path";
48410
48542
  function defaultAuditLogPath(home2 = homedir9()) {
48411
- return join23(home2, ".switchroom", "host-control-audit.log");
48543
+ return join24(home2, ".switchroom", "host-control-audit.log");
48412
48544
  }
48413
48545
  function parseAuditLine(line) {
48414
48546
  const trimmed = line.trim();
@@ -48514,7 +48646,7 @@ function readAndFilter(raw, filters, limit) {
48514
48646
  var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
48515
48647
  function readLastTerminalUpdateAudit(opts = {}) {
48516
48648
  const path = opts.auditLogPath ?? defaultAuditLogPath();
48517
- const exists = opts.exists ?? existsSync26;
48649
+ const exists = opts.exists ?? existsSync27;
48518
48650
  const readFile = opts.readFile ?? ((p) => readFileSync25(p, "utf-8"));
48519
48651
  if (!exists(path))
48520
48652
  return null;
@@ -48576,18 +48708,18 @@ function renderUpdateOutcomeLine(entry) {
48576
48708
  `);
48577
48709
  }
48578
48710
  function claimUpdateAnnouncement(requestId, opts = {}) {
48579
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join24(homedir10(), ".switchroom");
48580
- const dir = join24(stateDir, "update-announced");
48711
+ const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join25(homedir10(), ".switchroom");
48712
+ const dir = join25(stateDir, "update-announced");
48581
48713
  try {
48582
48714
  mkdirSync13(dir, { recursive: true });
48583
48715
  } catch {
48584
48716
  return false;
48585
48717
  }
48586
48718
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
48587
- const path = join24(dir, safeId);
48719
+ const path = join25(dir, safeId);
48588
48720
  try {
48589
- const fd = openSync3(path, "wx");
48590
- closeSync3(fd);
48721
+ const fd = openSync4(path, "wx");
48722
+ closeSync4(fd);
48591
48723
  return true;
48592
48724
  } catch {
48593
48725
  return false;
@@ -48804,24 +48936,24 @@ function createIssuesCardHandle(opts) {
48804
48936
  }
48805
48937
 
48806
48938
  // issues-watcher.ts
48807
- import { existsSync as existsSync28, statSync as statSync8 } from "node:fs";
48808
- import { join as join26 } from "node:path";
48939
+ import { existsSync as existsSync29, statSync as statSync8 } from "node:fs";
48940
+ import { join as join27 } from "node:path";
48809
48941
 
48810
48942
  // ../src/issues/store.ts
48811
48943
  import {
48812
- closeSync as closeSync4,
48813
- existsSync as existsSync27,
48944
+ closeSync as closeSync5,
48945
+ existsSync as existsSync28,
48814
48946
  mkdirSync as mkdirSync14,
48815
- openSync as openSync4,
48947
+ openSync as openSync5,
48816
48948
  readdirSync as readdirSync5,
48817
48949
  readFileSync as readFileSync27,
48818
48950
  renameSync as renameSync10,
48819
48951
  statSync as statSync7,
48820
- unlinkSync as unlinkSync10,
48952
+ unlinkSync as unlinkSync11,
48821
48953
  writeFileSync as writeFileSync16,
48822
48954
  writeSync
48823
48955
  } from "node:fs";
48824
- import { join as join25 } from "node:path";
48956
+ import { join as join26 } from "node:path";
48825
48957
  import { randomBytes as randomBytes4 } from "node:crypto";
48826
48958
  import { execSync } from "node:child_process";
48827
48959
 
@@ -48837,8 +48969,8 @@ var SEVERITY_RANK2 = {
48837
48969
  var ISSUES_FILE = "issues.jsonl";
48838
48970
  var ISSUES_LOCK = "issues.lock";
48839
48971
  function readAll(stateDir) {
48840
- const path = join25(stateDir, ISSUES_FILE);
48841
- if (!existsSync27(path))
48972
+ const path = join26(stateDir, ISSUES_FILE);
48973
+ if (!existsSync28(path))
48842
48974
  return [];
48843
48975
  let raw;
48844
48976
  try {
@@ -48874,7 +49006,7 @@ function list(stateDir, opts = {}) {
48874
49006
  });
48875
49007
  }
48876
49008
  function resolve6(stateDir, fingerprint, nowFn = Date.now) {
48877
- if (!existsSync27(join25(stateDir, ISSUES_FILE)))
49009
+ if (!existsSync28(join26(stateDir, ISSUES_FILE)))
48878
49010
  return 0;
48879
49011
  return withLock(stateDir, () => {
48880
49012
  const all = readAll(stateDir);
@@ -48892,7 +49024,7 @@ function resolve6(stateDir, fingerprint, nowFn = Date.now) {
48892
49024
  });
48893
49025
  }
48894
49026
  function writeAll(stateDir, events) {
48895
- const path = join25(stateDir, ISSUES_FILE);
49027
+ const path = join26(stateDir, ISSUES_FILE);
48896
49028
  sweepOrphanTmpFiles(stateDir);
48897
49029
  const tmp = `${path}.tmp-${process.pid}-${randomBytes4(4).toString("hex")}`;
48898
49030
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -48914,11 +49046,11 @@ function sweepOrphanTmpFiles(stateDir) {
48914
49046
  for (const entry of entries) {
48915
49047
  if (!entry.startsWith(TMP_PREFIX))
48916
49048
  continue;
48917
- const tmpPath2 = join25(stateDir, entry);
49049
+ const tmpPath2 = join26(stateDir, entry);
48918
49050
  try {
48919
49051
  const stat = statSync7(tmpPath2);
48920
49052
  if (stat.mtimeMs < cutoff) {
48921
- unlinkSync10(tmpPath2);
49053
+ unlinkSync11(tmpPath2);
48922
49054
  }
48923
49055
  } catch {}
48924
49056
  }
@@ -48926,12 +49058,12 @@ function sweepOrphanTmpFiles(stateDir) {
48926
49058
  var LOCK_RETRY_MS = 25;
48927
49059
  var LOCK_TIMEOUT_MS = 1e4;
48928
49060
  function withLock(stateDir, fn) {
48929
- const lockPath = join25(stateDir, ISSUES_LOCK);
49061
+ const lockPath = join26(stateDir, ISSUES_LOCK);
48930
49062
  const startedAt = Date.now();
48931
49063
  let fd = null;
48932
49064
  while (fd === null) {
48933
49065
  try {
48934
- fd = openSync4(lockPath, "wx");
49066
+ fd = openSync5(lockPath, "wx");
48935
49067
  try {
48936
49068
  writeSync(fd, String(process.pid));
48937
49069
  } catch {}
@@ -48951,10 +49083,10 @@ function withLock(stateDir, fn) {
48951
49083
  return fn();
48952
49084
  } finally {
48953
49085
  try {
48954
- closeSync4(fd);
49086
+ closeSync5(fd);
48955
49087
  } catch {}
48956
49088
  try {
48957
- unlinkSync10(lockPath);
49089
+ unlinkSync11(lockPath);
48958
49090
  } catch {}
48959
49091
  }
48960
49092
  }
@@ -48968,13 +49100,13 @@ function tryStealStaleLock(lockPath) {
48968
49100
  const pid = Number(pidStr);
48969
49101
  if (!Number.isFinite(pid) || pid <= 0) {
48970
49102
  try {
48971
- unlinkSync10(lockPath);
49103
+ unlinkSync11(lockPath);
48972
49104
  } catch {}
48973
49105
  return true;
48974
49106
  }
48975
49107
  if (pid === process.pid) {
48976
49108
  try {
48977
- unlinkSync10(lockPath);
49109
+ unlinkSync11(lockPath);
48978
49110
  } catch {}
48979
49111
  return true;
48980
49112
  }
@@ -48989,7 +49121,7 @@ function tryStealStaleLock(lockPath) {
48989
49121
  return false;
48990
49122
  }
48991
49123
  try {
48992
- unlinkSync10(lockPath);
49124
+ unlinkSync11(lockPath);
48993
49125
  } catch {}
48994
49126
  return true;
48995
49127
  }
@@ -49011,7 +49143,7 @@ function isIssueEvent(v) {
49011
49143
  // issues-watcher.ts
49012
49144
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
49013
49145
  function startIssuesWatcher(opts) {
49014
- const path = join26(opts.stateDir, ISSUES_FILE);
49146
+ const path = join27(opts.stateDir, ISSUES_FILE);
49015
49147
  const log = opts.log ?? (() => {});
49016
49148
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
49017
49149
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -49059,7 +49191,7 @@ function startIssuesWatcher(opts) {
49059
49191
  };
49060
49192
  }
49061
49193
  function defaultSignatureProvider(path) {
49062
- if (!existsSync28(path))
49194
+ if (!existsSync29(path))
49063
49195
  return null;
49064
49196
  try {
49065
49197
  const stat = statSync8(path);
@@ -49299,8 +49431,8 @@ function skillBasenameFromPath2(input) {
49299
49431
  }
49300
49432
 
49301
49433
  // credits-watch.ts
49302
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync17, existsSync as existsSync29, mkdirSync as mkdirSync15 } from "fs";
49303
- import { join as join27 } from "path";
49434
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync17, existsSync as existsSync30, mkdirSync as mkdirSync15 } from "fs";
49435
+ import { join as join28 } from "path";
49304
49436
  var STATE_FILE = "credits-watch.json";
49305
49437
  var FATAL_REASONS = new Set([
49306
49438
  "out_of_credits",
@@ -49312,8 +49444,8 @@ function emptyCreditState() {
49312
49444
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
49313
49445
  }
49314
49446
  function readClaudeJsonOverage(claudeConfigDir) {
49315
- const path = join27(claudeConfigDir, ".claude.json");
49316
- if (!existsSync29(path))
49447
+ const path = join28(claudeConfigDir, ".claude.json");
49448
+ if (!existsSync30(path))
49317
49449
  return null;
49318
49450
  let raw;
49319
49451
  try {
@@ -49397,8 +49529,8 @@ function escapeHtml10(s) {
49397
49529
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
49398
49530
  }
49399
49531
  function loadCreditState(stateDir) {
49400
- const path = join27(stateDir, STATE_FILE);
49401
- if (!existsSync29(path))
49532
+ const path = join28(stateDir, STATE_FILE);
49533
+ if (!existsSync30(path))
49402
49534
  return emptyCreditState();
49403
49535
  try {
49404
49536
  const raw = readFileSync28(path, "utf-8");
@@ -49414,54 +49546,54 @@ function loadCreditState(stateDir) {
49414
49546
  }
49415
49547
  function saveCreditState(stateDir, state4) {
49416
49548
  mkdirSync15(stateDir, { recursive: true });
49417
- const path = join27(stateDir, STATE_FILE);
49549
+ const path = join28(stateDir, STATE_FILE);
49418
49550
  writeFileSync17(path, JSON.stringify(state4, null, 2) + `
49419
49551
  `, { mode: 384 });
49420
49552
  }
49421
49553
 
49422
49554
  // gateway/turn-active-marker.ts
49423
49555
  import {
49424
- closeSync as closeSync5,
49425
- existsSync as existsSync30,
49556
+ closeSync as closeSync6,
49557
+ existsSync as existsSync31,
49426
49558
  mkdirSync as mkdirSync16,
49427
- openSync as openSync5,
49559
+ openSync as openSync6,
49428
49560
  readFileSync as readFileSync29,
49429
49561
  statSync as statSync9,
49430
- unlinkSync as unlinkSync11,
49562
+ unlinkSync as unlinkSync12,
49431
49563
  utimesSync as utimesSync2,
49432
49564
  writeFileSync as writeFileSync18
49433
49565
  } from "node:fs";
49434
- import { join as join28 } from "node:path";
49566
+ import { join as join29 } from "node:path";
49435
49567
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
49436
49568
  function writeTurnActiveMarker(stateDir, marker) {
49437
49569
  try {
49438
49570
  mkdirSync16(stateDir, { recursive: true });
49439
- writeFileSync18(join28(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
49571
+ writeFileSync18(join29(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
49440
49572
  `, { mode: 384 });
49441
49573
  } catch {}
49442
49574
  }
49443
49575
  function touchTurnActiveMarker2(stateDir) {
49444
- const path = join28(stateDir, TURN_ACTIVE_MARKER_FILE2);
49445
- if (!existsSync30(path))
49576
+ const path = join29(stateDir, TURN_ACTIVE_MARKER_FILE2);
49577
+ if (!existsSync31(path))
49446
49578
  return;
49447
49579
  const now = new Date;
49448
49580
  try {
49449
49581
  utimesSync2(path, now, now);
49450
49582
  } catch {
49451
49583
  try {
49452
- const fd = openSync5(path, "r+");
49453
- closeSync5(fd);
49584
+ const fd = openSync6(path, "r+");
49585
+ closeSync6(fd);
49454
49586
  } catch {}
49455
49587
  }
49456
49588
  }
49457
49589
  function removeTurnActiveMarker(stateDir) {
49458
49590
  try {
49459
- unlinkSync11(join28(stateDir, TURN_ACTIVE_MARKER_FILE2));
49591
+ unlinkSync12(join29(stateDir, TURN_ACTIVE_MARKER_FILE2));
49460
49592
  } catch {}
49461
49593
  }
49462
49594
  function sweepStaleTurnActiveMarker(stateDir, opts) {
49463
- const path = join28(stateDir, TURN_ACTIVE_MARKER_FILE2);
49464
- if (!existsSync30(path))
49595
+ const path = join29(stateDir, TURN_ACTIVE_MARKER_FILE2);
49596
+ if (!existsSync31(path))
49465
49597
  return false;
49466
49598
  const now = opts.now ?? Date.now();
49467
49599
  try {
@@ -49475,7 +49607,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
49475
49607
  try {
49476
49608
  payload = readFileSync29(path, "utf8");
49477
49609
  } catch {}
49478
- unlinkSync11(path);
49610
+ unlinkSync12(path);
49479
49611
  if (opts.onRemove) {
49480
49612
  try {
49481
49613
  opts.onRemove({
@@ -49492,10 +49624,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
49492
49624
  }
49493
49625
 
49494
49626
  // ../src/build-info.ts
49495
- var VERSION = "0.13.54";
49496
- var COMMIT_SHA = "95784728";
49497
- var COMMIT_DATE = "2026-05-27T05:55:02Z";
49498
- var LATEST_PR = 1894;
49627
+ var VERSION = "0.13.56";
49628
+ var COMMIT_SHA = "821a114e";
49629
+ var COMMIT_DATE = "2026-05-27T20:20:37Z";
49630
+ var LATEST_PR = 1923;
49499
49631
  var COMMITS_AHEAD_OF_TAG = 0;
49500
49632
 
49501
49633
  // gateway/boot-version.ts
@@ -49569,11 +49701,11 @@ init_peercred();
49569
49701
  import * as net4 from "node:net";
49570
49702
  import * as fs2 from "node:fs";
49571
49703
  import { homedir as homedir11 } from "node:os";
49572
- import { join as join29 } from "node:path";
49704
+ import { join as join30 } from "node:path";
49573
49705
  var DEFAULT_TIMEOUT_MS4 = 2000;
49574
49706
  var UNLOCK_TIMEOUT_MS = 30000;
49575
- var LEGACY_SOCKET_PATH2 = join29(homedir11(), ".switchroom", "vault-broker.sock");
49576
- var OPERATOR_SOCKET_PATH2 = join29(homedir11(), ".switchroom", "broker-operator", "sock");
49707
+ var LEGACY_SOCKET_PATH2 = join30(homedir11(), ".switchroom", "vault-broker.sock");
49708
+ var OPERATOR_SOCKET_PATH2 = join30(homedir11(), ".switchroom", "broker-operator", "sock");
49577
49709
  function defaultBrokerSocketPath2() {
49578
49710
  if (fs2.existsSync(OPERATOR_SOCKET_PATH2))
49579
49711
  return OPERATOR_SOCKET_PATH2;
@@ -49796,7 +49928,7 @@ function resolveVaultApprovalPosture(broker) {
49796
49928
 
49797
49929
  // registry/turns-schema.ts
49798
49930
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync17 } from "fs";
49799
- import { join as join30 } from "path";
49931
+ import { join as join31 } from "path";
49800
49932
  var DatabaseClass2 = null;
49801
49933
  function loadDatabaseClass2() {
49802
49934
  if (DatabaseClass2 != null)
@@ -49855,9 +49987,9 @@ function applySchema(db2) {
49855
49987
  }
49856
49988
  function openTurnsDb(agentDir) {
49857
49989
  const Database = loadDatabaseClass2();
49858
- const dir = join30(agentDir, "telegram");
49990
+ const dir = join31(agentDir, "telegram");
49859
49991
  mkdirSync17(dir, { recursive: true, mode: 448 });
49860
- const path = join30(dir, "registry.db");
49992
+ const path = join31(dir, "registry.db");
49861
49993
  const db2 = new Database(path, { create: true });
49862
49994
  applySchema(db2);
49863
49995
  try {
@@ -49998,11 +50130,11 @@ installGlobalErrorHandlers();
49998
50130
  process.on("beforeExit", () => {
49999
50131
  shutdownAnalytics();
50000
50132
  });
50001
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join32(homedir12(), ".claude", "channels", "telegram");
50002
- var ACCESS_FILE = join32(STATE_DIR, "access.json");
50003
- var APPROVED_DIR = join32(STATE_DIR, "approved");
50004
- var ENV_FILE = join32(STATE_DIR, ".env");
50005
- var INBOX_DIR = join32(STATE_DIR, "inbox");
50133
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join33(homedir12(), ".claude", "channels", "telegram");
50134
+ var ACCESS_FILE = join33(STATE_DIR, "access.json");
50135
+ var APPROVED_DIR = join33(STATE_DIR, "approved");
50136
+ var ENV_FILE = join33(STATE_DIR, ".env");
50137
+ var INBOX_DIR = join33(STATE_DIR, "inbox");
50006
50138
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
50007
50139
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
50008
50140
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -50193,7 +50325,7 @@ function assertSendable(f) {
50193
50325
  } catch {
50194
50326
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
50195
50327
  }
50196
- const inbox = join32(stateReal, "inbox");
50328
+ const inbox = join33(stateReal, "inbox");
50197
50329
  if (real.startsWith(stateReal + sep3) && !real.startsWith(inbox + sep3)) {
50198
50330
  throw new Error(`refusing to send channel state: ${f}`);
50199
50331
  }
@@ -50299,7 +50431,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
50299
50431
  if (HISTORY_ENABLED) {
50300
50432
  try {
50301
50433
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
50302
- process.stderr.write(`telegram gateway: history capture enabled at ${join32(STATE_DIR, "history.db")}
50434
+ process.stderr.write(`telegram gateway: history capture enabled at ${join33(STATE_DIR, "history.db")}
50303
50435
  `);
50304
50436
  } catch (err) {
50305
50437
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -50316,10 +50448,10 @@ try {
50316
50448
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s) as ended_via='restart'
50317
50449
  `);
50318
50450
  } else {
50319
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join32(agentDir, "telegram", "registry.db")}
50451
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join33(agentDir, "telegram", "registry.db")}
50320
50452
  `);
50321
50453
  }
50322
- const pendingEnvPath = join32(agentDir, ".pending-turn.env");
50454
+ const pendingEnvPath = join33(agentDir, ".pending-turn.env");
50323
50455
  try {
50324
50456
  const pending2 = findMostRecentInterruptedTurn(turnsDb);
50325
50457
  if (pending2 != null) {
@@ -50339,7 +50471,7 @@ try {
50339
50471
  renameSync12(pendingEnvTmp, pendingEnvPath);
50340
50472
  process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
50341
50473
  `);
50342
- } else if (existsSync34(pendingEnvPath)) {
50474
+ } else if (existsSync35(pendingEnvPath)) {
50343
50475
  rmSync4(pendingEnvPath, { force: true });
50344
50476
  process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
50345
50477
  `);
@@ -50393,7 +50525,7 @@ function checkApprovals() {
50393
50525
  return;
50394
50526
  }
50395
50527
  for (const senderId of files) {
50396
- const file = join32(APPROVED_DIR, senderId);
50528
+ const file = join33(APPROVED_DIR, senderId);
50397
50529
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync4(file, { force: true }), (err) => {
50398
50530
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
50399
50531
  `);
@@ -51270,11 +51402,11 @@ var unpinProgressCardForChat = null;
51270
51402
  var getPinnedProgressCardMessageId = null;
51271
51403
  var completeProgressCardTurn = null;
51272
51404
  var subagentWatcher = null;
51273
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join32(STATE_DIR, "gateway.sock");
51405
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join33(STATE_DIR, "gateway.sock");
51274
51406
  mkdirSync21(STATE_DIR, { recursive: true, mode: 448 });
51275
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join32(STATE_DIR, "gateway.pid.json");
51276
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join32(STATE_DIR, "gateway-session.json");
51277
- var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join32(STATE_DIR, "clean-shutdown.json");
51407
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join33(STATE_DIR, "gateway.pid.json");
51408
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join33(STATE_DIR, "gateway-session.json");
51409
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join33(STATE_DIR, "clean-shutdown.json");
51278
51410
  var GATEWAY_STARTED_AT_MS = Date.now();
51279
51411
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
51280
51412
  var activeBootCard = null;
@@ -51303,7 +51435,7 @@ function ensureIssuesCard(chatId, threadId) {
51303
51435
  bot: botApi,
51304
51436
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
51305
51437
  `),
51306
- persistPath: join32(stateDir, "issues-card.json")
51438
+ persistPath: join33(stateDir, "issues-card.json")
51307
51439
  });
51308
51440
  activeIssuesWatcher = startIssuesWatcher({
51309
51441
  stateDir,
@@ -51347,6 +51479,21 @@ startTimer({
51347
51479
  emitMetric: (event) => {
51348
51480
  emitRuntimeMetric(event);
51349
51481
  },
51482
+ onAwarenessPing: async (ctx) => {
51483
+ if (activeTurnStartedAt.get(ctx.key) == null && currentTurn == null) {
51484
+ return;
51485
+ }
51486
+ const text = formatFrameworkFallbackText(ctx.fallbackKind, ctx.silenceMs, ctx.inFlightTools);
51487
+ try {
51488
+ await robustApiCall(() => bot.api.sendMessage(ctx.chatId, text, {
51489
+ ...ctx.threadId != null ? { message_thread_id: ctx.threadId } : {},
51490
+ disable_notification: true
51491
+ }), { chat_id: ctx.chatId, ...ctx.threadId != null ? { threadId: ctx.threadId } : {} });
51492
+ } catch (err) {
51493
+ process.stderr.write(`silence-poke awareness-ping sendMessage failed chat=${ctx.chatId} thread=${ctx.threadId}: ${err}
51494
+ `);
51495
+ }
51496
+ },
51350
51497
  onFrameworkFallback: async (ctx) => {
51351
51498
  if (activeTurnStartedAt.get(ctx.key) == null && currentTurn == null) {
51352
51499
  process.stderr.write(`telegram gateway: silence-poke framework-fallback late-fire skipped \u2014 ` + `turn ended cleanly during silence window chat=${ctx.chatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs}
@@ -51395,7 +51542,6 @@ startTimer({
51395
51542
  longest_silent_gap_ms: outboundMetrics.longestOutboundGapMs,
51396
51543
  ended_via: "framework_fallback"
51397
51544
  });
51398
- clear(fbKey);
51399
51545
  }
51400
51546
  if (turnsDb != null && turnMatchesFallback && wedgedTurn?.registryKey != null) {
51401
51547
  const _turnKey = wedgedTurn.registryKey;
@@ -51458,13 +51604,13 @@ startTimer2({
51458
51604
  }
51459
51605
  });
51460
51606
  var inboundSpool = STATIC ? undefined : createInboundSpool({
51461
- path: join32(STATE_DIR, "inbound-spool.jsonl"),
51607
+ path: join33(STATE_DIR, "inbound-spool.jsonl"),
51462
51608
  fs: {
51463
51609
  appendFileSync: (p, d) => appendFileSync3(p, d),
51464
51610
  readFileSync: (p) => readFileSync32(p, "utf8"),
51465
51611
  writeFileSync: (p, d) => writeFileSync21(p, d),
51466
51612
  renameSync: (a, b) => renameSync12(a, b),
51467
- existsSync: (p) => existsSync34(p),
51613
+ existsSync: (p) => existsSync35(p),
51468
51614
  statSizeSync: (p) => statSync13(p).size
51469
51615
  }
51470
51616
  });
@@ -52277,6 +52423,12 @@ ${url}`;
52277
52423
  });
52278
52424
  noteOutbound(statusKey(chat_id, threadId), Date.now());
52279
52425
  noteOutbound2(statusKey(chat_id, threadId), Date.now());
52426
+ try {
52427
+ markAckSent();
52428
+ } catch (err) {
52429
+ process.stderr.write(`telegram gateway: markAckSent failed: ${err}
52430
+ `);
52431
+ }
52280
52432
  if (isFinalAnswerReply({ text: rawText, disableNotification })) {
52281
52433
  clearSilentEndState(statusKey(chat_id, threadId));
52282
52434
  }
@@ -52604,6 +52756,12 @@ async function executeStreamReply(args) {
52604
52756
  const sKey = statusKey(streamChatId, streamThreadId);
52605
52757
  noteOutbound(sKey, Date.now());
52606
52758
  noteOutbound2(sKey, Date.now());
52759
+ try {
52760
+ markAckSent();
52761
+ } catch (err) {
52762
+ process.stderr.write(`telegram gateway: markAckSent (stream_reply) failed: ${err}
52763
+ `);
52764
+ }
52607
52765
  if (isFinalAnswerReply({
52608
52766
  text: args.text ?? "",
52609
52767
  disableNotification: args.disable_notification === true,
@@ -52954,10 +53112,10 @@ async function executeSendGif(rawArgs) {
52954
53112
  };
52955
53113
  }
52956
53114
  async function publishToTelegraph(text, shortName, authorName) {
52957
- const accountPath = join32(STATE_DIR, "telegraph-account.json");
53115
+ const accountPath = join33(STATE_DIR, "telegraph-account.json");
52958
53116
  let account = null;
52959
53117
  try {
52960
- if (existsSync34(accountPath)) {
53118
+ if (existsSync35(accountPath)) {
52961
53119
  const raw = readFileSync32(accountPath, "utf-8");
52962
53120
  const parsed = JSON.parse(raw);
52963
53121
  if (parsed.shortName && parsed.accessToken) {
@@ -53450,6 +53608,7 @@ function handleSessionEvent(ev) {
53450
53608
  if (ev.chatId) {
53451
53609
  const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
53452
53610
  clearPending(statusKey(ev.chatId, enqThreadId), "handback");
53611
+ clearAckSent();
53453
53612
  }
53454
53613
  if (ev.chatId) {
53455
53614
  const prior = currentTurn;
@@ -54907,7 +55066,7 @@ function restartMarkerPath() {
54907
55066
  const agentDir = resolveAgentDirFromEnv();
54908
55067
  if (!agentDir)
54909
55068
  return null;
54910
- return join32(agentDir, "restart-pending.json");
55069
+ return join33(agentDir, "restart-pending.json");
54911
55070
  }
54912
55071
  function writeRestartMarker(marker) {
54913
55072
  const p = restartMarkerPath();
@@ -55031,7 +55190,7 @@ var _dockerReachable;
55031
55190
  function isDockerReachable() {
55032
55191
  if (_dockerReachable !== undefined)
55033
55192
  return _dockerReachable;
55034
- if (!existsSync34("/var/run/docker.sock")) {
55193
+ if (!existsSync35("/var/run/docker.sock")) {
55035
55194
  _dockerReachable = false;
55036
55195
  return _dockerReachable;
55037
55196
  }
@@ -55048,11 +55207,11 @@ function _resetDockerReachableCache() {
55048
55207
  }
55049
55208
  function spawnSwitchroomDetached(args, onFailure) {
55050
55209
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
55051
- const logPath = join32(STATE_DIR, "detached-spawn.log");
55210
+ const logPath = join33(STATE_DIR, "detached-spawn.log");
55052
55211
  let outFd = null;
55053
55212
  try {
55054
55213
  mkdirSync21(STATE_DIR, { recursive: true });
55055
- outFd = openSync8(logPath, "a");
55214
+ outFd = openSync9(logPath, "a");
55056
55215
  writeFileSync21(logPath, `
55057
55216
  [${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
55058
55217
  `, { flag: "a" });
@@ -55067,7 +55226,7 @@ function spawnSwitchroomDetached(args, onFailure) {
55067
55226
  });
55068
55227
  if (outFd != null) {
55069
55228
  try {
55070
- closeSync8(outFd);
55229
+ closeSync9(outFd);
55071
55230
  } catch {}
55072
55231
  }
55073
55232
  if (onFailure) {
@@ -55421,8 +55580,8 @@ bot.use(async (ctx, next) => {
55421
55580
  });
55422
55581
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
55423
55582
  try {
55424
- const auditPath = join32(homedir12(), ".switchroom", "vault-audit.log");
55425
- if (!existsSync34(auditPath))
55583
+ const auditPath = join33(homedir12(), ".switchroom", "vault-audit.log");
55584
+ if (!existsSync35(auditPath))
55426
55585
  return [];
55427
55586
  const raw = readFileSync32(auditPath, "utf8");
55428
55587
  return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
@@ -55475,7 +55634,7 @@ async function buildAgentMetadata(agentName3) {
55475
55634
  try {
55476
55635
  const agentDir = resolveAgentDirFromEnv();
55477
55636
  if (agentDir) {
55478
- const raw = readFileSync32(join32(agentDir, ".claude", ".claude.json"), "utf8");
55637
+ const raw = readFileSync32(join33(agentDir, ".claude", ".claude.json"), "utf8");
55479
55638
  claudeJson = JSON.parse(raw);
55480
55639
  }
55481
55640
  } catch {}
@@ -55689,10 +55848,10 @@ bot.command("restart", async (ctx) => {
55689
55848
  function flushAgentHandoff(agentDir) {
55690
55849
  let removed = 0;
55691
55850
  for (const fname of [".handoff.md", ".handoff-topic"]) {
55692
- const p = join32(agentDir, fname);
55851
+ const p = join33(agentDir, fname);
55693
55852
  try {
55694
- if (existsSync34(p)) {
55695
- unlinkSync14(p);
55853
+ if (existsSync35(p)) {
55854
+ unlinkSync15(p);
55696
55855
  removed++;
55697
55856
  }
55698
55857
  } catch (err) {
@@ -55747,7 +55906,7 @@ async function handleNewOrResetCommand(ctx, kind) {
55747
55906
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
55748
55907
  if (agentDir != null) {
55749
55908
  try {
55750
- writeFileSync21(join32(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
55909
+ writeFileSync21(join33(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
55751
55910
  `, "utf8");
55752
55911
  } catch (err) {
55753
55912
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -56108,14 +56267,14 @@ bot.command("interrupt", async (ctx) => {
56108
56267
  var lockoutOps = {
56109
56268
  readFileSync: (p, enc) => readFileSync32(p, enc),
56110
56269
  writeFileSync: (p, data, opts) => writeFileSync21(p, data, opts),
56111
- existsSync: (p) => existsSync34(p),
56270
+ existsSync: (p) => existsSync35(p),
56112
56271
  mkdirSync: (p, opts) => mkdirSync21(p, opts),
56113
- joinPath: (...parts) => join32(...parts)
56272
+ joinPath: (...parts) => join33(...parts)
56114
56273
  };
56115
56274
  var FLEET_FALLBACK_DEDUP_MS = 30000;
56116
56275
  function isAuthBrokerSocketReachable() {
56117
56276
  try {
56118
- return existsSync34(resolveAuthBrokerSocketPath2());
56277
+ return existsSync35(resolveAuthBrokerSocketPath2());
56119
56278
  } catch {
56120
56279
  return false;
56121
56280
  }
@@ -56176,7 +56335,7 @@ async function runCreditWatch() {
56176
56335
  if (!agentDir)
56177
56336
  return;
56178
56337
  const agentName3 = getMyAgentName();
56179
- const claudeConfigDir = join32(agentDir, ".claude");
56338
+ const claudeConfigDir = join33(agentDir, ".claude");
56180
56339
  const stateDir = STATE_DIR;
56181
56340
  const reason = readClaudeJsonOverage(claudeConfigDir);
56182
56341
  const prev = loadCreditState(stateDir);
@@ -56386,9 +56545,9 @@ async function handleVaultRecentDenialCallback(ctx, data) {
56386
56545
  return;
56387
56546
  }
56388
56547
  const { token, id } = result;
56389
- const tokenPath = join32(homedir12(), ".switchroom", "agents", agentName3, ".vault-token");
56548
+ const tokenPath = join33(homedir12(), ".switchroom", "agents", agentName3, ".vault-token");
56390
56549
  try {
56391
- mkdirSync21(join32(homedir12(), ".switchroom", "agents", agentName3), { recursive: true });
56550
+ mkdirSync21(join33(homedir12(), ".switchroom", "agents", agentName3), { recursive: true });
56392
56551
  writeFileSync21(tokenPath, token, { mode: 384 });
56393
56552
  } catch (err) {
56394
56553
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
@@ -56465,9 +56624,9 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
56465
56624
  return;
56466
56625
  }
56467
56626
  const { token, id } = result;
56468
- const tokenPath = join32(homedir12(), ".switchroom", "agents", pending2.agent, ".vault-token");
56627
+ const tokenPath = join33(homedir12(), ".switchroom", "agents", pending2.agent, ".vault-token");
56469
56628
  try {
56470
- mkdirSync21(join32(homedir12(), ".switchroom", "agents", pending2.agent), { recursive: true });
56629
+ mkdirSync21(join33(homedir12(), ".switchroom", "agents", pending2.agent), { recursive: true });
56471
56630
  writeFileSync21(tokenPath, token, { mode: 384 });
56472
56631
  } catch (err) {
56473
56632
  await switchroomReply(ctx, `<b>Grant created (${escapeHtmlForTg(id)}) but token write failed:</b> ${escapeHtmlForTg(String(err))}
@@ -56943,9 +57102,9 @@ async function executeGrantWizard(ctx, chatId, state4) {
56943
57102
  return;
56944
57103
  }
56945
57104
  const { token, id } = result;
56946
- const tokenPath = join32(homedir12(), ".switchroom", "agents", state4.agent, ".vault-token");
57105
+ const tokenPath = join33(homedir12(), ".switchroom", "agents", state4.agent, ".vault-token");
56947
57106
  try {
56948
- mkdirSync21(join32(homedir12(), ".switchroom", "agents", state4.agent), { recursive: true });
57107
+ mkdirSync21(join33(homedir12(), ".switchroom", "agents", state4.agent), { recursive: true });
56949
57108
  writeFileSync21(tokenPath, token, { mode: 384 });
56950
57109
  } catch (err) {
56951
57110
  await switchroomReply(ctx, `<b>Grant created but token write failed:</b> ${escapeHtmlForTg(String(err))}`, { html: true });
@@ -57803,7 +57962,7 @@ bot.command("usage", async (ctx) => {
57803
57962
  await switchroomReply(ctx, "<b>/usage:</b> cannot resolve agent dir.", { html: true });
57804
57963
  return;
57805
57964
  }
57806
- const result = await fetchQuota2({ claudeConfigDir: join32(agentDir, ".claude") });
57965
+ const result = await fetchQuota2({ claudeConfigDir: join33(agentDir, ".claude") });
57807
57966
  if (!result.ok) {
57808
57967
  await switchroomReply(ctx, `<b>/usage:</b> ${escapeHtmlForTg(result.reason)}`, { html: true });
57809
57968
  return;
@@ -58348,7 +58507,7 @@ async function maybeTranscribeVoice(fileId, mimeType, language) {
58348
58507
  let apiKey = null;
58349
58508
  try {
58350
58509
  const path = __require("path").join(__require("os").homedir(), ".switchroom", "openai-api-key");
58351
- if (existsSync34(path)) {
58510
+ if (existsSync35(path)) {
58352
58511
  apiKey = readFileSync32(path, "utf-8").trim();
58353
58512
  }
58354
58513
  } catch (err) {
@@ -59209,7 +59368,7 @@ var didOneTimeSetup = false;
59209
59368
  agentName: agentDisplayName,
59210
59369
  agentSlug,
59211
59370
  version: formatBootVersion(),
59212
- agentDir: agentDir ?? join32(homedir12(), ".switchroom", "agents", agentSlug),
59371
+ agentDir: agentDir ?? join33(homedir12(), ".switchroom", "agents", agentSlug),
59213
59372
  gatewayInfo: { pid: process.pid, startedAtMs: GATEWAY_STARTED_AT_MS },
59214
59373
  restartReason: reason,
59215
59374
  restartAgeMs: markerAgeMs,